Compare commits

..

No commits in common. "v3" and "1.2.x" have entirely different histories.
v3 ... 1.2.x

10575 changed files with 1838808 additions and 90735 deletions

View File

@ -1,3 +0,0 @@
((nil . ((php-project-root . auto)
(phpstan-executable . (root . "bin/phpstan"))
)

43
.env
View File

@ -1,43 +0,0 @@
# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
# * .env contains default values for the environment variables needed by the app
# * .env.local uncommitted file with local overrides
# * .env.$APP_ENV committed environment-specific defaults
# * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=6a9ac3a09c73230107373e8e0b71e0a3
#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
#TRUSTED_HOSTS='^(localhost|example\.com)$'
###< symfony/framework-bundle ###
###> symfony/mailer ###
# MAILER_DSN=smtp://localhost
###< symfony/mailer ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11&charset=utf8"
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
DATABASE_URL=postgresql://postgres:foobar@postgres:5432/social
#?serverVersion=11&charset=utf8
###< doctrine/doctrine-bundle ###
SHELL_VERBOSITY=3
###> symfony/messenger ###
# Choose one of the transports below
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
MESSENGER_TRANSPORT_DSN_HIGH=doctrine://default?queue_name=high
MESSENGER_TRANSPORT_DSN_LOW=doctrine://default?queue_name=low
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
###< symfony/messenger ###

View File

@ -1,6 +0,0 @@
# define your env variables for the test env here
KERNEL_CLASS='App\Kernel'
APP_SECRET='$ecretf0rt3st'
SYMFONY_DEPRECATIONS_HELPER=999999
PANTHER_APP_ENV=panther
DATABASE_URL=postgresql://postgres:password@db:5432/test

91
.gitignore vendored
View File

@ -1,64 +1,31 @@
###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###
###> symfony/phpunit-bridge ###
.phpunit
.phpunit.result.cache
/phpunit.xml
###< symfony/phpunit-bridge ###
###> friendsofphp/php-cs-fixer ###
!.php-cs-fixer.php
/.php_cs.cache
/.php-cs-fixer.cache
###< friendsofphp/php-cs-fixer ###
###> phpunit/phpunit ###
/phpunit.xml
.phpunit.result.cache
###< phpunit/phpunit ###
DOCUMENTATION/database/*
!DOCUMENTATION/database/database.pdf
docker/certbot/.files
docker/certbot/www
docker/*/*.env
docker/mail/etc/hostname
docker/mail/etc/hosts
docker/mail/etc/resolv.conf
docker/mail/config/aliases.db
docker/mail/config/domains.db
docker/mail/config/mailboxes.db
docker/mail/config/passwd.db
docker/mail/etc/service/*
!docker/mail/etc/service/*/run
!docker/testing/*
!docker/testing/docker-compose.yaml
docker/testing/*~
docker-compose.yaml
composer.local.json
social.local.yaml
# V2
avatar/*
files/*
file/*
local/*
_darcs/*
logs/*
log/*
run/*
config.php
/file
.htaccess
httpd.conf
*.tmproj
dataobject.ini
*~
*.bak
*.orig
*.rej
.#*
*.swp
.buildpath
.project
.settings
TODO.rym
config-*.php
good-config.php
lac08.log
php.log
.DS_Store
nbproject
*.mo
notes
.test_coverage_report
.phpunit_cache

View File

@ -1,378 +0,0 @@
<?php
declare(strict_types = 1);
/*
* This document has been generated with
* https://mlocati.github.io/php-cs-fixer-configurator/#version:3.2.1|configurator
* you can change this configuration by importing this file.
*/
$config = new PhpCsFixer\Config();
return $config
->setRiskyAllowed(true)
->setRules([
// Each line of multi-line DocComments must have an asterisk [PSR-5] and must be aligned with the first one.
'align_multiline_comment' => ['comment_type' => 'phpdocs_like'],
// Each element of an array must be indented exactly once.
'array_indentation' => true,
// Converts simple usages of `array_push($x, $y);` to `$x[] = $y;`.
'array_push' => true,
// PHP arrays should be declared using the configured syntax.
'array_syntax' => ['syntax' => 'short'],
// Use the null coalescing assignment operator `??=` where possible.
'assign_null_coalescing_to_coalesce_equal' => true,
// Binary operators should be surrounded by space as configured.
'binary_operator_spaces' => ['default' => 'align_single_space_minimal', 'operators' => ['??' => 'align']],
// There MUST be one blank line after the namespace declaration.
'blank_line_after_namespace' => true,
// Ensure there is no code on the same line as the PHP open tag and it is followed by a blank line.
'blank_line_after_opening_tag' => true,
// The body of each structure MUST be enclosed by braces. Braces should be properly placed. Body of braces should be properly indented.
'braces' => ['allow_single_line_anonymous_class_with_empty_body' => true, 'allow_single_line_closure' => true, 'position_after_functions_and_oop_constructs' => 'next'],
// A single space or none should be between cast and variable.
'cast_spaces' => true,
// Whitespace around the keywords of a class, trait or interfaces definition should be one space.
'class_definition' => ['single_item_single_line' => true, 'single_line' => true],
// Using `isset($var) &&` multiple times should be done in one call.
'combine_consecutive_issets' => true,
// Calling `unset` on multiple items should be done in one call.
'combine_consecutive_unsets' => true,
// Replace multiple nested calls of `dirname` by only one call with second `$level` parameter. Requires PHP >= 7.0.
'combine_nested_dirname' => true,
// Comments with annotation should be docblock when used on structural elements.
'comment_to_phpdoc' => false,
// Remove extra spaces in a nullable typehint.
'compact_nullable_typehint' => true,
// Concatenation should be spaced according configuration.
'concat_space' => ['spacing' => 'one'],
// The PHP constants `true`, `false`, and `null` MUST be written using the correct casing.
'constant_case' => true,
// Equal sign in declare statement should be surrounded by spaces or not following configuration.
'declare_equal_normalize' => ['space' => 'single'],
// There must not be spaces around `declare` statement parentheses.
'declare_parentheses' => true,
// Force strict types declaration in all files. Requires PHP >= 7.0.
'declare_strict_types' => true,
// Replaces `dirname(__FILE__)` expression with equivalent `__DIR__` constant.
'dir_constant' => true,
// Replaces short-echo `<?=` with long format `<?php echo`/`<?php print` syntax, or vice-versa.
'echo_tag_syntax' => true,
// The keyword `elseif` should be used instead of `else if` so that all control keywords look like single words.
'elseif' => true,
// Empty loop-body must be in configured style.
'empty_loop_body' => true,
// Empty loop-condition must be in configured style.
'empty_loop_condition' => true,
// PHP code MUST use only UTF-8 without BOM (remove BOM).
'encoding' => true,
// Replace deprecated `ereg` regular expression functions with `preg`.
'ereg_to_preg' => true,
// Escape implicit backslashes in strings and heredocs to ease the understanding of which are special chars interpreted by PHP and which not.
'escape_implicit_backslashes' => true,
// Add curly braces to indirect variables to make them clear to understand. Requires PHP >= 7.0.
'explicit_indirect_variable' => true,
// Converts implicit variables into explicit ones in double-quoted strings or heredoc syntax.
'explicit_string_variable' => true,
// PHP code must use the long `<?php` tags or short-echo `<?=` tags and not other tag variations.
'full_opening_tag' => true,
// Transforms imported FQCN parameters and return types in function arguments to short version.
'fully_qualified_strict_types' => true,
// Spaces should be properly placed in a function declaration.
'function_declaration' => ['closure_function_spacing' => 'one'],
// Ensure single space between function's argument and its typehint.
'function_typehint_space' => true,
// Imports or fully qualifies global classes/functions/constants.
'global_namespace_import' => true,
// Heredoc/nowdoc content must be properly indented. Requires PHP >= 7.3.
'heredoc_indentation' => true,
// Convert `heredoc` to `nowdoc` where possible.
'heredoc_to_nowdoc' => true,
// Function `implode` must be called with 2 arguments in the documented order.
'implode_call' => true,
// Pre- or post-increment and decrement operators should be used if possible.
'increment_style' => true,
// Code MUST use configured indentation type.
'indentation_type' => true,
// Lambda must not import variables it doesn't use.
'lambda_not_used_import' => true,
// All PHP files must use same line ending.
'line_ending' => true,
// Ensure there is no code on the same line as the PHP open tag.
'linebreak_after_opening_tag' => true,
// List (`array` destructuring) assignment should be declared using the configured syntax. Requires PHP >= 7.1.
'list_syntax' => ['syntax' => 'short'],
// Use `&&` and `||` logical operators instead of `and` and `or`.
'logical_operators' => true,
// Cast should be written in lower case.
'lowercase_cast' => true,
// PHP keywords MUST be in lower case.
'lowercase_keywords' => true,
// Class static references `self`, `static` and `parent` MUST be in lower case.
'lowercase_static_reference' => true,
// Magic constants should be referred to using the correct casing.
'magic_constant_casing' => true,
// Magic method definitions and calls must be using the correct casing.
'magic_method_casing' => true,
// Replace non multibyte-safe functions with corresponding mb function.
'mb_str_functions' => true,
// In method arguments and method call, there MUST NOT be a space before each comma and there MUST be one space after each comma. Argument lists MAY be split across multiple lines, where each subsequent line is indented once. When doing so, the first item in the list MUST be on the next line, and there MUST be only one argument per line.
'method_argument_space' => ['after_heredoc' => true, 'on_multiline' => 'ensure_fully_multiline'],
// Method chaining MUST be properly indented. Method chaining with different levels of indentation is not supported.
'method_chaining_indentation' => true,
// Replace `strpos()` calls with `str_starts_with()` or `str_contains()` if possible.
'modernize_strpos' => true,
// Replaces `intval`, `floatval`, `doubleval`, `strval` and `boolval` function calls with according type casting operator.
'modernize_types_casting' => true,
// DocBlocks must start with two asterisks, multiline comments must start with a single asterisk, after the opening slash. Both must end with a single asterisk before the closing slash.
'multiline_comment_opening_closing' => true,
// Forbid multi-line whitespace before the closing semicolon or move the semicolon to the new line for chained calls.
'multiline_whitespace_before_semicolons' => true,
// Add leading `\` before constant invocation of internal constant to speed up resolving. Constant name match is case-sensitive, except for `null`, `false` and `true`.
'native_constant_invocation' => true,
// Function defined by PHP should be called using the correct casing.
'native_function_casing' => true,
// Add leading `\` before function invocation to speed up resolving.
'native_function_invocation' => true,
// Native type hints for functions should use the correct case.
'native_function_type_declaration_casing' => true,
// Master language constructs shall be used instead of aliases.
'no_alias_language_construct_call' => true,
// Replace control structure alternative syntax to use braces.
'no_alternative_syntax' => true,
// There should be no empty lines after class opening brace.
'no_blank_lines_after_class_opening' => true,
// There should not be blank lines between docblock and the documented element.
'no_blank_lines_after_phpdoc' => true,
// There must be a comment when fall-through is intentional in a non-empty case body.
'no_break_comment' => true,
// The closing `? >` tag MUST be omitted from files containing only PHP.
'no_closing_tag' => true,
// There should not be any empty comments.
'no_empty_comment' => true,
// There should not be empty PHPDoc blocks.
'no_empty_phpdoc' => true,
// Remove useless (semicolon) statements.
'no_empty_statement' => true,
// Removes extra blank lines and/or blank lines following configuration.
'no_extra_blank_lines' => true,
// Remove leading slashes in `use` clauses.
'no_leading_import_slash' => true,
// The namespace declaration line shouldn't contain leading whitespace.
'no_leading_namespace_whitespace' => true,
// Either language construct `print` or `echo` should be used.
'no_mixed_echo_print' => true,
// Operator `=>` should not be surrounded by multi-line whitespaces.
'no_multiline_whitespace_around_double_arrow' => true,
// Properties MUST not be explicitly initialized with `null` except when they have a type declaration (PHP 7.4).
'no_null_property_initialization' => true,
// Short cast `bool` using double exclamation mark should not be used.
'no_short_bool_cast' => true,
// Single-line whitespace before closing semicolon are prohibited.
'no_singleline_whitespace_before_semicolons' => true,
// There must be no space around double colons (also called Scope Resolution Operator or Paamayim Nekudotayim).
'no_space_around_double_colon' => true,
// When making a method or function call, there MUST NOT be a space between the method or function name and the opening parenthesis.
'no_spaces_after_function_name' => true,
// There MUST NOT be spaces around offset braces.
'no_spaces_around_offset' => true,
// There MUST NOT be a space after the opening parenthesis. There MUST NOT be a space before the closing parenthesis.
'no_spaces_inside_parenthesis' => true,
// Removes `@param`, `@return` and `@var` tags that don't provide any useful information.
'no_superfluous_phpdoc_tags' => false,
// Remove trailing commas in list function calls.
'no_trailing_comma_in_list_call' => true,
// PHP single-line arrays should not have trailing comma.
'no_trailing_comma_in_singleline_array' => true,
// Remove trailing whitespace at the end of non-blank lines.
'no_trailing_whitespace' => true,
// There MUST be no trailing spaces inside comment or PHPDoc.
'no_trailing_whitespace_in_comment' => true,
// Removes unneeded parentheses around control statements.
'no_unneeded_control_parentheses' => true,
// Removes unneeded curly braces that are superfluous and aren't part of a control structure's body.
'no_unneeded_curly_braces' => true,
// In function arguments there must not be arguments with default values before non-default ones.
'no_unreachable_default_argument_value' => true,
// Variables must be set `null` instead of using `(unset)` casting.
'no_unset_cast' => true,
// Unused `use` statements must be removed.
'no_unused_imports' => true,
// There should not be an empty `return` statement at the end of a function.
'no_useless_return' => true,
// In array declaration, there MUST NOT be a whitespace before each comma.
'no_whitespace_before_comma_in_array' => true,
// Remove trailing whitespace at the end of blank lines.
'no_whitespace_in_blank_line' => true,
// Remove Zero-width space (ZWSP), Non-breaking space (NBSP) and other invisible unicode symbols.
'non_printable_character' => true,
// Array index should always be written by using square braces.
'normalize_index_brace' => true,
// Adds or removes `?` before type declarations for parameters with a default `null` value.
'nullable_type_declaration_for_default_null_value' => true,
// There should not be space before or after object operators `->` and `?->`.
'object_operator_without_whitespace' => true,
// Operators - when multiline - must always be at the beginning or at the end of the line.
'operator_linebreak' => true,
// Ordering `use` statements.
'ordered_imports' => true,
// PHPUnit assertion method calls like `->assertSame(true, $foo)` should be written with dedicated method like `->assertTrue($foo)`.
'php_unit_construct' => true,
// PHPUnit assertions like `assertInternalType`, `assertFileExists`, should be used over `assertTrue`.
'php_unit_dedicate_assert' => true,
// PHPUnit assertions like `assertIsArray` should be used over `assertInternalType`.
'php_unit_dedicate_assert_internal_type' => true,
// Usages of `->setExpectedException*` methods MUST be replaced by `->expectException*` methods.
'php_unit_expectation' => true,
// PHPUnit annotations should be a FQCNs including a root namespace.
'php_unit_fqcn_annotation' => true,
// Enforce camel (or snake) case for PHPUnit test methods, following configuration.
'php_unit_method_casing' => true,
// Usage of PHPUnit's mock e.g. `->will($this->returnValue(..))` must be replaced by its shorter equivalent such as `->willReturn(...)`.
'php_unit_mock_short_will_return' => true,
// PHPUnit classes MUST be used in namespaced version, e.g. `\PHPUnit\Framework\TestCase` instead of `\PHPUnit_Framework_TestCase`.
'php_unit_namespaced' => true,
// Usages of `@expectedException*` annotations MUST be replaced by `->setExpectedException*` methods.
'php_unit_no_expectation_annotation' => true,
// Changes the visibility of the `setUp()` and `tearDown()` functions of PHPUnit to `protected`, to match the PHPUnit TestCase.
'php_unit_set_up_tear_down_visibility' => true,
// PHPUnit methods like `assertSame` should be used instead of `assertEquals`.
'php_unit_strict' => true,
// Calls to `PHPUnit\Framework\TestCase` static methods must all be of the same type, either `$this->`, `self::` or `static::`.
'php_unit_test_case_static_method_calls' => true,
// PHPDoc should contain `@param` for all params.
'phpdoc_add_missing_param_annotation' => true,
// All items of the given phpdoc tags must be either left-aligned or (by default) aligned vertically.
'phpdoc_align' => true,
// PHPDoc annotation descriptions should not be a sentence.
'phpdoc_annotation_without_dot' => true,
// Docblocks should have the same indentation as the documented subject.
'phpdoc_indent' => true,
// Fixes PHPDoc inline tags.
'phpdoc_inline_tag_normalizer' => true,
// Changes doc blocks from single to multi line, or reversed. Works for class constants, properties and methods only.
'phpdoc_line_span' => true,
// `@access` annotations should be omitted from PHPDoc.
'phpdoc_no_access' => true,
// No alias PHPDoc tags should be used.
'phpdoc_no_alias_tag' => true,
// `@return void` and `@return null` annotations should be omitted from PHPDoc.
'phpdoc_no_empty_return' => true,
// Classy that does not inherit must not have `@inheritdoc` tags.
'phpdoc_no_useless_inheritdoc' => true,
// Annotations in PHPDoc should be ordered so that `@param` annotations come first, then `@throws` annotations, then `@return` annotations.
'phpdoc_order' => true,
// Order phpdoc tags by value.
'phpdoc_order_by_value' => ['annotations' => ['covers', 'throws']],
// The type of `@return` annotations of methods returning a reference to itself must the configured one.
'phpdoc_return_self_reference' => true,
// Scalar types should always be written in the same form. `int` not `integer`, `bool` not `boolean`, `float` not `real` or `double`.
'phpdoc_scalar' => true,
// Annotations in PHPDoc should be grouped together so that annotations of the same type immediately follow each other, and annotations of a different type are separated by a single blank line.
'phpdoc_separation' => true,
// Single line `@var` PHPDoc should have proper spacing.
'phpdoc_single_line_var_spacing' => true,
// Fixes casing of PHPDoc tags.
'phpdoc_tag_casing' => true,
// Would be neat, but breaks cases where the parents don't have annotations
// // EXPERIMENTAL: Takes `@param` annotations of non-mixed types and adjusts accordingly the function signature. Requires PHP >= 7.0.
// 'phpdoc_to_param_type' => true,
// // EXPERIMENTAL: Takes `@var` annotation of non-mixed types and adjusts accordingly the property signature. Requires PHP >= 7.4.
// 'phpdoc_to_property_type' => true,
// // EXPERIMENTAL: Takes `@return` annotation of non-mixed types and adjusts accordingly the function signature. Requires PHP >= 7.0.
// 'phpdoc_to_return_type' => true,
// PHPDoc should start and end with content, excluding the very first and last line of the docblocks.
'phpdoc_trim' => true,
// Removes extra blank lines after summary and after description in PHPDoc.
'phpdoc_trim_consecutive_blank_line_separation' => true,
// The correct case must be used for standard PHP types in PHPDoc.
'phpdoc_types' => true,
// Sorts PHPDoc types.
'phpdoc_types_order' => true,
// `@var` and `@type` annotations must have type and name in the correct order.
'phpdoc_var_annotation_correct_order' => true,
// Converts `pow` to the `**` operator.
'pow_to_exponentiation' => true,
// Replaces `rand`, `srand`, `getrandmax` functions calls with their `mt_*` analogs or `random_int`.
'random_api_migration' => true,
// Callables must be called without using `call_user_func*` when possible.
'regular_callable_call' => true,
// Local, dynamic and directly referenced variables should not be assigned and directly returned by a function or method.
'return_assignment' => true,
// There should be one or no space before colon, and one space after it in return type declarations, according to configuration.
'return_type_declaration' => true,
// Inside class or interface element `self` should be preferred to the class name itself.
'self_accessor' => true,
// Instructions must be terminated with a semicolon.
'semicolon_after_instruction' => true,
// Cast shall be used, not `settype`.
'set_type_to_cast' => true,
// Cast `(boolean)` and `(integer)` should be written as `(bool)` and `(int)`, `(double)` and `(real)` as `(float)`, `(binary)` as `(string)`.
'short_scalar_cast' => true,
// Converts explicit variables in double-quoted strings and heredoc syntax from simple to complex format (`${` to `{$`).
'simple_to_complex_string_variable' => true,
// Simplify `if` control structures that return the boolean result of their condition.
'simplified_if_return' => true,
// A return statement wishing to return `void` should not return `null`.
'simplified_null_return' => true,
// A PHP file without end tag must always end with a single empty line feed.
'single_blank_line_at_eof' => true,
// There should be exactly one blank line before a namespace declaration.
'single_blank_line_before_namespace' => true,
// There MUST NOT be more than one property or constant declared per statement.
'single_class_element_per_statement' => true,
// There MUST be one use keyword per declaration.
'single_import_per_statement' => true,
// Each namespace use MUST go on its own line and there MUST be one blank line after the use statements block.
'single_line_after_imports' => true,
// Single-line comments and multi-line comments with only one line of actual content should use the `//` syntax.
'single_line_comment_style' => true,
// Convert double quotes to single quotes for simple strings.
'single_quote' => true,
// Ensures a single space after language constructs.
'single_space_after_construct' => true,
// Each trait `use` must be done as single statement.
'single_trait_insert_per_statement' => true,
// Fix whitespace after a semicolon.
'space_after_semicolon' => true,
// Increment and decrement operators should be used if possible.
'standardize_increment' => true,
// Replace all `<>` with `!=`.
'standardize_not_equals' => true,
// String tests for empty must be done against `''`, not with `strlen`.
'string_length_to_empty' => true,
// A case should be followed by a colon and not a semicolon.
'switch_case_semicolon_to_colon' => true,
// Removes extra spaces between colon and case value.
'switch_case_space' => true,
// Switch case must not be ended with `continue` but with `break`.
'switch_continue_to_break' => true,
// Standardize spaces around ternary operator.
'ternary_operator_spaces' => true,
// Use the Elvis operator `?:` where possible.
'ternary_to_elvis_operator' => true,
// Use `null` coalescing operator `??` where possible. Requires PHP >= 7.0.
'ternary_to_null_coalescing' => true,
// Multi-line arrays, arguments list and parameters list must have a trailing comma.
'trailing_comma_in_multiline' => ['after_heredoc' => true, 'elements' => ['arguments', 'arrays', 'parameters']],
// Arrays should be formatted like function/method arguments, without leading or trailing single line space.
'trim_array_spaces' => true,
// A single space or none should be around union type operator.
'types_spaces' => true,
// Unary operators should be placed adjacent to their operands.
'unary_operator_spaces' => true,
// Anonymous functions with one-liner return statement must use arrow functions.
'use_arrow_functions' => true,
// Visibility MUST be declared on all properties and methods; `abstract` and `final` MUST be declared before the visibility; `static` MUST be declared after the visibility.
'visibility_required' => true,
// In array declaration, there MUST be a whitespace after each comma.
'whitespace_after_comma_in_array' => true,
])
->setFinder(
PhpCsFixer\Finder::create()
->exclude('vendor')
->exclude('var')
->exclude('docker')
->exclude('src/Entity')
->notPath('src/Core/DB/DefaultSettings.php')
->in(__DIR__),
);

784
CONFIGURE Normal file
View File

@ -0,0 +1,784 @@
Configuration options
=====================
The main configuration file for StatusNet (excepting configurations for
dependency software) is config.php in your StatusNet directory. If you
edit any other file in the directory, like lib/default.php (where most
of the defaults are defined), you will lose your configuration options
in any upgrade, and you will wish that you had been more careful.
Starting with version 0.9.0, a Web based configuration panel has been
added to StatusNet. The preferred method for changing config options is
to use this panel.
A command-line script, setconfig.php, can be used to set individual
configuration options. It's in the scripts/ directory.
Starting with version 0.7.1, you can put config files in the
/etc/statusnet/ directory on your server, if it exists. Config files
will be included in this order:
* /etc/statusnet/statusnet.php - server-wide config
* /etc/statusnet/<servername>.php - for a virtual host
* /etc/statusnet/<servername>_<pathname>.php - for a path
* INSTALLDIR/config.php - for a particular implementation
Almost all configuration options are made through a two-dimensional
associative array, cleverly named $config. A typical configuration
line will be:
$config['section']['option'] = value;
For brevity, the following documentation describes each section and
option.
site
----
This section is a catch-all for site-wide variables.
name: the name of your site, like 'YourCompany Microblog'.
server: the server part of your site's URLs, like 'example.net'.
path: The path part of your site's URLs, like 'statusnet' or ''
(installed in root).
fancy: whether or not your site uses fancy URLs (see Fancy URLs
section above). Default is false.
logfile: full path to a file for StatusNet to save logging
information to. You may want to use this if you don't have
access to syslog.
logdebug: whether to log additional debug info like backtraces on
hard errors. Default false.
locale_path: full path to the directory for locale data. Unless you
store all your locale data in one place, you probably
don't need to use this.
language: default language for your site. Defaults to US English.
Note that this is overridden if a user is logged in and has
selected a different language. It is also overridden if the
user is NOT logged in, but their browser requests a different
langauge. Since pretty much everybody's browser requests a
language, that means that changing this setting has little or
no effect in practice.
languages: A list of languages supported on your site. Typically you'd
only change this if you wanted to disable support for one
or another language:
"unset($config['site']['languages']['de'])" will disable
support for German.
theme: Theme for your site (see Theme section). Two themes are
provided by default: 'default' and 'stoica' (the one used by
Identi.ca). It's appreciated if you don't use the 'stoica' theme
except as the basis for your own.
email: contact email address for your site. By default, it's extracted
from your Web server environment; you may want to customize it.
broughtbyurl: name of an organization or individual who provides the
service. Each page will include a link to this name in the
footer. A good way to link to the blog, forum, wiki,
corporate portal, or whoever is making the service available.
broughtby: text used for the "brought by" link.
timezone: default timezone for message display. Users can set their
own time zone. Defaults to 'UTC', which is a pretty good default.
closed: If set to 'true', will disallow registration on your site.
This is a cheap way to restrict accounts to only one
individual or group; just register the accounts you want on
the service, *then* set this variable to 'true'.
inviteonly: If set to 'true', will only allow registration if the user
was invited by an existing user.
private: If set to 'true', anonymous users will be redirected to the
'login' page. Also, API methods that normally require no
authentication will require it. Note that this does not turn
off registration; use 'closed' or 'inviteonly' for the
behaviour you want.
notice: A plain string that will appear on every page. A good place
to put introductory information about your service, or info about
upgrades and outages, or other community info. Any HTML will
be escaped.
logo: URL of an image file to use as the logo for the site. Overrides
the logo in the theme, if any.
ssllogo: URL of an image file to use as the logo on SSL pages. If unset,
theme logo is used instead.
ssl: Whether to use SSL and https:// URLs for some or all pages.
Possible values are 'always' (use it for all pages), 'never'
(don't use it for any pages), or 'sometimes' (use it for
sensitive pages that include passwords like login and registration,
but not for regular pages). Default to 'never'.
sslproxy: Whether to force GNUsocial to think it is HTTPS when the
server gives no such information. I.e. when you're using a reverse
proxy that adds the encryption layer but the webserver that runs PHP
isn't configured with a key and certificate.
sslserver: use an alternate server name for SSL URLs, like
'secure.example.org'. You should be careful to set cookie
parameters correctly so that both the SSL server and the
"normal" server can access the session cookie and
preferably other cookies as well.
shorturllength: ignored. See 'url' section below.
dupelimit: minimum time allowed for one person to say the same thing
twice. Default 60s. Anything lower is considered a user
or UI error.
textlimit: default max size for texts in the site. Defaults to 0 (no limit).
Can be fine-tuned for notices, messages, profile bios and group descriptions.
db
--
This section is a reference to the configuration options for
DB_DataObject (see <http://ur1.ca/7xp>). The ones that you may want to
set are listed below for clarity.
database: a DSN (Data Source Name) for your StatusNet database. This is
in the format 'protocol://username:password@hostname/databasename',
where 'protocol' is 'mysql' or 'mysqli' (or possibly 'postgresql', if you
really know what you're doing), 'username' is the username,
'password' is the password, and etc.
ini_yourdbname: if your database is not named 'statusnet', you'll need
to set this to point to the location of the
statusnet.ini file. Note that the real name of your database
should go in there, not literally 'yourdbname'.
db_driver: You can try changing this to 'MDB2' to use the other driver
type for DB_DataObject, but note that it breaks the OpenID
libraries, which only support PEAR::DB.
debug: On a database error, you may get a message saying to set this
value to 5 to see debug messages in the browser. This breaks
just about all pages, and will also expose the username and
password
quote_identifiers: Set this to true if you're using postgresql.
type: either 'mysql' or 'postgresql' (used for some bits of
database-type-specific SQL in the code). Defaults to mysql.
mirror: you can set this to an array of DSNs, like the above
'database' value. If it's set, certain read-only actions will
use a random value out of this array for the database, rather
than the one in 'database' (actually, 'database' is overwritten).
You can offload a busy DB server by setting up MySQL replication
and adding the slaves to this array. Note that if you want some
requests to go to the 'database' (master) server, you'll need
to include it in this array, too.
utf8: whether to talk to the database in UTF-8 mode. This is the default
with new installations, but older sites may want to turn it off
until they get their databases fixed up. See "UTF-8 database"
above for details.
schemacheck: when to let plugins check the database schema to add
tables or update them. Values can be 'runtime' (default)
or 'script'. 'runtime' can be costly (plugins check the
schema on every hit, adding potentially several db
queries, some quite long), but not everyone knows how to
run a script. If you can, set this to 'script' and run
scripts/checkschema.php whenever you install or upgrade a
plugin.
syslog
------
By default, StatusNet sites log error messages to the syslog facility.
(You can override this using the 'logfile' parameter described above).
appname: The name that StatusNet uses to log messages. By default it's
"statusnet", but if you have more than one installation on the
server, you may want to change the name for each instance so
you can track log messages more easily.
priority: level to log at. Currently ignored.
facility: what syslog facility to used. Defaults to LOG_USER, only
reset if you know what syslog is and have a good reason
to change it.
queue
-----
You can configure the software to queue time-consuming tasks, like
sending out SMS email or XMPP messages, for off-line processing. See
'Queues and daemons' above for how to set this up.
enabled: Whether to uses queues. Defaults to false.
daemon: Wather to use queuedaemon. Defaults to false, which means
you'll use OpportunisticQM plugin.
subsystem: Which kind of queueserver to use. Values include "db" for
our hacked-together database queuing (no other server
required) and "stomp" for a stomp server.
stomp_server: "broker URI" for stomp server. Something like
"tcp://hostname:61613". More complicated ones are
possible; see your stomp server's documentation for
details.
queue_basename: a root name to use for queues (stomp only). Typically
something like '/queue/sitename/' makes sense. If running
multiple instances on the same server, make sure that
either this setting or $config['site']['nickname'] are
unique for each site to keep them separate.
stomp_username: username for connecting to the stomp server; defaults
to null.
stomp_password: password for connecting to the stomp server; defaults
to null.
stomp_persistent: keep items across queue server restart, if enabled.
Under ActiveMQ, the server configuration determines if and how
persistent storage is actually saved.
If using a message queue server other than ActiveMQ, you may
need to disable this if it does not support persistence.
stomp_transactions: use transactions to aid in error detection.
A broken transaction will be seen quickly, allowing a message
to be redelivered immediately if a daemon crashes.
If using a message queue server other than ActiveMQ, you may
need to disable this if it does not support transactions.
stomp_acks: send acknowledgements to aid in flow control.
An acknowledgement of successful processing tells the server
we're ready for more and can help keep things moving smoothly.
This should *not* be turned off when running with ActiveMQ, but
if using another message queue server that does not support
acknowledgements you might need to disable this.
softlimit: an absolute or relative "soft memory limit"; daemons will
restart themselves gracefully when they find they've hit
this amount of memory usage. Defaults to 90% of PHP's global
memory_limit setting.
inboxes: delivery of messages to receiver's inboxes can be delayed to
queue time for best interactive performance on the sender.
This may however be annoyingly slow when using the DB queues,
so you can set this to false if it's causing trouble.
breakout: for stomp, individual queues are by default grouped up for
best scalability. If some need to be run by separate daemons,
etc they can be manually adjusted here.
Default will share all queues for all sites within each group.
Specify as <group>/<queue> or <group>/<queue>/<site>,
using nickname identifier as site.
'main/distrib' separate "distrib" queue covering all sites
'xmpp/xmppout/mysite' separate "xmppout" queue covering just 'mysite'
max_retries: for stomp, drop messages after N failed attempts to process.
Defaults to 10.
dead_letter_dir: for stomp, optional directory to dump data on failed
queue processing events after discarding them.
stomp_no_transactions: for stomp, the server does not support transactions,
so do not try to user them. This is needed for http://www.morbidq.com/.
stomp_no_acks: for stomp, the server does not support acknowledgements.
so do not try to user them. This is needed for http://www.morbidq.com/.
license
-------
The default license to use for your users notices. The default is the
Creative Commons Attribution 3.0 license, which is probably the right
choice for any public site. Note that some other servers will not
accept notices if you apply a stricter license than this.
type: one of 'cc' (for Creative Commons licenses), 'allrightsreserved'
(default copyright), or 'private' (for private and confidential
information).
owner: for 'allrightsreserved' or 'private', an assigned copyright
holder (for example, an employer for a private site). If
not specified, will be attributed to 'contributors'.
url: URL of the license, used for links.
title: Title for the license, like 'Creative Commons Attribution 3.0'.
image: A button shown on each page for the license.
mail
----
This is for configuring out-going email. We use PEAR's Mail module,
see: http://pear.php.net/manual/en/package.mail.mail.factory.php
backend: the backend to use for mail, one of 'mail', 'sendmail', and
'smtp'. Defaults to PEAR's default, 'mail'.
params: if the mail backend requires any parameters, you can provide
them in an associative array.
nickname
--------
This is for configuring nicknames in the service.
blacklist: an array of strings for usernames that may not be
registered. A default array exists for strings that are
used by StatusNet (e.g. 'doc', 'main', 'avatar', 'theme')
but you may want to add others if you have other software
installed in a subdirectory of StatusNet or if you just
don't want certain words used as usernames.
featured: an array of nicknames of 'featured' users of the site.
Can be useful to draw attention to well-known users, or
interesting people, or whatever.
avatar
------
For configuring avatar access.
dir: Directory to look for avatar files and to put them into.
Defaults to avatar subdirectory of install directory; if
you change it, make sure to change path, too.
path: Path to avatars. Defaults to path for avatar subdirectory,
but you can change it if you wish. Note that this will
be included with the avatar server, too.
server: If set, defines another server where avatars are stored in the
root directory. Note that the 'avatar' subdir still has to be
writeable. You'd typically use this to split HTTP requests on
the client to speed up page loading, either with another
virtual server or with an NFS or SAMBA share. Clients
typically only make 2 connections to a single server at a
time <http://ur1.ca/6ih>, so this can parallelize the job.
Defaults to null.
ssl: Whether to access avatars using HTTPS. Defaults to null, meaning
to guess based on site-wide SSL settings.
public
------
For configuring the public stream.
localonly: If set to true, only messages posted by users of this
service (rather than other services, filtered through OStatus)
are shown in the public stream. Default true.
blacklist: An array of IDs of users to hide from the public stream.
Useful if you have someone making excessive Twitterfeed posts
to the site, other kinds of automated posts, testing bots, etc.
autosource: Sources of notices that are from automatic posters, and thus
should be kept off the public timeline. Default empty.
theme
-----
server: Like avatars, you can speed up page loading by pointing the
theme file lookup to another server (virtual or real).
Defaults to NULL, meaning to use the site server.
dir: Directory where theme files are stored. Used to determine
whether to show parts of a theme file. Defaults to the theme
subdirectory of the install directory.
path: Path part of theme URLs, before the theme name. Relative to the
theme server. It may make sense to change this path when upgrading,
(using version numbers as the path) to make sure that all files are
reloaded by caching clients or proxies. Defaults to null,
which means to use the site path + '/theme'.
ssl: Whether to use SSL for theme elements. Default is null, which means
guess based on site SSL settings.
sslserver: SSL server to use when page is HTTPS-encrypted. If
unspecified, site ssl server and so on will be used.
sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted.
javascript
----------
server: You can speed up page loading by pointing the
theme file lookup to another server (virtual or real).
Defaults to NULL, meaning to use the site server.
path: Path part of Javascript URLs. Defaults to null,
which means to use the site path + '/js/'.
ssl: Whether to use SSL for JavaScript files. Default is null, which means
guess based on site SSL settings.
sslserver: SSL server to use when page is HTTPS-encrypted. If
unspecified, site ssl server and so on will be used.
sslpath: If sslserver if defined, path to use when page is HTTPS-encrypted.
bustframes: If true, all web pages will break out of framesets. If false,
can comfortably live in a frame or iframe... probably. Default
to true.
xmpp
----
For configuring the XMPP sub-system.
enabled: Whether to accept and send messages by XMPP. Default false.
server: server part of XMPP ID for update user.
port: connection port for clients. Default 5222, which you probably
shouldn't need to change.
user: username for the client connection. Users will receive messages
from 'user'@'server'.
resource: a unique identifier for the connection to the server. This
is actually used as a prefix for each XMPP component in the system.
password: password for the user account.
host: some XMPP domains are served by machines with a different
hostname. (For example, @gmail.com GTalk users connect to
talk.google.com). Set this to the correct hostname if that's the
case with your server.
encryption: Whether to encrypt the connection between StatusNet and the
XMPP server. Defaults to true, but you can get
considerably better performance turning it off if you're
connecting to a server on the same machine or on a
protected network.
debug: if turned on, this will make the XMPP library blurt out all of
the incoming and outgoing messages as XML stanzas. Use as a
last resort, and never turn it on if you don't have queues
enabled, since it will spit out sensitive data to the browser.
public: an array of JIDs to send _all_ notices to. This is useful for
participating in third-party search and archiving services.
invite
------
For configuring invites.
enabled: Whether to allow users to send invites. Default true.
tag
---
Miscellaneous tagging stuff.
dropoff: Decay factor for tag listing, in seconds.
Defaults to exponential decay over ten days; you can twiddle
with it to try and get better results for your site.
popular
-------
Settings for the "popular" section of the site.
dropoff: Decay factor for popularity listing, in seconds.
Defaults to exponential decay over ten days; you can twiddle
with it to try and get better results for your site.
daemon
------
For daemon processes.
piddir: directory that daemon processes should write their PID file
(process ID) to. Defaults to /var/run/, which is where this
stuff should usually go on Unix-ish systems.
user: If set, the daemons will try to change their effective user ID
to this user before running. Probably a good idea, especially if
you start the daemons as root. Note: user name, like 'daemon',
not 1001.
group: If set, the daemons will try to change their effective group ID
to this named group. Again, a name, not a numerical ID.
emailpost
---------
For post-by-email.
enabled: Whether to enable post-by-email. Defaults to true. You will
also need to set up maildaemon.php.
sms
---
For SMS integration.
enabled: Whether to enable SMS integration. Defaults to true. Queues
should also be enabled.
integration
-----------
A catch-all for integration with other systems.
taguri: base for tag:// URIs. Defaults to site-server + ',2009'.
inboxes
-------
For notice inboxes.
enabled: No longer used. If you set this to something other than true,
StatusNet will no longer run.
throttle
--------
For notice-posting throttles.
enabled: Whether to throttle posting. Defaults to false.
count: Each user can make this many posts in 'timespan' seconds. So, if count
is 100 and timespan is 3600, then there can be only 100 posts
from a user every hour.
timespan: see 'count'.
profile
-------
Profile management.
biolimit: max character length of bio; 0 means no limit; null means to use
the site text limit default.
backup: whether users can backup their own profiles. Defaults to true.
restore: whether users can restore their profiles from backup files. Defaults
to true.
delete: whether users can delete their own accounts. Defaults to false.
move: whether users can move their accounts to another server. Defaults
to true.
newuser
-------
Options with new users.
default: nickname of a user account to automatically subscribe new
users to. Typically this would be system account for e.g.
service updates or announcements. Users are able to unsub
if they want. Default is null; no auto subscribe.
welcome: nickname of a user account that sends welcome messages to new
users. Can be the same as 'default' account, although on
busy servers it may be a good idea to keep that one just for
'urgent' messages. Default is null; no message.
If either of these special user accounts are specified, the users should
be created before the configuration is updated.
attachments
-----------
The software lets users upload files with their notices. You can configure
the types of accepted files by mime types and a trio of quota options:
per file, per user (total), per user per month.
We suggest the use of the pecl file_info extension to handle mime type
detection.
supported: an array of mime types you accept to store and distribute,
like 'image/gif', 'video/mpeg', 'audio/mpeg', etc. Make sure you
setup your server to properly recognize the types you want to
support.
uploads: false to disable uploading files with notices (true by default).
For quotas, be sure you've set the upload_max_filesize and post_max_size
in php.ini to be large enough to handle your upload. In httpd.conf
(if you're using apache), check that the LimitRequestBody directive isn't
set too low (it's optional, so it may not be there at all).
process_links: follow redirects and save all available file information
(mimetype, date, size, oembed, etc.). Defaults to true.
file_quota: maximum size for a single file upload in bytes. A user can send
any amount of notices with attachments as long as each attachment
is smaller than file_quota.
user_quota: total size in bytes a user can store on this server. Each user
can store any number of files as long as their total size does
not exceed the user_quota.
monthly_quota: total size permitted in the current month. This is the total
size in bytes that a user can upload each month.
dir: directory accessible to the Web process where uploads should go.
Defaults to the 'file' subdirectory of the install directory, which
should be writeable by the Web user.
server: server name to use when creating URLs for uploaded files.
Defaults to null, meaning to use the default Web server. Using
a virtual server here can speed up Web performance.
path: URL path, relative to the server, to find files. Defaults to
main path + '/file/'.
ssl: whether to use HTTPS for file URLs. Defaults to null, meaning to
guess based on other SSL settings.
sslserver: if specified, this server will be used when creating HTTPS
URLs. Otherwise, the site SSL server will be used, with /file/ path.
sslpath: if this and the sslserver are specified, this path will be used
when creating HTTPS URLs. Otherwise, the attachments|path value
will be used.
show_thumbs: show thumbnails in notice lists for uploaded images, and photos
and videos linked remotely that provide oEmbed info. Defaults to true.
show_html: show (filtered) text/html attachments (and oEmbed HTML etc.).
Doesn't affect AJAX calls. Defaults to false.
filename_base: for new files, choose one: 'upload', 'hash'. Defaults to hash.
group
-----
Options for group functionality.
maxaliases: maximum number of aliases a group can have. Default 3. Set
to 0 or less to prevent aliases in a group.
desclimit: maximum number of characters to allow in group descriptions.
null (default) means to use the site-wide text limits. 0
means no limit.
addtag: Whether to add a tag for the group nickname for every group post
(pre-1.0.x behaviour). Defaults to false.
search
------
Some stuff for search.
type: type of search. Ignored if PostgreSQL or Sphinx are enabled. Can either
be 'fulltext' or 'like' (default). The former is faster and more efficient
but requires the lame old MyISAM engine for MySQL. The latter
will work with InnoDB but could be miserably slow on large
systems. We'll probably add another type sometime in the future,
with our own indexing system (maybe like MediaWiki's).
sessions
--------
Session handling.
handle: boolean. Whether we should register our own PHP session-handling
code (using the database and cache layers if enabled). Defaults to false.
Setting this to true makes some sense on large or multi-server
sites, but it probably won't hurt for smaller ones, either.
debug: whether to output debugging info for session storage. Can help
with weird session bugs, sometimes. Default false.
ping
----
Using the "XML-RPC Ping" method initiated by weblogs.com, the site can
notify third-party servers of updates.
notify: an array of URLs for ping endpoints. Default is the empty
array (no notification).
notice
------
Configuration options specific to notices.
contentlimit: max length of the plain-text content of a notice.
Default is null, meaning to use the site-wide text limit.
0 means no limit.
defaultscope: default scope for notices. If null, the default
scope depends on site/private. It's 1 if the site is private,
0 otherwise. Set this value to override.
message
-------
Configuration options specific to messages.
contentlimit: max length of the plain-text content of a message.
Default is null, meaning to use the site-wide text limit.
0 means no limit.
logincommand
------------
Configuration options for the login command.
disabled: whether to enable this command. If enabled, users who send
the text 'login' to the site through any channel will
receive a link to login to the site automatically in return.
Possibly useful for users who primarily use an XMPP or SMS
interface and can't be bothered to remember their site
password. Note that the security implications of this are
pretty serious and have not been thoroughly tested. You
should enable it only after you've convinced yourself that
it is safe. Default is 'false'.
singleuser
----------
If an installation has only one user, this can simplify a lot of the
interface. It also makes the user's profile the root URL.
enabled: Whether to run in "single user mode". Default false.
nickname: nickname of the single user. If no nickname is specified,
the site owner account will be used (if present).
robotstxt
---------
We put out a default robots.txt file to guide the processing of
Web crawlers. See http://www.robotstxt.org/ for more information
on the format of this file.
crawldelay: if non-empty, this value is provided as the Crawl-Delay:
for the robots.txt file. see http://ur1.ca/l5a0
for more information. Default is zero, no explicit delay.
disallow: Array of (virtual) directories to disallow. Default is 'main',
'search', 'message', 'settings', 'admin'. Ignored when site
is private, in which case the entire site ('/') is disallowed.
api
---
Options for the Twitter-like API.
realm: HTTP Basic Auth realm (see http://tools.ietf.org/html/rfc2617
for details). Some third-party tools like ping.fm want this to be
'Identi.ca API', so set it to that if you want to. default = null,
meaning 'something based on the site name'.
nofollow
--------
We optionally put 'rel="nofollow"' on some links in some pages. The
following configuration settings let you fine-tune how or when things
are nofollowed. See http://en.wikipedia.org/wiki/Nofollow for more
information on what 'nofollow' means.
subscribers: whether to nofollow links to subscribers on the profile
and personal pages. Default is true.
members: links to members on the group page. Default true.
peopletag: links to people listed in the peopletag page. Default true.
external: external links in notices. One of three values: 'sometimes',
'always', 'never'. If 'sometimes', then external links are not
nofollowed on profile, notice, and favorites page. Default is
'sometimes'.
url
---
These are some options for fine-tuning how and when the server will
shorten URLs.
shortener: URL shortening service to use by default. Users can override
individually. 'internal' by default.
maxurllength: If an URL is strictly longer than this limit, it will be
shortened. Note that the URL shortener service may return an
URL longer than this limit. Defaults to 100. Users can
override. If set to 0, all URLs will be shortened.
maxnoticelength: If a notice is strictly longer than this limit, all
URLs in the notice will be shortened. Users can override.
-1 means the text limit for notices.
router
------
We use a router class for mapping URLs to code. This section controls
how that router works.
cache: whether to cache the router in cache layers. Defaults to true,
but may be set to false for developers (who might be actively
adding pages, so won't want the router cached) or others who see
strange behavior. You're unlikely to need this unless developing..
http
----
Settings for the HTTP client.
ssl_cafile: location of the CA file for SSL. If not set, won't verify
SSL peers. Default unset.
curl: Use cURL <http://curl.haxx.se/> for doing HTTP calls. You must
have the PHP curl extension installed for this to work.
proxy_host: Host to use for proxying HTTP requests. If unset, doesn't
do any HTTP proxy stuff. Default unset.
proxy_port: Port to use to connect to HTTP proxy host. Default null.
proxy_user: Username to use for authenticating to the HTTP proxy. Default null.
proxy_password: Password to use for authenticating to the HTTP proxy. Default null.
proxy_auth_scheme: Scheme to use for authenticating to the HTTP proxy. Default null.
plugins
-------
default: associative array mapping plugin name to array of arguments. To disable
a default plugin, unset its value in this array.
locale_path: path for finding plugin locale files. In the plugin's directory
by default.
server: Server to find static files for a plugin when the page is plain old HTTP.
Defaults to site/server (same as pages). Use this to move plugin CSS and
JS files to a CDN.
sslserver: Server to find static files for a plugin when the page is HTTPS. Defaults
to site/server (same as pages). Use this to move plugin CSS and JS files
to a CDN.
path: Path to the plugin files. defaults to site/path + '/plugins/'. Expects that
each plugin will have a subdirectory at plugins/NameOfPlugin. Change this
if you're using a CDN.
sslpath: Path to use on the SSL server. Same as plugins/path.
performance
-----------
high: if you need high performance, or if you're seeing bad
performance, set this to true. It will turn off some high-intensity code from
the site.
oldschool
---------
enabled: enable certain old-style user settings options, like stream-only mode,
conversation trees, and nicknames in streams. Off by default, and
may not be well supported in future versions.

View File

@ -1,105 +0,0 @@
Credits for GNU social
======================
The following is an incomplete list of developers
who've worked on GNU social, or its predecessors
StatusNet and Free Social. Apologies for any
oversight; please let mail@diogo.site know if
anyone's been overlooked in error.
Current team
------------
* Alexei Sorokin
* Diogo Cordeiro
* Eliseu Amaro
* Hugo Sales
V2 team
-------
* Diogo Cordeiro
* Alexei Sorokin
* Bruno Casteleiro
Additional Contributors
-----------------------
* Akio
* Blaine Cook
* Bob Mottram
* Brenda Wallace
* Brett Taylor
* Brian Hendrickson
* Brigitte Schuster
* Ciaran Gultnieks
* Craig Andrews
* Daniel Supernault
* Dan Moore
* David Yip
* Deb Nicholson
* Donald Robertson
* Eric Helgeson
* Federico Marani
* Fil
* Garret Buell
* Henry Story
* Ian Denhart
* Jeffery To
* Jeff Mitchell
* Ken Sedgwick
* Leslie Michael Orchard
* Maiyannah Bishop
* Matthew Gregg
* Matt Lee
* mEDI
* Melvin Carvalho
* Michael Landers
* Miguel Dantas
* Mikael Nordfeldth
* Mike Cochrane
* Moonman
* Neil E Hodges
* Normandy
* Ori Avtalion
* Sean Murphy
* Stéphane Bérubé
* Steven DuBois
* Tobias Diekershoff
* Verius
Credits for StatusNet
--------------
Leads
* Evan Prodromou
* Zach Copley
Team
* Adrian Lang
* Brion Vibber
* 'drry'
* Earle Martin
* Erik Stambaugh
* Florian Biree
* Gina Haeussge
* James Walker
* Joshua Judson Rosen (rozzin)
* Ken Sheppardson
* Marie-Claude Doyon
* Meitar Moscovitz
* Ori Avtalion
* Robin Millette
* Samantha Doherty
* Sarven Capadisli
* Simon Waters, Surevine
* Tryggvi Björgvinsson
Translators
-----------
* Siebrand Mazeland
* Tiago 'gouki' Faria
* TranslateWiki.net
A special thanks to the thousands of people who
have tried out GNU social, told their friends, and
built the fediverse network to what it is today.
License help from
-----------------
* Bradley M. Kuhn

View File

@ -1,3 +0,0 @@
In the `dev` environment, the default values for the config table are reloaded on each HTTP request
In case you want to override this, add `SOCIAL_NO_RELOAD_DEFAULTS=1` to your .env.local file

View File

@ -1 +0,0 @@
../../INSTALL.md

View File

@ -1,762 +0,0 @@
Configuration options
================================================================================
The configuration for GNU social is stored in the database table
`config`.
A Web based configuration panel exists so the site admin can configure
GNU social. The preferred method for changing config options is to use this
panel.
A command-line script, `set_config.php`, can be used to set individual
configuration options. It's in the `bin/` directory.
Almost all configuration options are made through a two-dimensional
associative array, cleverly named `$config`. A typical configuration
line will be:
$config['section']['setting] = value;
The following documentation describes each section and setting.
site
-------------------------------------------------------------------------------
This section is a catch-all for site-wide variables.
* `name` (string, required, defaults to the value provided in the configre script,
sitename): the name of your site, like 'YourCompany Microblog'.
* `server` (string, required, defaults to the value provided in the configre script,
sitename): the server domain, like 'example.net'.
* `notice` (string, default null): A plain string that will appear on every page. A good
place to put introductory information about your service, or info about upgrades and
outages, or other community info. Any HTML will be escaped.
* `theme` (string, default 'default'): Theme for your site (see Theme section).
* `logo` (string, default null): URL of an image file to use as the logo for the site.
Overrides the logo in the theme, if any.
* `language` (string, default "en"): default language for your site. Defaults to English.
Note that this is overridden, if enabled in the following setting, if a user is logged
in and has selected a different language or if the user is NOT logged in, but their
browser requests a different langauge. Since pretty much everybody's browser requests
a language, that means that changing this setting has little or no effect in practice.
* `detect_language` (boolean, default true): whether to use the most appropriate language
depending on the requester's browser preferences.
* `languages` (array, default null): A list of languages supported on your site. Typically
you'd only change this if you wanted to disable support for one or more languages:
`unset($config['site']['languages']['de'])` will disable support for German.
* `email` (string, required): contact email address for your site. By default, it's
extracted from your Web server environment or the value provided in the configure
script; you may want to customize it.
* `recovery_disclose` (boolean, default false): whether to confirm if the email exists
when attempting to login. Recommended to keep it false, for some privacy.
* `timezone` (string, default 'UTC'): default timezone for message display. Users
can set their own time zone. Defaults to 'UTC', which is a pretty good
default.
* `brought_by` (string, default null): text used for the "brought by" link.
* `brought_by_url` (string, default null): name of an organization or individual who
provides the service. Each page will include a link to this name in the footer or
sidebar. A good way to link to the blog, forum, wiki, corporate portal, or whoever is
making the service available.
* `closed` (boolean, default false): If set to 'true', will disallow registration on your
site. This is a easy way to restrict accounts to only one individual or group; just
register the accounts you want on the service, *then* set this variable to 'true'.
* `invite_only` (boolean, default false): If set to 'true', will only allow registration
if the user was invited by an existing user.
* `private` (boolean, default false): If set to 'true', anonymous users will be redirected
to the 'login' page. Also, API methods that normally require no authentication will
require it. Note that this does not turn off registration; use 'closed' or
'invite_only' for that behaviour.
* `ssl` (enum['always', 'sometimes', 'never'], default always'): Whether to use SSL and
https:// URLs for some or all pages.
Possible values are 'always' (use it for all pages), 'never' (don't use it for any
pages), or 'sometimes' (use it for sensitive pages that include passwords like login
and registration, but not for regular pages).
* `ssl_proxy` (string|boolean, default false): Whether to force GNUsocial to think it is
HTTPS when the server gives no such information. I.e. when you're using a reverse
proxy that adds the encryption layer but the webserver that runs PHP isn't configured
with a key and certificate. If a string is given, it will be used as the URL of the
proxy server.
* `duplicate_time_limit` (integer, default 60): minimum time allowed for one person to say
the same thing twice. Default 60s. If it happens faster than this, it's considered a
user or UI error.
* `text_limit` (integer, default 1000): default max size for texts in the site. Can be
fine-tuned for notices, messages, profile bios and group descriptions. Zero indicates
no limit.
* `x-static-delivery` (string, default null): when a string, use this as the header with
which to serve static files. Possible values are 'X-Sendfile' (for Apache and others)
and 'X-Accel-Redirect' (for nginx).
security
-------------------------------------------------------------------------------
* `hash_algos` (array, default ['sha1', 'sha256', 'sha512']): set to null for anything
that `hash_hmac()` can handle; can be any combination of the result of `hash_algos()`
db
-------------------------------------------------------------------------------
* `mirror` (array, default null): you can set this to an array of database connection
URIs. If it's set, load will be split among these, and replication will be enabled.
fix
-------------------------------------------------------------------------------
* `fancy_urls` (boolean, default true): fix any non-facy url to the correct form, when
possible.
* `http` (boolean, default true): fixe any http links to https.
queue
-------------------------------------------------------------------------------
You can configure the software to queue time-consuming tasks, like
sending out SMS, email or XMPP messages, for off-line processing.
* `enabled` (boolean, default true): Whether to uses queues.
* `daemon` (boolean, default false): Whether to use queuedaemon. False means
you'll use OpportunisticQM plugin.
* `threads` (int): How many queue processes to run. Defaults to number of cpu cores in
unix-like systems or 1 on other OSes.
* `subsystem` (enum["db", "stomp", "redis"], default 'db'): Which kind of
queueserver to use. Values include "db" for our database queuing (no other server
required), "stomp" for a stomp server amd "redis" for a Redis server.
* `basename` (string, default '/queue/gnusocial/'): a root name to use for queues (stomp
and redis only). Typically something like '/queue/sitename/' makes sense. If running
multiple instances on the same server, make sure that either this setting or
`$config['site']['nickname']` are unique for each site to keep them separate.
* `control_channel` (string, default '/topic/gnusocial/control'): the control channel used
for different queue processes to communicate.
* `monitor` (string, default null): URL endpoint to monitor queue status
* `soft_limit` (string, default '90%'): an absolute or relative "soft memory limit";
daemons will restart themselves gracefully when they find they've hit this amount of
memory usage. Relative means a percentage of PHP's global `memory_limit` setting.
* `spawn_delay` (integer, default 1): seconds to wait between deamon restarts.
* `debug_memory` (boolean, default false): log daemon's memory usage.
* `stomp_server` (string, default null): URI for stomp server. Something like
"tcp://hostname:61613". More complicated ones are possible; see your stomp server's
documentation for details.
* `stomp_username` (string, default null): username for connecting to the stomp server.
* `stomp_password` (string, default null): password for connecting to the stomp server.
* `stomp_persistent` (boolean, default true): Keep items across queue server restart, if
enabled. Note: Under ActiveMQ, the server configuration determines if and how
persistent storage is actually saved. Not all stomp servers support persistence.
* `stomp_transactions` (boolean, default true): use transactions to aid in error
detection. A broken transaction will be seen quickly, allowing a message to be
redelivered immediately if a daemon crashes. Not all stop servers support
transactions.
* `stomp_acks` (boolean, default true): send acknowledgements to aid in flow control. An
acknowledgement of successful processing tells the server we're ready for more and can
help keep things moving smoothly. This should *not* be turned off when running with
ActiveMQ, (it breaks if you do), but if using another message queue server that does
not support acknowledgements you might need to disable this.
* `stomp_manual_failover` (boolean, default true): if multiple servers are listed, treat
them as separate (enqueue on one randomly, listen on all).
* `max_retries` (integer, default 10): for stomp, drop messages after N failed
attempts to process.
* `dead_letter_dir` (string, default null): for stomp, optional directory to dump
data on failed queue processing events after discarding them.
avatar
-------------------------------------------------------------------------------
* `server` (string, default null): If set, defines another server where avatars are
stored. Note that the `dir` still has to be writeable. You'd typically use this to
split HTTP requests on the client to speed up page loading, either with another
virtual server or with an NFS or SAMBA share. Clients typically only make 2
connections to a single server at a time
<https://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.1.4>, so this can
parallelize the job.
* `url_base` (string, 'default '/avatar/'): URL where avatars can be found.
* `ssl` (boolean, default null): Whether to access avatars using HTTPS. Defaults
to null, meaning to guess based on site-wide SSL settings.
* `dir` (string, default 'file/avatar/'): Directory to save avatar files to.
* `max_size_px` (integer, default 300): Maximum width or height for user avatars, in pixels
javascript
-------------------------------------------------------------------------------
* `server` (string, default null): You can speed up page loading by pointing the
javascript file lookup to another server (virtual or real). Defaults to NULL, meaning
to use the site server.
* `url_base` (string default '/js/'): URL part for JavaScript files.
* `ssl` (boolean, default null): Whether to use SSL for JavaScript files. Default is null,
which means guess based on site SSL settings.
* `bust_frames` (boolean, default true): If true, all web pages will break out of
framesets. If false, can comfortably live in a frame or iframe... probably.
attachments
-------------------------------------------------------------------------------
* `server` (string, default null): Server name to use when creating URLs for uploaded
files. Defaults to null, meaning to use the default Web server. Using a virtual server
here can speed up Web performance.
* `url_base` (string, default '/file/'): URL path, relative to the server, to find
files. Defaults to main path + '/file/'.
* `ssl` (boolean, default null): Whether to use HTTPS for file URLs. Defaults to null,
meaning to use other SSL settings.
* `dir` (string, default '/file/uploads/'): Directory accessible to the Web process where
uploads should go.
* `supported` (array): An associative array of mime types you accept to store and
distribute, like 'image/gif', 'video/mpeg', 'audio/mpeg', to the corresponding file
extension. Make sure you setup your server to properly recognize the types you want to
support. It's important to use the result of calling `image_type_to_extension` for the
appropriate image type, in the case of images. This is so all parts of the code see
the same file extension for each image type (jpg vs jpeg). For example, to enable BMP
uploads, add this to the config.php file:
`image_type_to_mime_type(IMAGETYPE_BMP) => image_type_to_extension(IMAGETYPE_BMP);` See
https://www.php.net/manual/en/function.image-type-to-mime-type.php for a list of such
constants. If a filetype is not listed there, it's possible to add the mimetype and
the extension by hand, but they need to match those returned by the file command.
For quotas, be sure you've set the `upload_max_filesize` and `post_max_size` in php.ini to
be large enough to handle your upload. In httpd.conf (if you're using apache), check that
the LimitRequestBody directive isn't set too low (it's optional, so it may not be there at
all).
* `file_quota` (integer, defaults to minimum of `'post_max_size', 'upload_max_filesize',
'memory_limit'`): Maximum size for a single file upload, in bytes. A user can send any
amount of notices with attachments as long as each attachment is smaller than
file_quota.
* `user_quota` (integer, default 200M): Total size, in bytes, a user can store on this
server. Each user can store any number of files as long as their total size does not
exceed the user_quota.
* `monthly_quota` (integer, default 20M): Total size in bytes that a user can upload each
month.
* `uploads` (boolean, default true): Whether to allow uploading files with notices.
* `show_html` (boolean, default true): Whether to show (filtered) text/html attachments
(and oEmbed HTML etc.). Doesn't affect AJAX calls.
* `show_thumbs` (boolean, default true): Whether to show thumbnails in notice lists for
uploaded images, and photos and videos linked remotely that provide oEmbed info.
* `process_links` (boolean, default true): Whether to follow redirects and save all
available file information (mimetype, date, size, oembed, etc.).
* `ext_blacklist` (array, default []): associative array to either deny certain extensions or
change them to a different one. For example:
$config['attachments']['extblacklist']['php'] = 'phps'; // this turns .php into .phps
$config['attachments']['extblacklist']['exe'] = false; // this would deny any uploads
// of files with the "exe" extension
* `filename` (string, default hash): Name for new files, one of: 'upload', 'hash'.
* `memory_limit` (string, default '1024M'): PHP memory limit to use temporarily when
handling images
thumbnail
-------------------------------------------------------------------------------
* `server` (string, default null): Server name from which to serve thumbnails. Defaults to
null, meaning to use the default Web server. Using a virtual server here can speed up
Web performance.
* `url_base` (string, default '/thumb/'): URL path, relative to the server, to find
files.
* `ssl` (boolean, default null): Whether to use HTTPS for thumbnail URLs. Defaults to null,
meaning to use other SSL settings.
* `dir` (string, default '/file/thumbnails/'): Path where to store thumbnails.
* `crop` (boolean, default false): Whether to crop thumbnails (or scale them down)
* `max_size_px` (integer, default 1000): Thumbnails with an edge greater than this will
not be generated.
* `width` (integer, default 450): Width for generated thumbnails.
* `height` (integer, default 600): Heigth for generated thumbnails.
* `upscale` (boolean, default false): Whether to generate thumbnails bigger than the original.
* `animated` (boolean, default false): Whether to allow animated thumbnails.
theme
-------------------------------------------------------------------------------
* `server` (string, default null): Like avatars, you can speed up page loading
by pointing the theme file lookup to another server (virtual or real).
The default of null will use the same server as PA.
* `url_base` (string, default '/theme'): Path part of theme URLs, before the theme name.
Relative to the theme server. It may make sense to change this path when upgrading,
(using version numbers as the path) to make sure that all files are reloaded by
caching clients or proxies.
* `ssl` (boolean, default null): Whether to use SSL for theme elements. Default
is null, which means guess based on site SSL settings.
* `dir` (string, default "./themes"): Directory where theme files are stored.
Used to determine whether to show parts of a theme file. Defaults to the
theme subdirectory of the install directory.
plugins
-------------------------------------------------------------------------------
* `server` (string, default null): Server to find static files for a plugin when the page
is plain old HTTP. Defaults to site/server (same as pages). You can use this to move
plugin CSS and JS files to a CDN.
* `url_base` (string, default '/plugins/'): Path to the plugin files. Expects that each
plugin will have a subdirectory at plugins/NameOfPlugin. Change this if you're using
a CDN.
* `ssl` (boolean, default null) Whether to use ssl for files served by plugins.
* `core` (associative array, default TODO): Core GNU social modules, cannot be disabled.
* `default`: (associative array, default TODO): Mapping from plugin name to array of
plugin arguments.
* `locale_path` (string, default null): Path for finding plugin locale files. In the
plugin's directory by default.
license
-------------------------------------------------------------------------------
The default license to use for your users' notices. The default is the Creative Commons
Attribution 4.0 license, which is probably the right choice for any public site. Note that
some other servers will not accept notices if you apply a stricter license than this.
* `type` (enum["cc", "allrightsreserved", "private"], default 'cc'): One of 'cc' (for
Creative Commons licenses), 'allrightsreserved' (default copyright), or 'private' (for
private and confidential information).
* `owner` (string|boolean, default null): For 'allrightsreserved' or 'private', an
assigned copyright holder (for example, an employer for a private site). Use true to
attribute it to the poster.
* `url` (string, default 'https://creativecommons.org/licenses/by/4.0/'): URL of the
license, used for links.
* `title` (string, default 'Creative Commons Attribution 4.0'): Title for the license.
* `image` (string, default '/theme/licenses/cc_by_4.0.png'): URL path for the license image.
mail
-------------------------------------------------------------------------------
This is for configuring out-going email.
* `backend` (enum["mail", "sendmail", "smtp"], default 'mail'): The backend to use for
mail. We recommend SMTP where your setup supports it as it is of the three the more
difficult one for script exploits to abuse (relatively speaking - they all have
potential problems.).
* `params` (array, default null): If the mail backend requires any parameters, you can
provide them in this array.
* `domain_check` (boolean, default true): Check email origin is valid.
nickname
-------------------------------------------------------------------------------
* `blacklist` (array, default ['doc', 'main', 'avatar', 'theme']): an array of strings for
usernames that may not be registered. You may want to add others if you have other
software installed in a subdirectory of GNU social or if you just don't want certain
words used as usernames.
* `featured` (array, default null): an array of nicknames of 'featured' users of the site.
Can be useful to draw attention to well-known users, or interesting people, or
whatever.
profile
-------------------------------------------------------------------------------
* `banned` (array, defualt []): array of users to hell-ban
* `bio_text_limit` (integer, default null): Max character length of bio; 0 means no
limit; null means to use the site text limit default.
* `allow_nick_change` (boolean, default false): Whether to allow users to change their
nickname.
* `allow_private_stream` (boolean, default true): Whether users can set their streams to
private, so only followers can see it.
* `backup` (boolean, default false): Whether users can backup their own profiles. Can
cause DoS.
* `restore` (boolean, default false): Whether users can restore their profiles from backup
files. Can cause DoS.
* `delete` (boolean, default false): Whether users can delete their own accounts.
* `move` (boolean, default false): Whether users can move their accounts to another
server.
image
-------------------------------------------------------------------------------
* `jpegquality` {integer, default 85}: default quality to use when reencoding images as
jpeg.
theme_upload
-------------------------------------------------------------------------------
* `enabled` (boolean, default true): Whether to allow users to upload themes
* `formats` (array, default ['zip', 'tar', 'gz', 'tar.gz']): Formats to allow
foaf
-------------------------------------------------------------------------------
* `mbox_sha1sum` (boolean, default false): whether to include this box in the FOAF
protocol page
public
-------------------------------------------------------------------------------
For configuring the public stream.
* `local_only` (boolean, default false): If set to true, only messages posted by users of
this instance (rather than remote instances) are shown in the public stream.
* `blacklist` (array, default []): An array of IDs of users to hide from the public
stream. Useful if you have someone making an excessive amount of posts to the site or
some kind of automated poster, testing bots, etc.
* `exclude_sources` (array, default []): Sources of notices that should be kept off of
the public feed (because they're from automatic posters, for instance).
throttle
-------------------------------------------------------------------------------
For notice-posting throttles.
* `enabled` (boolean, default true): Whether to throttle posting.
* `count` (integer, default 20): Each user can make this many posts in 'timespan' seconds.
So, if count is 100 and timespan is 3600, then there can be only 100 posts from a user
every hour.
* `timespan` (integer, default 600): See 'count'.
invite
-------------------------------------------------------------------------------
* `enabled` (boolean, default true): Whether to allow users to send invites.
tag
-------------------------------------------------------------------------------
* `dropoff` (integer, default 86400 * 10): Exponential decay factor for tag listing, in
seconds. You can twiddle with this to try to get better results for your site.
* `cutoff` (integer, default 86400 * 90): Cutoff, in seconds, before which to not look for
notices.
popular
-------------------------------------------------------------------------------
* `dropoff` (integer, default 86400 * 10): Exponential decay factor for popular notices, in
seconds. You can twiddle with this to try to get better results for your site.
* `cutoff` (integer, default 86400 * 90): Cutoff, in seconds, before which to not look for
notices.
daemon
-------------------------------------------------------------------------------
* `piddir` (string, default `sys_get_temp_dir()`): Directory that daemon processes should
write their PID file (process ID) to.
* `user` (string|integer, default false): If set, the daemons will try to change their
effective user ID to this user before running. Probably a good idea, especially if you
start the daemons as root.
* `group` (string|integer, default false): If set, the daemons will try to change their
effective group ID to this named group.
ping
-------------------------------------------------------------------------------
Using the "XML-RPC Ping" method initiated by weblogs.com, the site can
notify third-party servers of updates.
* `notify` (array, default []): An array of URLs for ping endpoints.
* `timeout` (integer, default 2): Interval in seconds between notifications.
new_users
-------------------------------------------------------------------------------
* `default_subscriptions` (array, default null): Nickname of user accounts to
automatically subscribe new users to. Typically this would be a system account for e.g.
service updates or announcements. Users are able to unsub if they want.
* `welcome_user` (string, default null): Nickname of a user account that sends welcome
messages to new users.
N.B. If either of these special user accounts are specified, the users should be created
before the configuration is updated.
linkify
-------------------------------------------------------------------------------
* `bare_domain` (boolean, default false): Prepend schema to any linked domains (a href,
not display text).
* `linkify_ipv4` (boolean, default false): Convert IPv4 addresses into hyperlinks.
* `linkify_ipv6` (boolean, default false): Convert IPv6 addresses into hyperlinks.
group
-------------------------------------------------------------------------------
* `max_aliases` (integer, default 3): Maximum number of aliases a group can have.
Set to 0 or less to prevent aliases in a group.
* `description_limit` (integer, default null): Maximum number of characters to allow in
group descriptions. null means to use the site-wide text limits. 0 means no limit.
people_tag
-------------------------------------------------------------------------------
* `max_tags` (integer, default 100): Maximum number of people tags a user can create.
* `max_people` (integer, default 500): Maximum number of people with the same user people tag.
* `allow_tagging` (associative array, default ['local' => true, 'remote' => true])>: Which
kind of user to allow tagging.
* `description_limit` (integer, default null): Maximum tag description lenght.
search
-------------------------------------------------------------------------------
* `type` (enum('fulltext', 'like'), default 'like'): type of search. Ignored if PostgreSQL
is enabled. Can either be 'fulltext' or 'like'. The former is faster and more
efficient but requires the lame old MyISAM engine for MySQL. The latter will work with
InnoDB but could be miserably slow on large systems.
html_filter
-------------------------------------------------------------------------------
* `tags` (array, default ['img', 'video', 'audio', 'script']): Remove tags from
user/remotely generated HTML.
notice
-------------------------------------------------------------------------------
* `content_limit` (integer, default null): Max length of the plain-text content of a
notice. Null means to use the site-wide text limit. 0 means no limit.
* `allow_private` (boolean, default false): Whether to allow users to post notices visible
only to their subscribers.
* `hide_banned` (boolean, default true): Whether to hide hell-banned users' notices.
message
-------------------------------------------------------------------------------
* `content_limit` (integer, default null): Max length of the plain-text content of a
message. Null means to use the site-wide text limit. 0 means no limit.
location
-------------------------------------------------------------------------------
* `share` (enum('always', 'user', 'never'), default 'user'): Whether to share user
location. 'user' means each user can choose.
admin
-------------------------------------------------------------------------------
* `panels` (array, default ['site', 'user', 'paths', 'access', 'sessions', 'sitenotice',
'license', 'plugins']): Which panels to include in the admin tab.
single_user
-------------------------------------------------------------------------------
If an installation has only one user, this can simplify a lot of the
interface. It also makes the user's profile the root URL.
* `enabled` (boolean, default value provided in configure): Whether to run in "single user mode".
* `nickname` (string, default null): nickname of the single user. If no nickname is
specified, the site owner account will be used (if present).
robots_txt
-------------------------------------------------------------------------------
* `crawl_delay` (integer, default 0): if non-zero, this value is provided as the
'Crawl-Delay:' for the robots.txt file. see
<https://en.wikipedia.org/wiki/Robots_exclusion_standard#Crawl-delay_directive> for
more information. Default is zero, no explicit delay.
* `disallow`(array, default ['main', 'settings', 'admin', 'search', 'message']): Array of
paths to disallow. Ignored when site is private, in which case the entire site ('/')
is disallowed.
nofollow
-------------------------------------------------------------------------------
We optionally put 'rel="nofollow"' on some links in some pages. The following
configuration settings let you fine-tune how or when things are nofollowed. See
http://en.wikipedia.org/wiki/Nofollow for more information on what 'nofollow' means.
* `subscribers` (boolean, default true): Whether to nofollow links to subscribers on the
profile and personal pages.
* `members` (boolean, default true): Whether to nofollow links to members on the group
page. Default true.
* `peopletag` (boolean, default true): Whether to nofollow links to people listed in the
peopletag page. Default true.
* `external` (enum('always', 'sometimes', 'never'), default 'sometimes'): External links
in notices. One of three values: 'always', 'sometimes', 'never'. If 'sometimes', then
external links are not nofollowed on profile, notice, and favorites page. Default is
'sometimes'.
url_shortener
-------------------------------------------------------------------------------
* `service` (string, default 'internal'): URL shortening service to use by default. Users
can override individually.
* `max_url_length` (integer, default 100): If an URL is strictly longer than this limit,
it will be shortened. Note that the URL shortener service may return an URL longer
than this limit. Users can override. If set to 0, all URLs will be shortened.
* `max_notice_length` (integer, default null): If a notice is strictly longer than this
limit, all URLs in the notice will be shortened. Users can override this.
http
-------------------------------------------------------------------------------
* `ssl_cafile` (string, default '/docker/certbot/files/live/'): location of the CA file
for SSL connections. If not set, peers won't be able to verify our identity.
* `timeout` (integer, default `ini_get('default_socket_timeout')`): Timeout in seconds
when to close a connection.
* `proxy_host` (string, default null): Host to use for proxying HTTP requests. If null,
doesn't use an HTTP proxy.
* `proxy_port` (integer, default null): Port to use to connect to HTTP proxy host.
* `proxy_user` (string, default null): Username to use for authenticating to the HTTP proxy.
* `proxy_password` (string, default null): Password to use for authenticating to the HTTP proxy.
* `proxy_auth_scheme` (TODO): Scheme to use for authenticating to the HTTP proxy.
discovery
-------------------------------------------------------------------------------
* `CORS` (boolean, default false): Whether to allow Cross-Origin Resource Sharing for
service discovery (host-meta, XRD, etc.)
performance
-------------------------------------------------------------------------------
* `high` (boolean, default fakse): Disables some high-performance-intensity components.
login_command
-------------------------------------------------------------------------------
* `enabled` (boolean, default false): Whether to enable users to send the text 'login' to
the site through any channel and receive a link to login to the site automatically in
return. Possibly useful for users who primarily use an XMPP or SMS interface. Note
that the security implications of this are pretty serious. You should enable it only
after you've convinced yourself that it is safe.

View File

@ -1 +0,0 @@
../../INSTALL.md

1499
EVENTS.txt Normal file

File diff suppressed because it is too large Load Diff

466
INSTALL Normal file
View File

@ -0,0 +1,466 @@
TABLE OF CONTENTS
=================
* Prerequisites
- PHP modules
- Better performance
* Installation
- Getting it up and running
- Fancy URLs
- Themes
- Private
* Extra features
- Sphinx
- SMS
- Translation
- Queues and daemons
* After installation
- Backups
- Upgrading
Prerequisites
=============
PHP modules
-----------
The following software packages are *required* for this software to
run correctly.
- PHP 5.5+ For newer versions, some functions that are used may be
disabled by default, such as the pcntl_* family. See the
section on 'Queues and daemons' for more information.
- MariaDB 5+ GNU Social uses, by default, a MariaDB server for data
storage. Versions 5.x and 10.x have both reportedly
worked well. It is also possible to run MySQL 5.5+.
- Web server Apache, lighttpd and nginx will all work. CGI mode is
recommended and also some variant of 'suexec' (or a
proper setup php-fpm pool)
NOTE: mod_rewrite or its equivalent is extremely useful.
Your PHP installation must include the following PHP extensions for a
functional setup of GNU Social:
- openssl (compiled in for Debian, enabled manually in Arch Linux)
- php5-curl Fetching files by HTTP.
- php5-gd Image manipulation (scaling).
- php5-gmp For Salmon signatures (part of OStatus).
- php5-intl Internationalization support (transliteration et al).
- php5-json For WebFinger lookups and more.
- php5-mysqlnd The native driver for PHP5 MariaDB connections. If you
use MySQL, 'php5-mysql' or 'php5-mysqli' may be enough.
The above package names are for Debian based systems. In the case of
Arch Linux, PHP is compiled with support for most extensions but they
require manual enabling in the relevant php.ini file (mostly php5-gmp).
Better performance
------------------
For some functionality, you will also need the following extensions:
- opcache Improves performance a _lot_. Included in PHP, must be
enabled manually in php.ini for most distributions. Find
and set at least: opcache.enable=1
- mailparse Efficient parsing of email requires this extension.
Submission by email or SMS-over-email uses this.
- sphinx A client for the sphinx server, an alternative to MySQL
or Postgresql fulltext search. You will also need a
Sphinx server to serve the search queries.
- gettext For multiple languages. Default on many PHP installs;
will be emulated if not present.
- exif For thumbnails to be properly oriented.
You may also experience better performance from your site if you configure
a PHP cache/accelerator. Most distributions come with "opcache" support.
Enable it in your php.ini where it is documented together with its settings.
Installation
============
Getting it up and running
-------------------------
Installing the basic GNU Social web component is relatively easy,
especially if you've previously installed PHP/MariaDB packages.
1. Unpack the tarball you downloaded on your Web server. Usually a
command like this will work:
tar zxf gnusocial-*.tar.gz
...which will make a gnusocial-x.y.z subdirectory in your current
directory. (If you don't have shell access on your Web server, you
may have to unpack the tarball on your local computer and FTP the
files to the server.)
2. Move the tarball to a directory of your choosing in your Web root
directory. Usually something like this will work:
mv gnusocial-x.y.z /var/www/gnusocial
This will often make your GNU Social instance available in the gnusocial
path of your server, like "http://example.net/gnusocial". "social" or
"blog" might also be good path names. If you know how to configure
virtual hosts on your web server, you can try setting up
"http://social.example.net/" or the like.
If you have "rewrite" support on your webserver, and you should,
then please enable this in order to make full use of your site. This
will enable "Fancy URL" support, which you can read more about if you
scroll down a bit in this document.
3. Make your target directory writeable by the Web server, please note
however that 'a+w' will give _all_ users write access and securing the
webserver is not within the scope of this document.
chmod a+w /var/www/gnusocial/
On some systems, this will work as a more secure alternative:
chgrp www-data /var/www/gnusocial/
chmod g+w /var/www/gnusocial/
If your Web server runs as another user besides "www-data", try
that user's default group instead. As a last resort, you can create
a new group like "gnusocial" and add the Web server's user to the group.
4. You should also take this moment to make your 'avatar' and 'file' sub-
directories writeable by the Web server. The _insecure_ way to do
this is:
chmod a+w /var/www/gnusocial/avatar
chmod a+w /var/www/gnusocial/file
You can also make the avatar, and file directories just writable by
the Web server group, as noted above.
5. Create a database to hold your site data. Something like this
should work (you will be prompted for your database password):
mysqladmin -u "root" -p create social
Note that GNU Social should have its own database; you should not share
the database with another program. You can name it whatever you want,
though.
(If you don't have shell access to your server, you may need to use
a tool like phpMyAdmin to create a database. Check your hosting
service's documentation for how to create a new MariaDB database.)
6. Create a new database account that GNU Social will use to access the
database. If you have shell access, this will probably work from the
MariaDB shell:
GRANT ALL on social.*
TO 'social'@'localhost'
IDENTIFIED BY 'agoodpassword';
You should change the user identifier 'social' and 'agoodpassword'
to your preferred new database username and password. You may want to
test logging in to MariaDB as this new user.
7. In a browser, navigate to the GNU Social install script; something like:
https://social.example.net/install.php
Enter the database connection information and your site name. The
install program will configure your site and install the initial,
almost-empty database.
8. You should now be able to navigate to your social site's main directory
and see the "Public Timeline", which will probably be empty. You can
now register new user, post some notices, edit your profile, etc.
Fancy URLs
----------
By default, GNU Social will use URLs that include the main PHP program's
name in them. For example, a user's home profile might be found at either
of these URLS depending on the webserver's configuration and capabilities:
https://social.example.net/index.php/fred
https://social.example.net/index.php?p=fred
It's possible to configure the software to use fancy URLs so it looks like
this instead:
https://social.example.net/fred
These "fancy URLs" are more readable and memorable for users. To use
fancy URLs, you must either have Apache 2.x with .htaccess enabled and
mod_rewrite enabled, -OR- know how to configure "url redirection" in
your server (like lighttpd or nginx).
1. See the instructions for each respective webserver software:
* For Apache, inspect the "htaccess.sample" file and save it as
".htaccess" after making any necessary modifications. Our sample
file is well commented.
* For lighttpd, inspect the lighttpd.conf.example file and apply the
appropriate changes in your virtualhost configuration for lighttpd.
* For nginx, inspect the nginx.conf.sample file and apply the appropriate
changes.
* For other webservers, we gladly accept contributions of
server configuration examples.
2. Assuming your webserver is properly configured and have its settings
applied (remember to reload/restart it), you can add this to your
GNU social's config.php file:
$config['site']['fancy'] = true;
You should now be able to navigate to a "fancy" URL on your server,
like:
https://social.example.net/main/register
Themes
------
As of right now, your ability change the theme is limited to CSS
stylesheets and some image files; you can't change the HTML output,
like adding or removing menu items, without the help of a plugin.
You can choose a theme using the $config['site']['theme'] element in
the config.php file. See below for details.
You can add your own theme by making a sub-directory of the 'theme'
subdirectory with the name of your theme. Each theme can have the
following files:
display.css: a CSS2 file for "default" styling for all browsers.
logo.png: a logo image for the site.
default-avatar-profile.png: a 96x96 pixel image to use as the avatar for
users who don't upload their own.
default-avatar-stream.png: Ditto, but 48x48. For streams of notices.
default-avatar-mini.png: Ditto ditto, but 24x24. For subscriptions
listing on profile pages.
You may want to start by copying the files from the default theme to
your own directory.
Private
-------
A GNU social node can be configured as "private", which means it will not
federate with other nodes in the network. It is not a recommended method
of using GNU social and we cannot at the current state of development
guarantee that there are no leaks (what a public network sees as features,
private sites will likely see as bugs).
Private nodes are however an easy way to easily setup collaboration and
image sharing within a workgroup or a smaller community where federation
is not a desired feature. Also, it is possible to change this setting and
instantly gain full federation features.
Access to file attachments can also be restricted to logged-in users only:
1. Add a directory outside the web root where your file uploads will be
stored. Use this command as an initial guideline to create it:
mkdir /var/www/gnusocial-files
2. Make the file uploads directory writeable by the web server. An
insecure way to do this is (to do it properly, read up on UNIX file
permissions and configure your webserver accordingly):
chmod a+x /var/www/gnusocial-files
3. Tell GNU social to use this directory for file uploads. Add a line
like this to your config.php:
$config['attachments']['dir'] = '/var/www/gnusocial-files';
Extra features
==============
Sphinx
------
To use a Sphinx server to search users and notices, you'll need to
enable the SphinxSearch plugin. Add to your config.php:
addPlugin('SphinxSearch');
$config['sphinx']['server'] = 'searchhost.local';
You also need to install, compile and enable the sphinx pecl extension for
php on the client side, which itself depends on the sphinx development files.
See plugins/SphinxSearch/README for more details and server setup.
SMS
---
StatusNet supports a cheap-and-dirty system for sending update messages
to mobile phones and for receiving updates from the mobile. Instead of
sending through the SMS network itself, which is costly and requires
buy-in from the wireless carriers, it simply piggybacks on the email
gateways that many carriers provide to their customers. So, SMS
configuration is essentially email configuration.
Each user sends to a made-up email address, which they keep a secret.
Incoming email that is "From" the user's SMS email address, and "To"
the users' secret email address on the site's domain, will be
converted to a notice and stored in the DB.
For this to work, there *must* be a domain or sub-domain for which all
(or most) incoming email can pass through the incoming mail filter.
1. Run the SQL script carrier.sql in your StatusNet database. This will
usually work:
mysql -u "statusnetuser" --password="statusnetpassword" statusnet < db/carrier.sql
This will populate your database with a list of wireless carriers
that support email SMS gateways.
2. Make sure the maildaemon.php file is executable:
chmod +x scripts/maildaemon.php
Note that "daemon" is kind of a misnomer here; the script is more
of a filter than a daemon.
2. Edit /etc/aliases on your mail server and add the following line:
*: /path/to/statusnet/scripts/maildaemon.php
3. Run whatever code you need to to update your aliases database. For
many mail servers (Postfix, Exim, Sendmail), this should work:
newaliases
You may need to restart your mail server for the new database to
take effect.
4. Set the following in your config.php file:
$config['mail']['domain'] = 'yourdomain.example.net';
Translations
------------
For info on helping with translations, see the platform currently in use
for translations: https://www.transifex.com/projects/p/gnu-social/
Translations use the gettext system <http://www.gnu.org/software/gettext/>.
If you for some reason do not wish to sign up to the Transifex service,
you can review the files in the "locale/" sub-directory of GNU social.
Each plugin also has its own translation files.
To get your own site to use all the translated languages, and you are
tracking the git repo, you will need to install at least 'gettext' on
your system and then run:
$ make translations
Queues and daemons
------------------
Some activities that StatusNet needs to do, like broadcast OStatus, SMS,
XMPP messages and TwitterBridge operations, can be 'queued' and done by
off-line bots instead.
Two mechanisms are available to achieve offline operations:
* New embedded OpportunisticQM plugin, which is enabled by default
* Legacy queuedaemon script, which can be enabled via config file.
### OpportunisticQM plugin
This plugin is enabled by default. It tries its best to do background
jobs during regular HTTP requests, like API or HTML pages calls.
Since queueing system is enabled by default, notices to be broadcasted
will be stored, by default, into DB (table queue_item).
Whenever it has time, OpportunisticQM will try to handle some of them.
This is a good solution whether you:
* have no access to command line (shared hosting)
* do not want to deal with long-running PHP processes
* run a low traffic GNU social instance
In other case, you really should consider enabling the queuedaemon for
performance reasons. Background daemons are necessary anyway if you wish
to use the Instant Messaging features such as communicating via XMPP.
### queuedaemon
If you want to use legacy queuedaemon, you must be able to run
long-running offline processes, either on your main Web server or on
another server you control. (Your other server will still need all the
above prerequisites, with the exception of Apache.) Installing on a
separate server is probably a good idea for high-volume sites.
1. You'll need the "CLI" (command-line interface) version of PHP
installed on whatever server you use.
Modern PHP versions in some operating systems have disabled functions
related to forking, which is required for daemons to operate. To make
this work, make sure that your php-cli config (/etc/php5/cli/php.ini)
does NOT have these functions listed under 'disable_functions':
* pcntl_fork, pcntl_wait, pcntl_wifexited, pcntl_wexitstatus,
pcntl_wifsignaled, pcntl_wtermsig
Other recommended settings for optimal performance are:
* mysqli.allow_persistent = On
* mysqli.reconnect = On
2. If you're using a separate server for queues, install StatusNet
somewhere on the server. You don't need to worry about the
.htaccess file, but make sure that your config.php file is close
to, or identical to, your Web server's version.
3. In your config.php files (on the server where you run the queue
daemon), set the following variable:
$config['queue']['daemon'] = true;
You may also want to look at the 'Queues and Daemons' section in
this file for more background processing options.
4. On the queues server, run the command scripts/startdaemons.sh.
This will run the queue handlers:
* queuedaemon.php - polls for queued items for inbox processing and
pushing out to OStatus, SMS, XMPP, etc.
* imdaemon.php - if an IM plugin is enabled (like XMPP)
* other daemons, like TwitterBridge ones, that you may have enabled
These daemons will automatically restart in most cases of failure
including memory leaks (if a memory_limit is set), but may still die
or behave oddly if they lose connections to the XMPP or queue servers.
It may be a good idea to use a daemon-monitoring service, like 'monit',
to check their status and keep them running.
All the daemons write their process IDs (pids) to /var/run/ by
default. This can be useful for starting, stopping, and monitoring the
daemons. If you are running multiple sites on the same machine, it will
be necessary to avoid collisions of these PID files by setting a site-
specific directory in config.php:
$config['daemon']['piddir'] = __DIR__ . '/../run/';
It is also possible to use a STOMP server instead of our kind of hacky
home-grown DB-based queue solution. This is strongly recommended for
best response time, especially when using XMPP.
After installation
==================
Backups
-------
There is no built-in system for doing backups in GNU social. You can make
backups of a working StatusNet system by backing up the database and
the Web directory. To backup the database use mysqldump <https://mariadb.com/kb/en/mariadb/mysqldump/>
and to backup the Web directory, try tar.
Upgrading
---------
Upgrading is strongly recommended to stay up to date with security fixes
and new features. For instructions on how to upgrade GNU social code,
please see the UPGRADE file.

View File

@ -1,5 +0,0 @@
GNU social
=====
GNU social is a federated social network. For documentation, visit
https://docs.gnusocial.rocks/ or view the files under docs/

129
Makefile
View File

@ -1,125 +1,18 @@
# Warning: do not transform tabs to spaces in this file.
DIR=$(strip $(notdir $(CURDIR))) # Seems a bit hack-ish, but `basename` works differently
all : translations
translate-container-name = $$(if docker container inspect $(1) > /dev/null 2>&1; then echo $(1); else echo $(1) | sed 'y/_/-/' ; fi)
args = `arg="$(filter-out $@,$(MAKECMDGOALS))" && echo $${arg:-${1}}`
core_mo = $(patsubst %.po,%.mo,$(wildcard locale/*/LC_MESSAGES/statusnet.po))
plugin_mo = $(patsubst %.po,%.mo,$(wildcard plugins/*/locale/*/LC_MESSAGES/*.po))
%:
@:
translations : $(core_mo) $(plugin_mo)
.PHONY:
@if ! docker info > /dev/null; then echo "Docker does not seem to be running"; exit 1; fi
clean :
rm -f $(core_mo) $(plugin_mo)
up: .PHONY
docker-compose up -d
updatepo :
php scripts/update_po_templates.php --all
down: .PHONY
docker-compose down
%.mo : %.po
msgfmt -o $@ $<
redis-shell:
docker exec -it $(call translate-container-name,$(strip $(DIR))_redis_1) sh -c 'redis-cli'
php-repl: .PHONY
docker exec -it $(call translate-container-name,$(strip $(DIR))_php_1) sh -c '/var/www/social/bin/console psysh'
php-shell: .PHONY
docker exec -it $(call translate-container-name,$(strip $(DIR))_php_1) sh -c 'cd /var/www/social; sh'
psql-shell: .PHONY
docker exec -it $(call translate-container-name,$(strip $(DIR))_db_1) sh -c "psql -U postgres social"
database-force-nuke:
docker stop $(call translate-container-name,$(strip $(DIR))_worker_1) \
&& docker exec -it $(call translate-container-name,$(strip $(DIR))_php_1) sh -c "cd /var/www/social; bin/console doctrine:database:drop --force && bin/console doctrine:database:create && bin/console doctrine:schema:update --dump-sql --force && bin/console app:populate_initial_values" \
&& docker-compose up -d
database-force-schema-update:
docker exec -it $(call translate-container-name,$(strip $(DIR))_php_1) sh -c "/var/www/social/bin/console doctrine:schema:update --dump-sql --force"
tooling-docker-up: .PHONY
@sh -c 'if [ ! docker container inspect $(call translate-container-name,tooling_php_1) > /dev/null 2>&1 ]; then cd docker/tooling && docker-compose up -d --build > /dev/null 2>&1; fi'
tooling-docker-down: .PHONY
cd docker/tooling && docker-compose down
test: tooling-docker-up
@docker exec $(call translate-container-name,tooling_php_1) /var/tooling/coverage.sh $(call args,'')
test-database-force-nuke: tooling-docker-up
docker exec -it $(call translate-container-name,tooling_php_1) sh -c 'cd /var/www/social; bin/console doctrine:database:drop --force'
tooling-php-shell: tooling-docker-up
docker exec -it $(call translate-container-name,tooling_php_1) sh
test-accesibility: tooling-docker-up
cd docker/tooling && docker-compose run pa11y /accessibility.sh
cs-fixer: tooling-docker-up
@bin/php-cs-fixer $${CS_FIXER_FILE}
doc-check: tooling-docker-up
bin/php-doc-check
phpstan: tooling-docker-up
bin/phpstan
remove-var:
rm -rf var/*
remove-file:
sudo rm -rf file/*
flush-redis-cache:
docker exec -it $(call translate-container-name,$(strip $(DIR))_redis_1) sh -c 'redis-cli flushall'
install-plugins:
docker exec -it $(call translate-container-name,$(strip $(DIR))_php_1) /var/www/social/bin/install_plugins.sh
update-dependencies:
docker exec -it $(call translate-container-name,$(strip $(DIR))_php_1) sh -c 'cd /var/www/social && composer update'
update-autocode:
docker exec -it $(call translate-container-name,$(strip $(DIR))_php_1) sh -c 'cd /var/www/social && bin/update_autocode'
backup-actors:
docker exec -it $(call translate-container-name,$(strip $(DIR))_db_1) \
sh -c 'su postgres -c "mkdir -p /tmp/backup"' && \
docker exec -it $(call translate-container-name,$(strip $(DIR))_php_1) \
sh -c "cd /var/www/social && bin/console doctrine:query:sql \"\
copy actor to '/tmp/backup/actor.csv';\
copy local_user to '/tmp/backup/local_user.csv';\
copy local_group to '/tmp/backup/local_group.csv';\
\
copy activitypub_actor to '/tmp/backup/ap_actor.csv';\
copy activitypub_rsa to '/tmp/backup/ap_rsa.csv';\
\
copy actor_subscription to '/tmp/backup/actor_subscription.csv';\
copy group_member to '/tmp/backup/group_member.csv';\
\
copy feed to '/tmp/backup/feed.csv';\
copy (SELECT 'ALTER SEQUENCE ' || c.relname || ' RESTART WITH ' || nextval(c.relname::regclass) || ';'\
FROM pg_class c WHERE c.relkind = 'S') to '/tmp/backup/sequences';\"" && \
mkdir -p /tmp/social-sql-backup && \
docker cp $(call translate-container-name,$(strip $(DIR))_db_1):/tmp/backup/. /tmp/social-sql-backup
restore-actors:
docker cp /tmp/social-sql-backup/. $(call translate-container-name,$(strip $(DIR))_db_1):/tmp/backup
docker exec -it $(call translate-container-name,$(strip $(DIR))_db_1) sh -c 'chown postgres /tmp/backup' && \
docker exec -it $(call translate-container-name,$(strip $(DIR))_php_1) \
sh -c "cd /var/www/social && bin/console doctrine:query:sql \"\
copy actor from '/tmp/backup/actor.csv';\
copy local_user from '/tmp/backup/local_user.csv';\
copy local_group from '/tmp/backup/local_group.csv';\
\
copy activitypub_actor from '/tmp/backup/ap_actor.csv';\
copy activitypub_rsa from '/tmp/backup/ap_rsa.csv';\
\
copy actor_subscription from '/tmp/backup/actor_subscription.csv';\
copy group_member from '/tmp/backup/group_member.csv';\
\
copy feed from '/tmp/backup/feed.csv';\
`cat /tmp/social-sql-backup/sequences`\""
force-nuke-everything: down remove-var remove-file up flush-redis-cache database-force-nuke install-plugins
force-delete-content: backup-actors force-nuke-everything restore-actors

44
PLUGINS.txt Normal file
View File

@ -0,0 +1,44 @@
Plugins
=======
GNU social supports a simple but
powerful plugin architecture. Important events in the code are named,
like 'StartNoticeSave', and other software can register interest
in those events. When the events happen, the other software is called
and has a choice of accepting or rejecting the events.
In the simplest case, you can add a function to config.php and use the
Event::addHandler() function to hook an event:
function AddGoogleLink($action)
{
$action->menuItem('http://www.google.com/', _('Google'), _('Search engine'));
return true;
}
Event::addHandler('EndPrimaryNav', 'AddGoogleLink');
This adds a menu item to the end of the main navigation menu. You can
see the list of existing events, and parameters that handlers must
implement, in EVENTS.txt.
The Plugin class in lib/plugin.php makes it easier to write more
complex plugins. Sub-classes can just create methods named
'onEventName', where 'EventName' is the name of the event (case
matters!). These methods will be automatically registered as event
handlers by the Plugin constructor (which you must call from your own
class's constructor).
Several example plugins are included in the plugins/ directory. You
can enable a plugin with the following line in config.php:
addPlugin('Example', array('param1' => 'value1',
'param2' => 'value2'));
This will look for and load files named 'ExamplePlugin.php' or
'Example/ExamplePlugin.php' either in the plugins/ directory (for
plugins that ship with GNU social) or in the local/ directory (for
plugins you write yourself or that you get from somewhere else) or
local/plugins/.
Plugins are documented in their own directories.

251
README.md Normal file
View File

@ -0,0 +1,251 @@
# GNU social 1.2.x
2015
(c) Free Software Foundation, Inc
(c) StatusNet, Inc
This is the README file for GNU social, the free
software social networking platform. It includes
general information about the software and the
project.
Some other files to review:
- INSTALL: instructions on how to install the software.
- UPGRADE: upgrading from earlier versions
- CONFIGURE: configuration options in gruesome detail.
- PLUGINS.txt: how to install and configure plugins.
- EVENTS.txt: events supported by the plugin system
- COPYING: full text of the software license
Information on using GNU social can be found in
the "doc" subdirectory or in the "help" section
on-line, or you can catch us on IRC in #social on
the freenode network.
## About
GNU social is a free social networking
platform. It helps people in a community, company
or group to exchange short status updates, do
polls, announce events, or other social activities
(and you can add more!). Users can choose which
people to "follow" and receive only their friends'
or colleagues' status messages. It provides a
similar service to sites like Twitter, Google+ or
Facebook, but is much more awesome.
With a little work, status messages can be sent to
mobile phones, instant messenger programs (using
XMPP), and specially-designed desktop clients that
support the Twitter API.
GNU social supports an open standard called
OStatus <https://www.w3.org/community/ostatus/> that lets users in
different networks follow each other. It enables a
distributed social network spread all across the
Web.
GNU social was originally developed as "StatusNet" by
StatusNet, Inc. with Evan Prodromou as lead developer.
It is shared with you in hope that you too make an
service available to your users. To learn more,
please see the Open Software Service Definition
1.1: <http://www.opendefinition.org/ossd>
### License
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public
License along with this program, in the file "COPYING". If not, see
<http://www.gnu.org/licenses/>.
IMPORTANT NOTE: The GNU Affero General Public License (AGPL) has
*different requirements* from the "regular" GPL. In particular, if
you make modifications to the GNU social source code on your server,
you *MUST MAKE AVAILABLE* the modified version of the source code
to your users under the same license. This is a legal requirement
of using the software, and if you do not wish to share your
modifications, *YOU MAY NOT INSTALL GNU SOCIAL*.
Documentation in the /doc-src/ directory is available under the
Creative Commons Attribution 3.0 Unported license, with attribution to
"GNU social". See <http://creativecommons.org/licenses/by/3.0/> for details.
CSS and images in the /theme/ directory are available under the
Creative Commons Attribution 3.0 Unported license, with attribution to
"GNU social". See <http://creativecommons.org/licenses/by/3.0/> for details.
Our understanding and intention is that if you add your own theme that
uses only CSS and images, those files are not subject to the copyleft
requirements of the Affero General Public License 3.0. See
<http://wordpress.org/news/2009/07/themes-are-gpl-too/>. This is not
legal advice; consult your lawyer.
Additional library software has been made available in the 'extlib'
directory. All of it is Free Software and can be distributed under
liberal terms, but those terms may differ in detail from the AGPL's
particulars. See each package's license file in the extlib directory
for additional terms.
## New this version
This is the development branch for the 1.2.x version of GNU social.
All daring 1.1.x admins should upgrade to this version.
So far it includes the following changes:
- Backing up a user's account is more and more complete.
- Emojis 😸 (utf8mb4 support)
The last release, 1.1.3, gave us these improvements:
- XSS security fix (thanks Simon Waters, <https://www.surevine.com/>)
- Many improvements to ease adoption of the Qvitter front-end <https://github.com/hannesmannerheim/qvitter>
- Protocol adaptions for improved performance and stability
Upgrades from _StatusNet_ 1.1.1 will also experience these improvements:
- Fixes for SQL injection errors in profile lists.
- Improved ActivityStreams JSON representation of activities and objects.
- Upgrade to the Twitter 1.1 API.
- More robust handling of errors in distribution.
- Fix error in OStatus subscription for remote groups.
- Fix error in XMPP distribution.
- Tracking of conversation URI metadata (more coherent convos)
### Troubleshooting
The primary output for GNU social is syslog,
unless you configured a separate logfile. This is
probably the first place to look if you're getting
weird behaviour from GNU social.
If you're tracking the unstable version of
GNU social in the git repository (see below), and you
get a compilation error ("unexpected T_STRING") in
the browser, check to see that you don't have any
conflicts in your code.
### Unstable version
If you're adventurous or impatient, you may want
to install the development version of GNU social.
To get it, use the git version control tool
<http://git-scm.com/> like so:
git clone git@git.gnu.io:gnu/gnu-social.git
In the current phase of development it is probably
recommended to use git as a means to stay up to date
with the source code. You can choose between these
branches:
- 1.2.x "stable", few updates, well tested code
- master "testing", more updates, usually working well
- nightly "unstable", most updates, not always working
To keep it up-to-date, use 'git pull'. Watch for conflicts!
## Further information
There are several ways to get more information about GNU social.
* The #social IRC channel on freenode.net <https://www.freenode.net/>.
* The unofficial XMPP room linked to IRC on <xmpp:gnusocial@conference.bka.li>
* The GNU social website <https://gnu.io/social/>
* Following us on GNU social -- <https://quitter.se/gnusocial>
* GNU social has a bug tracker for any defects you may find, or ideas for
making things better. <https://git.gnu.io/gnu/gnu-social/issues/>
* Patches are welcome, preferrably to our repository on git.gnu.io. <https://git.gnu.io/gnu/gnu-social>
Credits
=======
The following is an incomplete list of developers
who've worked on GNU social, or its predecessors
StatusNet and Free Social. Apologies for any
oversight; please let mattl@gnu.org know if
anyone's been overlooked in error.
## Project Founders
* Matt Lee (GNU social)
* Evan Prodromou (StatusNet)
* Mikael Nordfeldth (Free Social)
Thanks to all of the StatusNet developers:
* Zach Copley, StatusNet, Inc.
* Earle Martin, StatusNet, Inc.
* Marie-Claude Doyon, designer, StatusNet, Inc.
* Sarven Capadisli, StatusNet, Inc.
* Robin Millette, StatusNet, Inc.
* Ciaran Gultnieks
* Michael Landers
* Ori Avtalion
* Garret Buell
* Mike Cochrane
* Matthew Gregg
* Florian Biree
* Erik Stambaugh
* 'drry'
* Gina Haeussge
* Tryggvi Björgvinsson
* Adrian Lang
* Ori Avtalion
* Meitar Moscovitz
* Ken Sheppardson (Trac server, man-about-town)
* Tiago 'gouki' Faria (i18n manager)
* Sean Murphy
* Leslie Michael Orchard
* Eric Helgeson
* Ken Sedgwick
* Brian Hendrickson
* Tobias Diekershoff
* Dan Moore
* Fil
* Jeff Mitchell
* Brenda Wallace
* Jeffery To
* Federico Marani
* mEDI
* Brett Taylor
* Brigitte Schuster
* Siebrand Mazeland and the amazing volunteer translators at translatewiki.net
* Brion Vibber, StatusNet, Inc.
* James Walker, StatusNet, Inc.
* Samantha Doherty, designer, StatusNet, Inc.
* Simon Waters, Surevine
* Joshua Judson Rosen (rozzin)
### Extra special thanks to the GNU socialites
* Craig Andrews
* Donald Robertson
* Deb Nicholson
* Ian Denhart
* Steven DuBois
* Blaine Cook
* Henry Story
* Melvin Carvalho
Thanks also to the developers of our upstream
library code and to the thousands of people who
have tried out GNU social, told their friends, and
built the fediverse network to what it is today.
### License help from
* Bradley M. Kuhn

12
TODO.SOCIAL Normal file
View File

@ -0,0 +1,12 @@
Things to be done
=================
* Create a theme for GNU social
* Create a set of plugins to give StatusNet a more social-network UI
* Work on improvements for annoying things in StatusNet (ie. no
redirect to login page when you need to be logged in, etc)
* Work on adding further Activities, such as sharing photos/video,
events, UI for managing relationships.

97
UPGRADE Normal file
View File

@ -0,0 +1,97 @@
Upgrading
=========
GNU social 1.1.x to GNU social 1.2.x
------------------------------------
If you are tracking the GNU social git repository, we currently recommend
using the "master" branch (or nightly if you want to use latest features)
and follow this procedure:
0. Backup your data. The StatusNet upgrade discussions below have some
guidelines to back up the database and files (mysqldump and rsync).
1. Stop your queue daemons (you can run this command even if you do not
use the queue daemons):
$ bash scripts/stopdaemons.sh
2. Run the command to fetch the latest sourcecode:
$ git pull
If you are not using git we recommend following the instructions below
for upgrading "StatusNet 1.1.x to GNU social 1.2.x" as they are similar.
3. Run the upgrade script:
$ php scripts/upgrade.php
The upgrade script will likely take a long time because it will
upgrade the tables to another character encoding and make other
automated upgrades. Make sure it ends without errors. If you get
errors, create a new task on https://git.gnu.io/gnu/gnu-social/issues
4. Start your queue daemons again (you can run this command even if you
do not use the queue daemons):
$ bash scripts/startdaemons.sh
5. Report any issues at https://git.gnu.io/gnu/gnu-social/issues
If you are using ssh keys to log in to your server, you can make this
procedure pretty painless (assuming you have automated backups already).
Make sure you "cd" into the correct directory (in this case "htdocs")
and use the correct login@hostname combo:
$ ssh social@domain.example 'cd htdocs
&& bash scripts/stopdaemons.sh
&& git pull
&& time php scripts/upgrade.php
&& bash scripts/startdaemons.sh'
StatusNet 1.1.x to GNU social 1.2.x
-----------------------------------
We cannot support migrating from any other version of StatusNet than
1.1.1. If you are running a StatusNet version lower than this, please
follow the upgrade procedures for each respective StatusNet version.
You are now running StatusNet 1.1.1 and want to migrate to GNU social
1.2.x. Beware there may be changes in minimum required version of PHP
and the modules required, so review the INSTALL file (php5-intl is a
newly added dependency for example).
* Before you begin: Make backups. Always make backups. Of your entire
directory structure and the database too. All tables. All data. Alles.
0. Make a backup of everything. To backup the database, you can use a
variant of this command (you will be prompted for the database password):
$ mysqldump -u dbuser -p dbname > social-backup.sql
1. Stop your queue daemons 'bash scripts/stopdaemons.sh' should do it.
Not everyone runs queue daemons, but the above command won't hurt.
2. Unpack your GNU social code to a fresh directory. You can do this
by cloning our git repository:
$ git clone https://git.gnu.io/gnu/gnu-social.git gnusocial
3. Synchronize your local files to the GNU social directory. These
will be the local files such as avatars, config and files:
avatar/*
file/*
local/*
.htaccess
config.php
This command will point you in the right direction on how to do it:
$ rsync -avP statusnet/{.htaccess,avatar,file,local,config.php} gnusocial/
4. Replace your old StatusNet directory with the new GNU social
directory in your webserver root.
5. Run the upgrade script: 'php scripts/upgrade.php'
The upgrade script will likely take a long time because it will
upgrade the tables to another character encoding and make other
automated upgrades. Make sure it ends without errors. If you get
errors, create a new task on https://git.gnu.io/gnu/gnu-social/issues
6. Start your queue daemons: 'bash scripts/startdaemons.sh'
7. Report any issues at https://git.gnu.io/gnu/gnu-social/issues

View File

@ -0,0 +1,214 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Site access administration panel
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Settings
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Administer site access settings
*
* @category Admin
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class AccessadminpanelAction extends AdminPanelAction
{
/**
* Returns the page title
*
* @return string page title
*/
function title()
{
// TRANS: Page title for Access admin panel that allows configuring site access.
return _('Access');
}
/**
* Instructions for using this form.
*
* @return string instructions
*/
function getInstructions()
{
// TRANS: Page notice.
return _('Site access settings');
}
/**
* Show the site admin panel form
*
* @return void
*/
function showForm()
{
$form = new AccessAdminPanelForm($this);
$form->show();
return;
}
/**
* Save settings from the form
*
* @return void
*/
function saveSettings()
{
static $booleans = array('site' => array('private', 'inviteonly', 'closed'),
'public' => array('localonly'));
foreach ($booleans as $section => $parts) {
foreach ($parts as $setting) {
$values[$section][$setting] = ($this->boolean($setting)) ? 1 : 0;
}
}
$config = new Config();
$config->query('BEGIN');
foreach ($booleans as $section => $parts) {
foreach ($parts as $setting) {
Config::save($section, $setting, $values[$section][$setting]);
}
}
$config->query('COMMIT');
return;
}
}
class AccessAdminPanelForm extends AdminForm
{
/**
* ID of the form
*
* @return int ID of the form
*/
function id()
{
return 'form_site_admin_panel';
}
/**
* class of the form
*
* @return string class of the form
*/
function formClass()
{
return 'form_settings';
}
/**
* Action of the form
*
* @return string URL of the action
*/
function action()
{
return common_local_url('accessadminpanel');
}
/**
* Data elements of the form
*
* @return void
*/
function formData()
{
$this->out->elementStart('fieldset', array('id' => 'settings_admin_account_access'));
// TRANS: Form legend for registration form.
$this->out->element('legend', null, _('Registration'));
$this->out->elementStart('ul', 'form_data');
$this->li();
// TRANS: Checkbox instructions for admin setting "Invite only".
$instructions = _('Make registration invitation only.');
// TRANS: Checkbox label for configuring site as invite only.
$this->out->checkbox('inviteonly', _('Invite only'),
(bool) $this->value('inviteonly'),
$instructions);
$this->unli();
$this->li();
// TRANS: Checkbox instructions for admin setting "Closed" (no new registrations).
$instructions = _('Disable new registrations.');
// TRANS: Checkbox label for disabling new user registrations.
$this->out->checkbox('closed', _('Closed'),
(bool) $this->value('closed'),
$instructions);
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
// Public access settings (login requirements for feeds etc.)
$this->out->elementStart('fieldset', array('id' => 'settings_admin_public_access'));
// TRANS: Form legend for registration form.
$this->out->element('legend', null, _('Feed access'));
$this->out->elementStart('ul', 'form_data');
$this->li();
// TRANS: Checkbox instructions for admin setting "Private".
$instructions = _('Prohibit anonymous users (not logged in) from viewing site?');
// TRANS: Checkbox label for prohibiting anonymous users from viewing site.
$this->out->checkbox('private', _m('LABEL', 'Private'),
(bool) $this->value('private'),
$instructions);
$this->unli();
$this->li();
// TRANS: Description of the full network notice stream views..
$instructions = _('The full network view includes (public) remote notices which may be unrelated to local conversations.');
// TRANS: Checkbox label for hiding remote network posts if they have not been interacted with locally.
$this->out->checkbox('localonly', _('Restrict full network view to accounts'),
(bool) $this->value('localonly', 'public'),
$instructions);
$this->unli();
$this->out->elementEnd('ul');
$this->out->elementEnd('fieldset');
}
/**
* Action elements
*
* @return void
*/
function formActions()
{
// TRANS: Button title to save access settings in site admin panel.
$title = _('Save access settings.');
// TRANS: Button text to save access settings in site admin panel.
$this->out->submit('submit', _m('BUTTON', 'Save'), 'submit', null, $title);
}
}

161
actions/addpeopletag.php Normal file
View File

@ -0,0 +1,161 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008-2010, StatusNet, Inc.
*
* Action to add a people tag to a user.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5
*
* @category Action
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR . '/lib/togglepeopletag.php';
/**
*
* Action to tag a profile with a single tag.
*
* Takes parameters:
*
* - tagged: the ID of the profile being tagged
* - token: session token to prevent CSRF attacks
* - ajax: boolean; whether to return Ajax or full-browser results
* - peopletag_id: the ID of the tag being used
*
* Only works if the current user is logged in.
*
* @category Action
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
* @link http://status.net/
*/
class AddpeopletagAction extends Action
{
var $user;
var $tagged;
var $peopletag;
/**
* Check pre-requisites and instantiate attributes
*
* @param Array $args array of arguments (URL, GET, POST)
*
* @return boolean success flag
*/
function prepare($args)
{
parent::prepare($args);
// CSRF protection
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
// TRANS: Client error displayed when the session token does not match or is not given.
$this->clientError(_('There was a problem with your session token.'.
' Try again, please.'));
}
// Only for logged-in users
$this->user = common_current_user();
if (empty($this->user)) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user.
$this->clientError(_('Not logged in.'));
}
// Profile to subscribe to
$tagged_id = $this->arg('tagged');
$this->tagged = Profile::getKV('id', $tagged_id);
if (empty($this->tagged)) {
// TRANS: Client error displayed trying to perform an action related to a non-existing profile.
$this->clientError(_('No such profile.'));
}
$id = $this->arg('peopletag_id');
$this->peopletag = Profile_list::getKV('id', $id);
if (empty($this->peopletag)) {
// TRANS: Client error displayed trying to reference a non-existing list.
$this->clientError(_('No such list.'));
}
return true;
}
/**
* Handle request
*
* Does the tagging and returns results.
*
* @param Array $args unused.
*
* @return void
*/
function handle($args)
{
// Throws exception on error
$ptag = Profile_tag::setTag($this->user->id, $this->tagged->id,
$this->peopletag->tag);
if (!$ptag) {
$user = User::getKV('id', $id);
if ($user) {
$this->clientError(
// TRANS: Client error displayed when an unknown error occurs when adding a user to a list.
// TRANS: %s is a username.
sprintf(_('There was an unexpected error while listing %s.'),
$user->nickname));
} else {
// TRANS: Client error displayed when an unknown error occurs when adding a user to a list.
// TRANS: %s is a profile URL.
$this->clientError(sprintf(_('There was a problem listing %s. ' .
'The remote server is probably not responding correctly. ' .
'Please try retrying later.'), $this->profile->profileurl));
}
}
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
// TRANS: Title after adding a user to a list.
$this->element('title', null, _m('TITLE','Listed'));
$this->elementEnd('head');
$this->elementStart('body');
$unsubscribe = new UntagButton($this, $this->tagged, $this->peopletag);
$unsubscribe->show();
$this->elementEnd('body');
$this->endHTML();
} else {
$url = common_local_url('subscriptions',
array('nickname' => $this->user->nickname));
common_redirect($url, 303);
}
}
}

188
actions/all.php Normal file
View File

@ -0,0 +1,188 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008-2011, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Actions
* @package Actions
* @author Adrian Lang <mail@adrianlang.de>
* @author Brenda Wallace <shiny@cpan.org>
* @author Brion Vibber <brion@pobox.com>
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Meitar Moscovitz <meitarm@gmail.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <millette@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org
* @license GNU Affero General Public License http://www.gnu.org/licenses/
* @link http://status.net
*/
if (!defined('GNUSOCIAL')) { exit(1); }
class AllAction extends ShowstreamAction
{
public function getStream()
{
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
$stream = new InboxNoticeStream($this->target, $this->scoped);
} else {
$stream = new ThreadingInboxNoticeStream($this->target, $this->scoped);
}
return $stream;
}
function title()
{
if (!empty($this->scoped) && $this->scoped->sameAs($this->target)) {
// TRANS: Title of a user's own start page.
return _('Home timeline');
} else {
// TRANS: Title of another user's start page.
// TRANS: %s is the other user's name.
return sprintf(_("%s's home timeline"), $this->target->getBestName());
}
}
function getFeeds()
{
return array(
new Feed(Feed::JSON,
common_local_url(
'ApiTimelineFriends', array(
'format' => 'as',
'id' => $this->target->getNickname()
)
),
// TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (Activity Streams JSON)'), $this->target->getNickname())),
new Feed(Feed::RSS1,
common_local_url(
'allrss', array(
'nickname' =>
$this->target->getNickname())
),
// TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->target->getNickname())),
new Feed(Feed::RSS2,
common_local_url(
'ApiTimelineFriends', array(
'format' => 'rss',
'id' => $this->target->getNickname()
)
),
// TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->target->getNickname())),
new Feed(Feed::ATOM,
common_local_url(
'ApiTimelineFriends', array(
'format' => 'atom',
'id' => $this->target->getNickname()
)
),
// TRANS: %s is user nickname.
sprintf(_('Feed for friends of %s (Atom)'), $this->target->getNickname()))
);
}
function showEmptyListMessage()
{
// TRANS: Empty list message. %s is a user nickname.
$message = sprintf(_('This is the timeline for %s and friends but no one has posted anything yet.'), $this->target->getNickname()) . ' ';
if (common_logged_in()) {
if ($this->target->id === $this->scoped->id) {
// TRANS: Encouragement displayed on logged in user's empty timeline.
// TRANS: This message contains Markdown links. Keep "](" together.
$message .= _('Try subscribing to more people, [join a group](%%action.groups%%) or post something yourself.');
} else {
// TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@".
// TRANS: This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('You can try to [nudge %1$s](../%2$s) from their profile or [post something to them](%%%%action.newnotice%%%%?status_textarea=%3$s).'), $this->target->getNickname(), $this->target->getNickname(), '@' . $this->target->getNickname());
}
} else {
// TRANS: Encouragement displayed on empty timeline user pages for anonymous users.
// TRANS: %s is a user nickname. This message contains Markdown links. Keep "](" together.
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to them.'), $this->target->getNickname());
}
$this->elementStart('div', 'guide');
$this->raw(common_markup_to_html($message));
$this->elementEnd('div');
}
function showContent()
{
if (Event::handle('StartShowAllContent', array($this))) {
if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
$nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
} else {
$nl = new ThreadedNoticeList($this->notice, $this, $this->scoped);
}
$cnt = $nl->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
}
$this->pagination(
$this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'all', array('nickname' => $this->target->getNickname())
);
Event::handle('EndShowAllContent', array($this));
}
}
function showSections()
{
// Show invite button, as long as site isn't closed, and
// we have a logged in user.
if (common_config('invite', 'enabled') && !common_config('site', 'closed') && common_logged_in()) {
if (!common_config('site', 'private')) {
$ibs = new InviteButtonSection(
$this,
// TRANS: Button text for inviting more users to the StatusNet instance.
// TRANS: Less business/enterprise-oriented language for public sites.
_m('BUTTON', 'Send invite')
);
} else {
$ibs = new InviteButtonSection($this);
}
$ibs->show();
}
// XXX: make this a little more convenient
if (!common_config('performance', 'high')) {
$pop = new InboxTagCloudSection($this, $this->target);
$pop->show();
}
}
}
class ThreadingInboxNoticeStream extends ThreadingNoticeStream
{
function __construct(Profile $target, Profile $scoped=null)
{
parent::__construct(new InboxNoticeStream($target, $scoped));
}
}

73
actions/allrss.php Normal file
View File

@ -0,0 +1,73 @@
<?php
/**
* RSS feed for user and friends timeline action class.
*
* PHP version 5
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Robin Millette <millette@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* RSS feed for user and friends timeline.
*
* Formatting of RSS handled by Rss10Action
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Robin Millette <millette@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class AllrssAction extends TargetedRss10Action
{
protected function getNotices()
{
$stream = new InboxNoticeStream($this->target);
return $stream->getNotices(0, $this->limit)->fetchAll();
}
/**
* Get channel.
*
* @return array associative array on channel information
*/
function getChannel()
{
$c = array('url' => common_local_url('allrss',
array('nickname' =>
$this->target->getNickname())),
// TRANS: Message is used as link title. %s is a user nickname.
'title' => sprintf(_('%s and friends'), $this->target->getNickname()),
'link' => common_local_url('all',
array('nickname' =>
$this->target->getNickname())),
// TRANS: Message is used as link description. %1$s is a username, %2$s is a site name.
'description' => sprintf(_('Updates from %1$s and friends on %2$s!'),
$this->target->getNickname(), common_config('site', 'name')));
return $c;
}
}

View File

@ -0,0 +1,119 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Dummy action that emulates Twitter's rate limit status API resource
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Brion Vibber <brion@pobox.com>
* @author Evan Prodromou <evan@status.net>
* @author Robin Millette <robin@millette.info>
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* We don't have a rate limit, but some clients check this method.
* It always returns the same thing: 150 hits left.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiAccountRateLimitStatusAction extends ApiBareAuthAction
{
/**
* Handle the request
*
* Return some Twitter-ish data about API limits
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
404,
$this->format
);
}
$reset = new DateTime();
$reset->modify('+1 hour');
$this->initDocument($this->format);
if ($this->format == 'xml') {
$this->elementStart('hash');
$this->element('remaining-hits', array('type' => 'integer'), 150);
$this->element('hourly-limit', array('type' => 'integer'), 150);
$this->element(
'reset-time', array('type' => 'datetime'),
common_date_iso8601($reset->format('r'))
);
$this->element(
'reset_time_in_seconds',
array('type' => 'integer'),
strtotime('+1 hour')
);
$this->elementEnd('hash');
} elseif ($this->format == 'json') {
$out = array(
'reset_time_in_seconds' => strtotime('+1 hour'),
'remaining_hits' => 150,
'hourly_limit' => 150,
'reset_time' => common_date_rfc2822(
$reset->format('r')
)
);
print json_encode($out);
}
$this->endDocument($this->format);
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -0,0 +1,194 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Register account
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
class ApiAccountRegisterAction extends ApiAction
{
/**
* Has there been an error?
*/
var $error = null;
/**
* Have we registered?
*/
var $registered = false;
protected $needPost = true;
protected $code = null; // invite code
protected $invite = null; // invite to-be-stored
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
if ($this->format !== 'json') {
$this->clientError('This method currently only serves JSON.', 415);
}
$this->code = $this->trimmed('code');
return true;
}
/**
* Handle the request
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
$nickname = $this->trimmed('nickname');
$email = $this->trimmed('email');
$fullname = $this->trimmed('fullname');
$homepage = $this->trimmed('homepage');
$bio = $this->trimmed('bio');
$location = $this->trimmed('location');
// We don't trim these... whitespace is OK in a password!
$password = $this->arg('password');
$confirm = $this->arg('confirm');
if (empty($this->code)) {
common_ensure_session();
if (array_key_exists('invitecode', $_SESSION)) {
$this->code = $_SESSION['invitecode'];
}
}
if (common_config('site', 'inviteonly') && empty($this->code)) {
// TRANS: Client error displayed when trying to register to an invite-only site without an invitation.
$this->clientError(_('Sorry, only invited people can register.'), 401);
}
if (!empty($this->code)) {
$this->invite = Invitation::getKV('code', $this->code);
if (empty($this->invite)) {
// TRANS: Client error displayed when trying to register to an invite-only site without a valid invitation.
$this->clientError(_('Sorry, invalid invitation code.'), 401);
}
// Store this in case we need it
common_ensure_session();
$_SESSION['invitecode'] = $this->code;
}
// Input scrubbing
try {
$nickname = Nickname::normalize($nickname, true);
} catch (NicknameException $e) {
// clientError handles Api exceptions with various formats and stuff
$this->clientError($e->getMessage(), $e->getCode());
}
$email = common_canonical_email($email);
if ($email && !Validate::email($email, common_config('email', 'check_domain'))) {
// TRANS: Form validation error displayed when trying to register without a valid e-mail address.
$this->clientError(_('Not a valid email address.'), 400);
} else if ($this->emailExists($email)) {
// TRANS: Form validation error displayed when trying to register with an already registered e-mail address.
$this->clientError(_('Email address already exists.'), 400);
} else if (!is_null($homepage) && (strlen($homepage) > 0) &&
!common_valid_http_url($homepage)) {
// TRANS: Form validation error displayed when trying to register with an invalid homepage URL.
$this->clientError(_('Homepage is not a valid URL.'), 400);
} else if (Profile::bioTooLong($bio)) {
// TRANS: Form validation error on registration page when providing too long a bio text.
// TRANS: %d is the maximum number of characters for bio; used for plural.
$this->clientError(sprintf(_m('Bio is too long (maximum %d character).',
'Bio is too long (maximum %d characters).',
Profile::maxBio()),
Profile::maxBio()), 400);
} else if (strlen($password) < 6) {
// TRANS: Form validation error displayed when trying to register with too short a password.
$this->clientError(_('Password must be 6 or more characters.'), 400);
} else if ($password != $confirm) {
// TRANS: Form validation error displayed when trying to register with non-matching passwords.
$this->clientError(_('Passwords do not match.'), 400);
} else {
// annoy spammers
sleep(7);
if (Event::handle('APIStartRegistrationTry', array($this))) {
try {
$user = User::register(array('nickname' => $nickname,
'password' => $password,
'email' => $email,
'fullname' => $fullname,
'homepage' => $homepage,
'bio' => $bio,
'location' => $location,
'code' => $this->code));
Event::handle('EndRegistrationTry', array($this));
$this->initDocument('json');
$this->showJsonObjects($this->twitterUserArray($user->getProfile()));
$this->endDocument('json');
} catch (Exception $e) {
$this->clientError($e->getMessage(), 400);
}
}
}
}
/**
* Does the given email address already exist?
*
* Checks a canonical email address against the database.
*
* @param string $email email address to check
*
* @return boolean true if the address already exists
*/
function emailExists($email)
{
$email = common_canonical_email($email);
if (!$email || strlen($email) == 0) {
return false;
}
$user = User::getKV('email', $email);
return is_object($user);
}
}

View File

@ -0,0 +1,90 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Update a user's background color
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
class ApiAccountUpdateBackgroundColorAction extends ApiAuthAction
{
var $backgroundcolor = null;
protected $needPost = true;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
if ($this->format !== 'json') {
$this->clientError('This method currently only serves JSON.', 415);
}
$this->backgroundcolor = $this->trimmed('backgroundcolor');
return true;
}
/**
* Handle the request
*
* Try to save the user's colors in her design. Create a new design
* if the user doesn't already have one.
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
$validhex = preg_match('/^[a-f0-9]{6}$/i',$this->backgroundcolor);
if ($validhex === false || $validhex == 0) {
$this->clientError(_('Not a valid hex color.'), 400);
}
// save the new color
$original = clone($this->auth_user);
$this->auth_user->backgroundcolor = $this->backgroundcolor;
if (!$this->auth_user->update($original)) {
$this->clientError(_('Error updating user.'), 404);
}
$twitter_user = $this->twitterUserArray($this->scoped, true);
$this->initDocument('json');
$this->showJsonObjects($twitter_user);
$this->endDocument('json');
}
}

View File

@ -0,0 +1,147 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Update the authenticating user notification channels
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Siebrand Mazeland <s.mazeland@xs4all.nl>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Sets which channel (device) StatusNet delivers updates to for
* the authenticating user. Sending none as the device parameter
* will disable IM and/or SMS updates.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
{
protected $needPost = true;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{
parent::prepare($args);
$this->user = $this->auth_user;
$this->device = $this->trimmed('device');
return true;
}
/**
* Handle the request
*
* See which request params have been set, and update the user settings
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
{
parent::handle($args);
if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
404,
$this->format
);
}
// Note: Twitter no longer supports IM
if (!in_array(strtolower($this->device), array('sms', 'im', 'none'))) {
// TRANS: Client error displayed when no valid device parameter is provided for a user's delivery device setting.
$this->clientError(_( 'You must specify a parameter named ' .
'\'device\' with a value of one of: sms, im, none.' ));
}
if (empty($this->user)) {
// TRANS: Client error displayed when no existing user is provided for a user's delivery device setting.
$this->clientError(_('No such user.'), 404);
}
$original = clone($this->user);
if (strtolower($this->device) == 'sms') {
$this->user->smsnotify = true;
} elseif (strtolower($this->device) == 'im') {
//TODO IM is pluginized now, so what should we do?
//Enable notifications for all IM plugins?
//For now, don't do anything
//$this->user->jabbernotify = true;
} elseif (strtolower($this->device == 'none')) {
$this->user->smsnotify = false;
//TODO IM is pluginized now, so what should we do?
//Disable notifications for all IM plugins?
//For now, don't do anything
//$this->user->jabbernotify = false;
}
$result = $this->user->update($original);
if ($result === false) {
common_log_db_error($this->user, 'UPDATE', __FILE__);
// TRANS: Server error displayed when a user's delivery device cannot be updated.
$this->serverError(_('Could not update user.'));
}
$profile = $this->user->getProfile();
$twitter_user = $this->twitterUserArray($profile, true);
// Note: this Twitter API method is retarded because it doesn't give
// any success/failure information. Twitter's docs claim that the
// notification field will change to reflect notification choice,
// but that's not true; notification> is used to indicate
// whether the auth user is following the user in question.
if ($this->format == 'xml') {
$this->initDocument('xml');
$this->showTwitterXmlUser($twitter_user, 'user', true);
$this->endDocument('xml');
} elseif ($this->format == 'json') {
$this->initDocument('json');
$this->showJsonObjects($twitter_user);
$this->endDocument('json');
}
}
}

View File

@ -0,0 +1,89 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Update a user's link color
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
class ApiAccountUpdateLinkColorAction extends ApiAuthAction
{
var $linkcolor = null;
protected $needPost = true;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
if ($this->format !== 'json') {
$this->clientError('This method currently only serves JSON.', 415);
}
$this->linkcolor = $this->trimmed('linkcolor');
return true;
}
/**
* Handle the request
*
* Try to save the user's colors in her design. Create a new design
* if the user doesn't already have one.
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
$validhex = preg_match('/^[a-f0-9]{6}$/i',$this->linkcolor);
if ($validhex === false || $validhex == 0) {
$this->clientError(_('Not a valid hex color.'), 400);
}
// save the new color
$original = clone($this->auth_user);
$this->auth_user->linkcolor = $this->linkcolor;
if (!$this->auth_user->update($original)) {
$this->clientError(_('Error updating user.'), 400);
}
$twitter_user = $this->twitterUserArray($this->scoped, true);
$this->initDocument('json');
$this->showJsonObjects($twitter_user);
$this->endDocument('json');
}
}

View File

@ -0,0 +1,141 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Update the authenticating user's profile
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* API analog to the profile settings page
* Only the parameters specified will be updated.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiAccountUpdateProfileAction extends ApiAuthAction
{
protected $needPost = true;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->user = $this->auth_user;
$this->name = $this->trimmed('name');
$this->url = $this->trimmed('url');
$this->location = $this->trimmed('location');
$this->description = $this->trimmed('description');
return true;
}
/**
* Handle the request
*
* See which request params have been set, and update the profile
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
if (empty($this->user)) {
// TRANS: Client error displayed if a user could not be found.
$this->clientError(_('No such user.'), 404);
}
$profile = $this->user->getProfile();
if (empty($profile)) {
// TRANS: Error message displayed when referring to a user without a profile.
$this->clientError(_('User has no profile.'));
}
$original = clone($profile);
$profile->fullname = $this->name;
$profile->homepage = $this->url;
$profile->bio = $this->description;
$profile->location = $this->location;
if (!empty($this->location)) {
$loc = Location::fromName($this->location);
if (!empty($loc)) {
$profile->lat = $loc->lat;
$profile->lon = $loc->lon;
$profile->location_id = $loc->location_id;
$profile->location_ns = $loc->location_ns;
}
} else {
// location is empty so reset the extrapolated information too
$profile->lat = '';
$profile->lon = '';
$profile->location_id = '';
$profile->location_ns = '';
}
$result = $profile->update($original);
if (!$result) {
common_log_db_error($profile, 'UPDATE', __FILE__);
// TRANS: Server error displayed if a user profile could not be saved.
$this->serverError(_('Could not save profile.'));
}
$twitter_user = $this->twitterUserArray($profile, true);
if ($this->format == 'xml') {
$this->initDocument('xml');
$this->showTwitterXmlUser($twitter_user, 'user', true);
$this->endDocument('xml');
} elseif ($this->format == 'json') {
$this->initDocument('json');
$this->showJsonObjects($twitter_user);
$this->endDocument('json');
}
}
}

View File

@ -0,0 +1,112 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Update the authenticating user's profile image
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Updates the authenticating user's profile image. Note that this API method
* expects raw multipart data, not a URL to an image.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiAccountUpdateProfileImageAction extends ApiAuthAction
{
protected $needPost = true;
/**
* Handle the request
*
* Check whether the credentials are valid and output the result
*
* @return void
*/
protected function handle()
{
parent::handle();
// Workaround for PHP returning empty $_POST and $_FILES when POST
// length > post_max_size in php.ini
if (empty($_FILES)
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
}
if (empty($this->user)) {
// TRANS: Client error displayed updating profile image without having a user object.
$this->clientError(_('No such user.'), 404);
}
try {
$imagefile = ImageFile::fromUpload('image');
} catch (Exception $e) {
$this->clientError($e->getMessage());
}
$type = $imagefile->preferredType();
$filename = Avatar::filename(
$user->id,
image_type_to_extension($type),
null,
'tmp'.common_timestamp()
);
$filepath = Avatar::path($filename);
$imagefile->copyTo($filepath);
$profile = $this->user->getProfile();
$profile->setOriginal($filename);
$twitter_user = $this->twitterUserArray($profile, true);
if ($this->format == 'xml') {
$this->initDocument('xml');
$this->showTwitterXmlUser($twitter_user, 'user', true);
$this->endDocument('xml');
} elseif ($this->format == 'json') {
$this->initDocument('json');
$this->showJsonObjects($twitter_user);
$this->endDocument('json');
}
}
}

View File

@ -0,0 +1,85 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Test if supplied user credentials are valid.
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Check a user's credentials. Returns an HTTP 200 OK response code and a
* representation of the requesting user if authentication was successful;
* returns a 401 status code and an error message if not.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiAccountVerifyCredentialsAction extends ApiAuthAction
{
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), $code = 404);
}
$twitter_user = $this->twitterUserArray($this->auth_user->getProfile(), true);
if ($this->format == 'xml') {
$this->initDocument('xml');
$this->showTwitterXmlUser($twitter_user, 'user', true);
$this->endDocument('xml');
} elseif ($this->format == 'json') {
$this->initDocument('json');
$this->showJsonObjects($twitter_user);
$this->endDocument('json');
}
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
}

136
actions/apiatomservice.php Normal file
View File

@ -0,0 +1,136 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* An AtomPub service document for a user
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Shows an AtomPub service document for a user
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPLv3
* @link http://status.net/
*/
class ApiAtomServiceAction extends ApiBareAuthAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
function prepare($args)
{
parent::prepare($args);
$this->user = $this->getTargetUser($this->arg('id'));
if (empty($this->user)) {
// TRANS: Client error displayed when making an Atom API request for an unknown user.
$this->clientError(_('No such user.'), 404);
}
return true;
}
/**
* Handle the arguments. In our case, show a service document.
*
* @param Array $args unused.
*
* @return void
*/
function handle($args)
{
parent::handle($args);
header('Content-Type: application/atomsvc+xml');
$this->startXML();
$this->elementStart('service', array('xmlns' => 'http://www.w3.org/2007/app',
'xmlns:atom' => 'http://www.w3.org/2005/Atom',
'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/'));
$this->elementStart('workspace');
// TRANS: Title for Atom feed.
$this->element('atom:title', null, _m('ATOM','Main'));
$this->elementStart('collection',
array('href' => common_local_url('ApiTimelineUser',
array('id' => $this->user->id,
'format' => 'atom'))));
$this->element('atom:title',
null,
// TRANS: Title for Atom feed. %s is a user nickname.
sprintf(_("%s timeline"),
$this->user->nickname));
$this->element('accept', null, 'application/atom+xml;type=entry');
$this->element('activity:verb', null, ActivityVerb::POST);
$this->elementEnd('collection');
$this->elementStart('collection',
array('href' => common_local_url('AtomPubSubscriptionFeed',
array('subscriber' => $this->user->id))));
$this->element('atom:title',
null,
// TRANS: Title for Atom feed with a user's subscriptions. %s is a user nickname.
sprintf(_("%s subscriptions"),
$this->user->nickname));
$this->element('accept', null, 'application/atom+xml;type=entry');
$this->element('activity:verb', null, ActivityVerb::FOLLOW);
$this->elementEnd('collection');
$this->elementStart('collection',
array('href' => common_local_url('AtomPubFavoriteFeed',
array('profile' => $this->user->id))));
$this->element('atom:title',
null,
// TRANS: Title for Atom feed with a user's favorite notices. %s is a user nickname.
sprintf(_("%s favorites"),
$this->user->nickname));
$this->element('accept', null, 'application/atom+xml;type=entry');
$this->element('activity:verb', null, ActivityVerb::FAVORITE);
$this->elementEnd('collection');
$this->elementStart('collection',
array('href' => common_local_url('AtomPubMembershipFeed',
array('profile' => $this->user->id))));
$this->element('atom:title',
null,
// TRANS: Title for Atom feed with a user's memberships. %s is a user nickname.
sprintf(_("%s memberships"),
$this->user->nickname));
$this->element('accept', null, 'application/atom+xml;type=entry');
$this->element('activity:verb', null, ActivityVerb::JOIN);
$this->elementEnd('collection');
$this->elementEnd('workspace');
$this->elementEnd('service');
$this->endXML();
}
}

106
actions/apiattachment.php Normal file
View File

@ -0,0 +1,106 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show a notice's attachment
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Show a notice's attachment
*
*/
class ApiAttachmentAction extends ApiAuthAction
{
const MAXCOUNT = 100;
var $original = null;
var $cnt = self::MAXCOUNT;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
if ($this->format !== 'json') {
$this->clientError('This method currently only serves JSON.', 415);
}
return true;
}
/**
* Handle the request
*
* Make a new notice for the update, save it, and show it
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
$file = new File();
$file->selectAdd(); // clears it
$file->selectAdd('url');
$file->id = $this->trimmed('id');
$url = $file->fetchAll('url');
$file_txt = '';
if(strstr($url[0],'.html')) {
$file_txt['txt'] = file_get_contents($url[0]);
$file_txt['body_start'] = strpos($file_txt['txt'],'<body>')+6;
$file_txt['body_end'] = strpos($file_txt['txt'],'</body>');
$file_txt = substr($file_txt['txt'],$file_txt['body_start'],$file_txt['body_end']-$file_txt['body_start']);
}
$this->initDocument('json');
$this->showJsonObjects($file_txt);
$this->endDocument('json');
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

113
actions/apiblockcreate.php Normal file
View File

@ -0,0 +1,113 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Block a user via the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Blocks the user specified in the ID parameter as the authenticating user.
* Destroys a friendship to the blocked user if it exists. Returns the
* blocked user in the requested format when successful.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiBlockCreateAction extends ApiAuthAction
{
protected $needPost = true;
var $other = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->other = $this->getTargetProfile($this->arg('id'));
return true;
}
/**
* Handle the request
*
* Save the new message
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
if (empty($this->user) || empty($this->other)) {
// TRANS: Client error displayed when trying to block a non-existing user or a user from another site.
$this->clientError(_('No such user.'), 404);
}
// Don't allow blocking yourself!
if ($this->user->id == $this->other->id) {
// TRANS: Client error displayed when users try to block themselves.
$this->clientError(_("You cannot block yourself!"), 403);
}
if (!$this->user->hasBlocked($this->other)) {
if (Event::handle('StartBlockProfile', array($this->user, $this->other))) {
$result = $this->user->block($this->other);
if ($result) {
Event::handle('EndBlockProfile', array($this->user, $this->other));
}
}
}
if ($this->user->hasBlocked($this->other)) {
$this->initDocument($this->format);
$this->showProfile($this->other, $this->format);
$this->endDocument($this->format);
} else {
// TRANS: Server error displayed when blocking a user has failed.
$this->serverError(_('Block user failed.'), 500);
}
}
}

102
actions/apiblockdestroy.php Normal file
View File

@ -0,0 +1,102 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Un-block a user via the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Un-blocks the user specified in the ID parameter for the authenticating user.
* Returns the un-blocked user in the requested format when successful.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiBlockDestroyAction extends ApiAuthAction
{
protected $needPost = true;
var $other = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->other = $this->getTargetProfile($this->arg('id'));
return true;
}
/**
* Handle the request
*
* Save the new message
*
* @return void
*/
protected function handle()
{
parent::handle();
if (empty($this->user) || empty($this->other)) {
// TRANS: Client error when user not found for an API action to remove a block for a user.
$this->clientError(_('No such user.'), 404);
}
if ($this->user->hasBlocked($this->other)) {
if (Event::handle('StartUnblockProfile', array($this->user, $this->other))) {
$result = $this->user->unblock($this->other);
if ($result) {
Event::handle('EndUnblockProfile', array($this->user, $this->other));
}
}
}
if (!$this->user->hasBlocked($this->other)) {
$this->initDocument($this->format);
$this->showProfile($this->other, $this->format);
$this->endDocument($this->format);
} else {
// TRANS: Server error displayed when unblocking a user has failed.
$this->serverError(_('Unblock user failed.'));
}
}
}

115
actions/apicheckhub.php Normal file
View File

@ -0,0 +1,115 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show a notice's attachment
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Check if a url have a push-hub, i.e. if it is possible to subscribe
*
*/
class ApiCheckHubAction extends ApiAuthAction
{
protected $url = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
if ($this->format !== 'json') {
$this->clientError('This method currently only serves JSON.', 415);
}
$this->url = urldecode($args['url']);
if (empty($this->url)) {
$this->clientError(_('No URL.'), 403);
}
if (!common_valid_http_url($this->url)) {
$this->clientError(_('Invalid URL.'), 403);
}
return true;
}
/**
* Handle the request
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
$discover = new FeedDiscovery();
try {
$feeduri = $discover->discoverFromURL($this->url);
if($feeduri) {
$huburi = $discover->getHubLink();
}
} catch (FeedSubNoFeedException $e) {
$this->clientError(_('No feed found'), 403);
} catch (FeedSubBadResponseException $e) {
$this->clientError(_('No hub found'), 403);
}
$hub_status = array();
if ($huburi) {
$hub_status = array('huburi' => $huburi);
}
$this->initDocument('json');
$this->showJsonObjects($hub_status);
$this->endDocument('json');
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -0,0 +1,69 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Check nickname
*
* Returns 1 if nickname is available on this instance, 0 if not. Error if site is private.
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
class ApiCheckNicknameAction extends ApiAction
{
protected function prepare(array $args=array())
{
parent::prepare($args);
if (common_config('site', 'private')) {
$this->clientError(_('This site is private.'), 403);
}
if ($this->format !== 'json') {
$this->clientError('This method currently only serves JSON.', 415);
}
return true;
}
protected function handle()
{
parent::handle();
$nickname = $this->trimmed('nickname');
try {
Nickname::normalize($nickname, true);
$nickname_ok = 1;
} catch (NicknameException $e) {
$nickname_ok = 0;
}
$this->initDocument('json');
$this->showJsonObjects($nickname_ok);
$this->endDocument('json');
}
}

227
actions/apiconversation.php Normal file
View File

@ -0,0 +1,227 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2011, StatusNet, Inc.
*
* Show a stream of notices in a particular conversation
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
// This check helps protect against security problems;
// your code file can't be executed directly from the web.
exit(1);
}
/**
* Show a stream of notices in a particular conversation
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class ApiconversationAction extends ApiAuthAction
{
protected $conversation = null;
protected $notices = null;
protected function prepare(array $args=array())
{
parent::prepare($args);
$convId = $this->trimmed('id');
if (empty($convId)) {
// TRANS: Client exception thrown when no conversation ID is given.
throw new ClientException(_('No conversation ID.'));
}
$this->conversation = Conversation::getKV('id', $convId);
if (empty($this->conversation)) {
// TRANS: Client exception thrown when referring to a non-existing conversation ID (%d).
throw new ClientException(sprintf(_('No conversation with ID %d.'), $convId),
404);
}
$stream = new ConversationNoticeStream($convId, $this->scoped);
$notice = $stream->getNotices(($this->page-1) * $this->count,
$this->count,
$this->since_id,
$this->max_id);
$this->notices = $notice->fetchAll();
return true;
}
/**
* Handler method
*
* @param array $argarray is ignored since it's now passed in in prepare()
*
* @return void
*/
function handle($argarray=null)
{
$sitename = common_config('site', 'name');
// TRANS: Title for conversion timeline.
$title = _m('TITLE', 'Conversation');
$id = common_local_url('apiconversation', array('id' => $this->conversation->id, 'format' => $this->format));
$link = common_local_url('conversation', array('id' => $this->conversation->id));
$self = $id;
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline(
$this->notices,
$title,
$link,
null,
null,
null,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id);
$atom->setTitle($title);
$atom->setUpdated('now');
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
break;
case 'json':
$this->showJsonTimeline($this->notices);
break;
case 'as':
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
$doc = new ActivityStreamJSONDocument($this->auth_user);
$doc->setTitle($title);
$doc->addLink($link, 'alternate', 'text/html');
$doc->addItemsFromNotices($this->notices);
$this->raw($doc->asString());
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), $code = 404);
break;
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
$_SERVER['REQUEST_METHOD'] == 'HEAD') {
return true;
} else {
return false;
}
}
/**
* Return last modified, if applicable.
*
* MAY override
*
* @return string last modified http header
*/
function lastModified()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
return strtotime($this->notices[0]->created);
}
return null;
}
/**
* Return etag, if applicable.
*
* MAY override
*
* @return string etag http header
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
$last = count($this->notices) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->user->id,
strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created))
)
. '"';
}
return null;
}
/**
* Does this require authentication?
*
* @return boolean true if delete, else false
*/
function requiresAuth()
{
if ($_SERVER['REQUEST_METHOD'] == 'GET' ||
$_SERVER['REQUEST_METHOD'] == 'HEAD') {
return false;
} else {
return true;
}
}
}

View File

@ -0,0 +1,99 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show an external user's profile information
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Ouputs information for a user, specified by ID or screen name.
* The user's most recent status will be returned inline.
*/
class ApiExternalProfileShowAction extends ApiPrivateAuthAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
if ($this->format !== 'json') {
$this->clientError('This method currently only serves JSON.', 415);
}
$profileurl = urldecode($this->arg('profileurl'));
// TODO: Make this more ... unique!
$this->profile = Profile::getKV('profileurl', $profileurl);
if (!($this->profile instanceof Profile)) {
// TRANS: Client error displayed when requesting profile information for a non-existing profile.
$this->clientError(_('Profile not found.'), 404);
}
return true;
}
/**
* Handle the request
*
* Check the format and show the user info
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
$twitter_user = $this->twitterUserArray($this->profile, true);
$this->initDocument('json');
$this->showJsonObjects($twitter_user);
$this->endDocument('json');
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -0,0 +1,111 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Subscribe to a user via the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Allows the authenticating users to follow (subscribe) the user specified in
* the ID parameter. Returns the befriended user in the requested format when
* successful. Returns a string describing the failure condition when unsuccessful.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiFriendshipsCreateAction extends ApiAuthAction
{
protected $needPost = true;
var $other = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->other = $this->getTargetProfile($this->arg('id'));
return true;
}
/**
* Handle the request
*
* Check the format and show the user info
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
if (empty($this->other)) {
// TRANS: Client error displayed when trying follow who's profile could not be found.
$this->clientError(_('Could not follow user: profile not found.'), 403);
}
if ($this->scoped->isSubscribed($this->other)) {
$errmsg = sprintf(
// TRANS: Client error displayed when trying to follow a user that's already being followed.
// TRANS: %s is the nickname of the user that is already being followed.
_('Could not follow user: %s is already on your list.'),
$this->other->nickname
);
$this->clientError($errmsg, 403);
}
try {
Subscription::start($this->scoped, $this->other);
} catch (AlreadyFulfilledException $e) {
$this->clientError($e->getMessage(), 409);
}
$this->initDocument($this->format);
$this->showProfile($this->other, $this->format);
$this->endDocument($this->format);
}
}

View File

@ -0,0 +1,114 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Unsubscribe to a user via API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Allows the authenticating users to unfollow (unsubscribe) the user specified in
* the ID parameter. Returns the unfollowed user in the requested format when
* successful. Returns a string describing the failure condition when unsuccessful.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiFriendshipsDestroyAction extends ApiAuthAction
{
protected $needPost = true;
protected $other = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->other = $this->getTargetProfile($this->arg('id'));
return true;
}
/**
* Handle the request
*
* Check the format and show the user info
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
404
);
}
if (!$this->other instanceof Profile) {
$this->clientError(
// TRANS: Client error displayed when trying to unfollow a user that cannot be found.
_('Could not unfollow user: User not found.'),
403
);
}
// Don't allow unsubscribing from yourself!
if ($this->scoped->id == $this->other->id) {
$this->clientError(
// TRANS: Client error displayed when trying to unfollow self.
_("You cannot unfollow yourself."),
403
);
}
// throws an exception on error
Subscription::cancel($this->scoped, $this->other);
$this->initDocument($this->format);
$this->showProfile($this->other, $this->format);
$this->endDocument($this->format);
}
}

View File

@ -0,0 +1,118 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show whether there is a friendship between two users
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Tests for the existence of friendship between two users. Will return true if
* user_a follows user_b, otherwise will return false.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiFriendshipsExistsAction extends ApiPrivateAuthAction
{
var $profile_a = null;
var $profile_b = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->profile_a = $this->getTargetProfile($this->trimmed('user_a'));
$this->profile_b = $this->getTargetProfile($this->trimmed('user_b'));
return true;
}
/**
* Handle the request
*
* Check the format and show the user info
*
* @return void
*/
protected function handle()
{
parent::handle();
if (empty($this->profile_a) || empty($this->profile_b)) {
$this->clientError(
// TRANS: Client error displayed when supplying invalid parameters to an API call checking if a friendship exists.
_('Two valid IDs or nick names must be supplied.'),
400
);
}
$result = Subscription::exists($this->profile_a, $this->profile_b);
switch ($this->format) {
case 'xml':
$this->initDocument('xml');
$this->element('friends', null, $result);
$this->endDocument('xml');
break;
case 'json':
$this->initDocument('json');
print json_encode($result);
$this->endDocument('json');
break;
default:
break;
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -0,0 +1,169 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show information about the relationship between two users
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Outputs detailed information about the relationship between two users
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiFriendshipsShowAction extends ApiBareAuthAction
{
var $source = null;
var $target = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$source_id = (int)$this->trimmed('source_id');
$source_screen_name = $this->trimmed('source_screen_name');
$target_id = (int)$this->trimmed('target_id');
$target_screen_name = $this->trimmed('target_screen_name');
if (!empty($source_id)) {
$this->source = User::getKV($source_id);
} elseif (!empty($source_screen_name)) {
$this->source = User::getKV('nickname', $source_screen_name);
} else {
$this->source = $this->auth_user;
}
if (!empty($target_id)) {
$this->target = User::getKV($target_id);
} elseif (!empty($target_screen_name)) {
$this->target = User::getKV('nickname', $target_screen_name);
}
return true;
}
/**
* Determines whether this API resource requires auth. Overloaded to look
* return true in case source_id and source_screen_name are both empty
*
* @return boolean true or false
*/
function requiresAuth()
{
if (common_config('site', 'private')) {
return true;
}
$source_id = $this->trimmed('source_id');
$source_screen_name = $this->trimmed('source_screen_name');
if (empty($source_id) && empty($source_screen_name)) {
return true;
}
return false;
}
/**
* Handle the request
*
* Check the format and show the user info
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
if (empty($this->source)) {
$this->clientError(
// TRANS: Client error displayed when a source user could not be determined showing friendship.
_('Could not determine source user.'),
404
);
}
if (empty($this->target)) {
$this->clientError(
// TRANS: Client error displayed when a target user could not be determined showing friendship.
_('Could not find target user.'),
404
);
}
$result = $this->twitterRelationshipArray($this->source, $this->target);
switch ($this->format) {
case 'xml':
$this->initDocument('xml');
$this->showTwitterXmlRelationship($result[relationship]);
$this->endDocument('xml');
break;
case 'json':
$this->initDocument('json');
print json_encode($result);
$this->endDocument('json');
break;
default:
break;
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -0,0 +1,148 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Dump of configuration variables
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Gives a full dump of configuration variables for this instance
* of GNU social, minus variables that may be security-sensitive (like
* passwords).
* URL: https://example.com/api/gnusocial/config.(xml|json)
* Formats: xml, json
*
* @category API
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
class ApiGNUsocialConfigAction extends ApiAction
{
var $keys = array(
'site' => array('name', 'server', 'theme', 'path', 'logo', 'fancy', 'language',
'email', 'broughtby', 'broughtbyurl', 'timezone', 'closed',
'inviteonly', 'private', 'textlimit', 'ssl', 'sslserver'),
'license' => array('type', 'owner', 'url', 'title', 'image'),
'nickname' => array('featured'),
'profile' => array('biolimit'),
'group' => array('desclimit'),
'notice' => array('contentlimit'),
'throttle' => array('enabled', 'count', 'timespan'),
'xmpp' => array('enabled', 'server', 'port', 'user'),
'integration' => array('source'),
'attachments' => array('uploads', 'file_quota'),
'url' => array('maxurllength', 'maxnoticelength'),
);
protected function handle()
{
parent::handle();
switch ($this->format) {
case 'xml':
$this->initDocument('xml');
$this->elementStart('config');
// XXX: check that all sections and settings are legal XML elements
foreach ($this->keys as $section => $settings) {
$this->elementStart($section);
foreach ($settings as $setting) {
$value = $this->setting($section, $setting);
if (is_array($value)) {
$value = implode(',', $value);
} else if ($value === false || $value == '0') {
$value = 'false';
} else if ($value === true || $value == '1') {
$value = 'true';
}
// return theme logo if there's no site specific one
if (empty($value)) {
if ($section == 'site' && $setting == 'logo') {
$value = Theme::path('logo.png');
}
}
$this->element($setting, null, $value);
}
$this->elementEnd($section);
}
$this->elementEnd('config');
$this->endDocument('xml');
break;
case 'json':
$result = array();
foreach ($this->keys as $section => $settings) {
$result[$section] = array();
foreach ($settings as $setting) {
$result[$section][$setting]
= $this->setting($section, $setting);
}
}
$this->initDocument('json');
$this->showJsonObjects($result);
$this->endDocument('json');
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
function setting($section, $key) {
$result = common_config($section, $key);
if ($key == 'file_quota') {
// hack: adjust for the live upload limit
if (common_config($section, 'uploads')) {
$max = ImageFile::maxFileSizeInt();
} else {
$max = 0;
}
return min($result, $max);
}
return $result;
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -0,0 +1,82 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* A version stamp for the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Returns a version number for this version of GNU social, which
* should make things a bit easier for upgrades.
* URL: http://identi.ca/api/statusnet/version.(xml|json)
* Formats: xml, js
*
* @category API
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
class ApiGNUsocialVersionAction extends ApiPrivateAuthAction
{
protected function handle()
{
parent::handle();
switch ($this->format) {
case 'xml':
$this->initDocument('xml');
$this->element('version', null, GNUSOCIAL_VERSION);
$this->endDocument('xml');
break;
case 'json':
$this->initDocument('json');
print '"'.GNUSOCIAL_VERSION.'"';
$this->endDocument('json');
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

190
actions/apigroupadmins.php Normal file
View File

@ -0,0 +1,190 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* List a group's admins
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) {
exit(1);
}
/**
* List 20 newest admins of the group specified by name or ID.
*
* @category API
* @package GNUsocial
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
class ApiGroupAdminsAction extends ApiPrivateAuthAction
{
var $group = null;
var $profiles = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->group = $this->getTargetGroup($this->arg('id'));
if (empty($this->group)) {
// TRANS: Client error displayed trying to show group membership on a non-existing group.
$this->clientError(_('Group not found.'), 404);
}
$this->profiles = $this->getProfiles();
return true;
}
/**
* Handle the request
*
* Show the admin of the group
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
// XXX: RSS and Atom
switch($this->format) {
case 'xml':
$this->showTwitterXmlUsers($this->profiles);
break;
case 'json':
$this->showJsonUsers($this->profiles);
break;
default:
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
404,
$this->format
);
break;
}
}
/**
* Fetch the admins of a group
*
* @return array $profiles list of profiles
*/
function getProfiles()
{
$profiles = array();
$profile = $this->group->getAdmins(
($this->page - 1) * $this->count,
$this->count,
$this->since_id,
$this->max_id
);
while ($profile->fetch()) {
$profiles[] = clone($profile);
}
return $profiles;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this list of profiles last modified?
*
* @return string datestamp of the lastest profile in the group
*/
function lastModified()
{
if (!empty($this->profiles) && (count($this->profiles) > 0)) {
return strtotime($this->profiles[0]->created);
}
return null;
}
/**
* An entity tag for this list of groups
*
* Returns an Etag based on the action name, language
* the group id, and timestamps of the first and last
* user who has joined the group
*
* @return string etag
*/
function etag()
{
if (!empty($this->profiles) && (count($this->profiles) > 0)) {
$last = count($this->profiles) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->group->id,
strtotime($this->profiles[0]->created),
strtotime($this->profiles[$last]->created))
)
. '"';
}
return null;
}
}

181
actions/apigroupcreate.php Normal file
View File

@ -0,0 +1,181 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Create a group via the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Make a new group. Sets the authenticated user as the administrator of the group.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupCreateAction extends ApiAuthAction
{
protected $needPost = true;
var $group = null;
var $nickname = null;
var $fullname = null;
var $homepage = null;
var $description = null;
var $location = null;
var $aliasstring = null;
var $aliases = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->nickname = Nickname::normalize($this->arg('nickname'), true);
$this->fullname = $this->arg('full_name');
$this->homepage = $this->arg('homepage');
$this->description = $this->arg('description');
$this->location = $this->arg('location');
$this->aliasstring = $this->arg('aliases');
return true;
}
/**
* Handle the request
*
* Save the new group
*
* @return void
*/
protected function handle()
{
parent::handle();
if (empty($this->user)) {
// TRANS: Client error given when a user was not found (404).
$this->clientError(_('No such user.'), 404);
}
if ($this->validateParams() == false) {
return;
}
$group = User_group::register(array('nickname' => $this->nickname,
'fullname' => $this->fullname,
'homepage' => $this->homepage,
'description' => $this->description,
'location' => $this->location,
'aliases' => $this->aliases,
'userid' => $this->user->id,
'local' => true));
switch($this->format) {
case 'xml':
$this->showSingleXmlGroup($group);
break;
case 'json':
$this->showSingleJsonGroup($group);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Validate params for the new group
*
* @return void
*/
function validateParams()
{
if (!is_null($this->homepage)
&& strlen($this->homepage) > 0
&& !common_valid_http_url($this->homepage)) {
// TRANS: Client error in form for group creation.
$this->clientError(_('Homepage is not a valid URL.'), 403);
} elseif (!is_null($this->fullname)
&& mb_strlen($this->fullname) > 255) {
// TRANS: Client error in form for group creation.
$this->clientError(_('Full name is too long (maximum 255 characters).'), 403);
} elseif (User_group::descriptionTooLong($this->description)) {
// TRANS: Client error shown when providing too long a description during group creation.
// TRANS: %d is the maximum number of allowed characters.
$this->clientError(sprintf(_m('Description is too long (maximum %d character).',
'Description is too long (maximum %d characters).',
User_group::maxDescription()), User_group::maxDescription()), 403);
} elseif (!is_null($this->location)
&& mb_strlen($this->location) > 255) {
// TRANS: Client error shown when providing too long a location during group creation.
$this->clientError(_('Location is too long (maximum 255 characters).'), 403);
}
if (!empty($this->aliasstring)) {
$this->aliases = array_map(
array('Nickname', 'normalize'), // static call to Nickname::normalize
array_unique(preg_split('/[\s,]+/', $this->aliasstring))
);
} else {
$this->aliases = array();
}
if (count($this->aliases) > common_config('group', 'maxaliases')) {
$this->clientError(sprintf(
// TRANS: Client error shown when providing too many aliases during group creation.
// TRANS: %d is the maximum number of allowed aliases.
_m('Too many aliases! Maximum %d allowed.',
'Too many aliases! Maximum %d allowed.',
common_config('group', 'maxaliases')),
common_config('group', 'maxaliases')),
403);
}
// Everything looks OK
return true;
}
}

View File

@ -0,0 +1,125 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Check to see whether a user a member of a group
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns whether a user is a member of a specified group.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupIsMemberAction extends ApiBareAuthAction
{
var $group = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->target = $this->getTargetProfile(null);
$this->group = $this->getTargetGroup(null);
return true;
}
/**
* Handle the request
*
* Save the new message
*
* @return void
*/
protected function handle()
{
parent::handle();
if (empty($this->target)) {
// TRANS: Client error displayed when checking group membership for a non-existing user.
$this->clientError(_('No such user.'), 404);
}
if (empty($this->group)) {
// TRANS: Client error displayed when checking group membership for a non-existing group.
$this->clientError(_('Group not found.'), 404);
}
$is_member = $this->target->isMember($this->group);
switch($this->format) {
case 'xml':
$this->initDocument('xml');
$this->element('is_member', null, $is_member);
$this->endDocument('xml');
break;
case 'json':
$this->initDocument('json');
$this->showJsonObjects(array('is_member' => $is_member));
$this->endDocument('json');
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'));
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

124
actions/apigroupjoin.php Normal file
View File

@ -0,0 +1,124 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Join a group via the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Joins the authenticated user to the group speicified by ID
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupJoinAction extends ApiAuthAction
{
protected $needPost = true;
var $group = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->group = $this->getTargetGroup($this->arg('id'));
return true;
}
/**
* Handle the request
*
* Join the authenticated user to the group
*
* @return void
*/
protected function handle()
{
parent::handle();
if (empty($this->scoped)) {
// TRANS: Client error displayed when trying to have a non-existing user join a group.
$this->clientError(_('No such user.'), 404);
}
if (empty($this->group)) {
// TRANS: Client error displayed when trying to join a group that does not exist.
$this->clientError(_('Group not found.'), 404);
}
if ($this->scoped->isMember($this->group)) {
// TRANS: Server error displayed when trying to join a group the user is already a member of.
$this->clientError(_('You are already a member of that group.'), 403);
}
if (Group_block::isBlocked($this->group, $this->scoped)) {
// TRANS: Server error displayed when trying to join a group the user is blocked from joining.
$this->clientError(_('You have been blocked from that group by the admin.'), 403);
}
try {
$this->scoped->joinGroup($this->group);
} catch (Exception $e) {
// TRANS: Server error displayed when joining a group failed in the database.
// TRANS: %1$s is the joining user's nickname, $2$s is the group nickname for which the join failed.
$this->serverError(sprintf(_('Could not join user %1$s to group %2$s.'),
$this->scoped->nickname, $this->group->nickname));
}
switch($this->format) {
case 'xml':
$this->showSingleXmlGroup($this->group);
break;
case 'json':
$this->showSingleJsonGroup($this->group);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
}

123
actions/apigroupleave.php Normal file
View File

@ -0,0 +1,123 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Leave a group via the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Removes the authenticated user from the group specified by ID
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupLeaveAction extends ApiAuthAction
{
protected $needPost = true;
var $group = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->group = $this->getTargetGroup($this->arg('id'));
return true;
}
/**
* Handle the request
*
* Save the new message
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!$this->scoped instanceof Profile) {
// TRANS: Client error displayed when trying to have a non-existing user leave a group.
$this->clientError(_('No such user.'), 404);
}
if (!$this->group instanceof User_group) {
// TRANS: Client error displayed when trying to leave a group that does not exist.
$this->clientError(_('Group not found.'), 404);
}
$member = new Group_member();
$member->group_id = $this->group->id;
$member->profile_id = $this->scoped->id;
if (!$member->find(true)) {
// TRANS: Server error displayed when trying to leave a group the user is not a member of.
$this->serverError(_('You are not a member of this group.'));
}
try {
$this->user->leaveGroup($this->group);
} catch (Exception $e) {
// TRANS: Server error displayed when leaving a group failed in the database.
// TRANS: %1$s is the leaving user's nickname, $2$s is the group nickname for which the leave failed.
$this->serverError(sprintf(_('Could not remove user %1$s from group %2$s.'),
$this->scoped->getNickname(), $this->group->nickname));
}
switch($this->format) {
case 'xml':
$this->showSingleXmlGroup($this->group);
break;
case 'json':
$this->showSingleJsonGroup($this->group);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
}

211
actions/apigrouplist.php Normal file
View File

@ -0,0 +1,211 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Check to see whether a user a member of a group
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns whether a user is a member of a specified group.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupListAction extends ApiBareAuthAction
{
var $groups = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
//TODO: Make sure this doesn't leak unwantedly for federated users
$this->target = $this->getTargetProfile(null);
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed when user not found for an action.
$this->clientError(_('No such user.'), 404);
}
$this->groups = $this->getGroups();
return true;
}
/**
* Handle the request
*
* Show the user's groups
*
* @return void
*/
protected function handle()
{
parent::handle();
$sitename = common_config('site', 'name');
// TRANS: Used as title in check for group membership. %s is a user name.
$title = sprintf(_("%s's groups"), $this->target->nickname);
$taguribase = TagURI::base();
$id = "tag:$taguribase:Groups";
$link = common_local_url(
'usergroups',
array('nickname' => $this->target->nickname)
);
$subtitle = sprintf(
// TRANS: Used as subtitle in check for group membership. %1$s is the site name, %2$s is a user name.
_('%1$s groups %2$s is a member of.'),
$sitename,
$this->target->nickname
);
switch($this->format) {
case 'xml':
$this->showXmlGroups($this->groups);
break;
case 'rss':
$this->showRssGroups($this->groups, $title, $link, $subtitle);
break;
case 'atom':
$selfuri = common_local_url('ApiGroupList', array('id'=>$this->target->id, 'format'=>'atom'));
$this->showAtomGroups(
$this->groups,
$title,
$id,
$link,
$subtitle,
$selfuri
);
break;
case 'json':
$this->showJsonGroups($this->groups);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Get groups
*
* @return array groups
*/
function getGroups()
{
$groups = array();
$group = $this->target->getGroups(
($this->page - 1) * $this->count,
$this->count,
$this->since_id,
$this->max_id
);
while ($group->fetch()) {
$groups[] = clone($group);
}
return $groups;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest group the user has joined
*/
function lastModified()
{
if (!empty($this->groups) && (count($this->groups) > 0)) {
return strtotime($this->groups[0]->created);
}
return null;
}
/**
* An entity tag for this list of groups
*
* Returns an Etag based on the action name, language, user ID and
* timestamps of the first and last group the user has joined
*
* @return string etag
*/
function etag()
{
if (!empty($this->groups) && (count($this->groups) > 0)) {
$last = count($this->groups) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->target->id,
strtotime($this->groups[0]->created),
strtotime($this->groups[$last]->created))
)
. '"';
}
return null;
}
}

208
actions/apigrouplistall.php Normal file
View File

@ -0,0 +1,208 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show the newest groups
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns of the lastest 20 groups for the site
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupListAllAction extends ApiPrivateAuthAction
{
var $groups = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{
parent::prepare($args);
$this->user = $this->getTargetUser(null);
$this->groups = $this->getGroups();
return true;
}
/**
* Handle the request
*
* Show the user's groups
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$sitename = common_config('site', 'name');
// TRANS: Message is used as a title when listing the lastest 20 groups. %s is a site name.
$title = sprintf(_("%s groups"), $sitename);
$taguribase = TagURI::base();
$id = "tag:$taguribase:Groups";
$link = common_local_url('groups');
// TRANS: Message is used as a subtitle when listing the latest 20 groups. %s is a site name.
$subtitle = sprintf(_("groups on %s"), $sitename);
switch($this->format) {
case 'xml':
$this->showXmlGroups($this->groups);
break;
case 'rss':
$this->showRssGroups($this->groups, $title, $link, $subtitle);
break;
case 'atom':
$selfuri = common_root_url() .
'api/statusnet/groups/list_all.atom';
$this->showAtomGroups(
$this->groups,
$title,
$id,
$link,
$subtitle,
$selfuri
);
break;
case 'json':
$this->showJsonGroups($this->groups);
break;
default:
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
404,
$this->format
);
break;
}
}
/**
* Get groups
*
* @return array groups
*/
function getGroups()
{
$qry = 'SELECT user_group.* '.
'from user_group join local_group on user_group.id = local_group.group_id '.
'order by created desc ';
$offset = intval($this->page - 1) * intval($this->count);
$limit = intval($this->count);
if (common_config('db', 'type') == 'pgsql') {
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
} else {
$qry .= ' LIMIT ' . $offset . ', ' . $limit;
}
$group = new User_group();
$group->query($qry);
$groups = array();
while ($group->fetch()) {
$groups[] = clone($group);
}
return $groups;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the site's latest group
*/
function lastModified()
{
if (!empty($this->groups) && (count($this->groups) > 0)) {
return strtotime($this->groups[0]->created);
}
return null;
}
/**
* An entity tag for this list of groups
*
* Returns an Etag based on the action name, language, and
* timestamps of the first and last group the user has joined
*
* @return string etag
*/
function etag()
{
if (!empty($this->groups) && (count($this->groups) > 0)) {
$last = count($this->groups) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
strtotime($this->groups[0]->created),
strtotime($this->groups[$last]->created))
)
. '"';
}
return null;
}
}

View File

@ -0,0 +1,181 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* List a group's members
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* List 20 newest members of the group specified by name or ID.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupMembershipAction extends ApiPrivateAuthAction
{
var $group = null;
var $profiles = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->group = $this->getTargetGroup($this->arg('id'));
if (empty($this->group)) {
// TRANS: Client error displayed trying to show group membership on a non-existing group.
$this->clientError(_('Group not found.'), 404);
}
$this->profiles = $this->getProfiles();
return true;
}
/**
* Handle the request
*
* Show the members of the group
*
* @return void
*/
protected function handle()
{
parent::handle();
// XXX: RSS and Atom
switch($this->format) {
case 'xml':
$this->showTwitterXmlUsers($this->profiles);
break;
case 'json':
$this->showJsonUsers($this->profiles);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Fetch the members of a group
*
* @return array $profiles list of profiles
*/
function getProfiles()
{
$profiles = array();
$profile = $this->group->getMembers(
($this->page - 1) * $this->count,
$this->count,
$this->since_id,
$this->max_id
);
while ($profile->fetch()) {
$profiles[] = clone($profile);
}
return $profiles;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this list of profiles last modified?
*
* @return string datestamp of the lastest profile in the group
*/
function lastModified()
{
if (!empty($this->profiles) && (count($this->profiles) > 0)) {
return strtotime($this->profiles[0]->created);
}
return null;
}
/**
* An entity tag for this list of groups
*
* Returns an Etag based on the action name, language
* the group id, and timestamps of the first and last
* user who has joined the group
*
* @return string etag
*/
function etag()
{
if (!empty($this->profiles) && (count($this->profiles) > 0)) {
$last = count($this->profiles) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->group->id,
strtotime($this->profiles[0]->created),
strtotime($this->profiles[$last]->created))
)
. '"';
}
return null;
}
}

View File

@ -0,0 +1,247 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Update a group's profile
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* API analog to the group edit page
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupProfileUpdateAction extends ApiAuthAction
{
protected $needPost = true;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->nickname = Nickname::normalize($this->trimmed('nickname'));
$this->fullname = $this->trimmed('fullname');
$this->homepage = $this->trimmed('homepage');
$this->description = $this->trimmed('description');
$this->location = $this->trimmed('location');
$this->aliasstring = $this->trimmed('aliases');
$this->user = $this->auth_user;
$this->group = $this->getTargetGroup($this->arg('id'));
return true;
}
/**
* Handle the request
*
* See which request params have been set, and update the profile
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
if (empty($this->user)) {
// TRANS: Client error displayed when not providing a user or an invalid user.
$this->clientError(_('No such user.'), 404);
}
if (empty($this->group)) {
// TRANS: Client error displayed when not providing a group or an invalid group.
$this->clientError(_('Group not found.'), 404);
}
if (!$this->user->isAdmin($this->group)) {
// TRANS: Client error displayed when trying to edit a group without being an admin.
$this->clientError(_('You must be an admin to edit the group.'), 403);
}
$this->group->query('BEGIN');
$orig = clone($this->group);
try {
if (common_config('profile', 'changenick') == true && $this->group->nickname !== $this->nickname) {
try {
$this->group->nickname = Nickname::normalize($this->nickname, true);
} catch (NicknameException $e) {
throw new ApiValidationException($e->getMessage());
}
$this->group->mainpage = common_local_url('showgroup',
array('nickname' => $this->group->nickname));
}
if (!empty($this->fullname)) {
$this->validateFullname();
$this->group->fullname = $this->fullname;
}
if (!empty($this->homepage)) {
$this->validateHomepage();
$this->group->homepage = $this->homepage;
}
if (!empty($this->description)) {
$this->validateDescription();
$this->group->description = $this->decription;
}
if (!empty($this->location)) {
$this->validateLocation();
$this->group->location = $this->location;
}
} catch (ApiValidationException $ave) {
$this->clientError($ave->getMessage(), 400);
}
$result = $this->group->update($orig);
if (!$result) {
common_log_db_error($this->group, 'UPDATE', __FILE__);
// TRANS: Server error displayed when group update fails.
$this->serverError(_('Could not update group.'));
}
$aliases = array();
try {
if (!empty($this->aliasstring)) {
$aliases = $this->validateAliases();
}
} catch (ApiValidationException $ave) {
$this->clientError($ave->getMessage(), 403);
}
$result = $this->group->setAliases($aliases);
if (!$result) {
// TRANS: Server error displayed when adding group aliases fails.
$this->serverError(_('Could not create aliases.'));
}
$this->group->query('COMMIT');
switch($this->format) {
case 'xml':
$this->showSingleXmlGroup($this->group);
break;
case 'json':
$this->showSingleJsonGroup($this->group);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
function validateHomepage()
{
if (!is_null($this->homepage)
&& (strlen($this->homepage) > 0)
&& !common_valid_http_url($this->homepage)) {
throw new ApiValidationException(
// TRANS: API validation exception thrown when homepage URL does not validate.
_('Homepage is not a valid URL.')
);
}
}
function validateFullname()
{
if (!is_null($this->fullname) && mb_strlen($this->fullname) > 255) {
throw new ApiValidationException(
// TRANS: API validation exception thrown when full name does not validate.
_('Full name is too long (maximum 255 characters).')
);
}
}
function validateDescription()
{
if (User_group::descriptionTooLong($this->description)) {
// TRANS: API validation exception thrown when description does not validate.
// TRANS: %d is the maximum description length and used for plural.
throw new ApiValidationException(sprintf(_m('Description is too long (maximum %d character).',
'Description is too long (maximum %d characters).',
User_group::maxDescription()),
User_group::maxDescription()));
}
}
function validateLocation()
{
if (!is_null($this->location) && mb_strlen($this->location) > 255) {
throw new ApiValidationException(
// TRANS: API validation exception thrown when location does not validate.
_('Location is too long (maximum 255 characters).')
);
}
}
function validateAliases()
{
try {
$aliases = array_map(array('Nickname', 'normalize'),
array_unique(preg_split('/[\s,]+/', $this->aliasstring)));
} catch (NicknameException $e) {
throw new ApiValidationException(sprintf('Error processing aliases: %s', $e->getMessage()));
}
if (count($aliases) > common_config('group', 'maxaliases')) {
// TRANS: API validation exception thrown when aliases do not validate.
// TRANS: %d is the maximum number of aliases and used for plural.
throw new ApiValidationException(sprintf(_m('Too many aliases! Maximum %d allowed.',
'Too many aliases! Maximum %d allowed.',
common_config('group', 'maxaliases')),
common_config('group', 'maxaliases')));
}
return $aliases;
}
}

163
actions/apigroupshow.php Normal file
View File

@ -0,0 +1,163 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show information about a group
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Outputs detailed information about the group specified by ID
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @author Michele <macno@macno.org>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiGroupShowAction extends ApiPrivateAuthAction
{
var $group = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->group = $this->getTargetGroup($this->arg('id'));
if (empty($this->group)) {
$alias = Group_alias::getKV(
'alias',
common_canonical_nickname($this->arg('id'))
);
if (!empty($alias)) {
$args = array('id' => $alias->group_id, 'format' => $this->format);
common_redirect(common_local_url('ApiGroupShow', $args), 301);
} else {
// TRANS: Client error displayed when trying to show a group that could not be found.
$this->clientError(_('Group not found.'), 404);
}
return;
}
return true;
}
/**
* Handle the request
*
* Check the format and show the user info
*
* @return void
*/
protected function handle()
{
parent::handle();
switch($this->format) {
case 'xml':
$this->showSingleXmlGroup($this->group);
break;
case 'json':
$this->showSingleJsonGroup($this->group);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* When was this group last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->group)) {
return strtotime($this->group->modified);
}
return null;
}
/**
* An entity tag for this group
*
* Returns an Etag based on the action name, language, and
* timestamps of the notice
*
* @return string etag
*/
function etag()
{
if (!empty($this->group)) {
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->group->id,
strtotime($this->group->modified))
)
. '"';
}
return null;
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

102
actions/apihelptest.php Normal file
View File

@ -0,0 +1,102 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Test that you can connect to the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns the string "ok" in the requested format with a 200 OK HTTP status code.
*
* @category API
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiHelpTestAction extends ApiPrivateAuthAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{
parent::prepare($args);
return true;
}
/**
* Handle the request
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
{
parent::handle($args);
if ($this->format == 'xml') {
$this->initDocument('xml');
$this->element('ok', null, 'true');
$this->endDocument('xml');
} elseif ($this->format == 'json') {
$this->initDocument('json');
print '"ok"';
$this->endDocument('json');
} else {
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
404,
$this->format
);
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

240
actions/apilist.php Normal file
View File

@ -0,0 +1,240 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show, update or delete a list.
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
class ApiListAction extends ApiBareAuthAction
{
/**
* The list in question in the current request
*/
var $list = null;
/**
* Is this an update request?
*/
var $update = false;
/**
* Is this a delete request?
*/
var $delete = false;
/**
* Set the flags for handling the request. Show list if this is a GET
* request, update it if it is POST, delete list if method is DELETE
* or if method is POST and an argument _method is set to DELETE. Act
* like we don't know if the current user has no access to the list.
*
* Takes parameters:
* - user: the user id or nickname
* - id: the id of the tag or the tag itself
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->delete = ($_SERVER['REQUEST_METHOD'] == 'DELETE' ||
($this->trimmed('_method') == 'DELETE' &&
$_SERVER['REQUEST_METHOD'] == 'POST'));
// update list if method is POST or PUT and $this->delete is not true
$this->update = (!$this->delete &&
in_array($_SERVER['REQUEST_METHOD'], array('POST', 'PUT')));
$this->user = $this->getTargetUser($this->arg('user'));
$this->list = $this->getTargetList($this->arg('user'), $this->arg('id'));
if (empty($this->list)) {
// TRANS: Client error displayed when referring to a non-existing list.
$this->clientError(_('List not found.'), 404);
}
return true;
}
/**
* Handle the request
*
* @return boolean success flag
*/
protected function handle()
{
parent::handle();
if($this->delete) {
$this->handleDelete();
return true;
}
if($this->update) {
$this->handlePut();
return true;
}
switch($this->format) {
case 'xml':
$this->showSingleXmlList($this->list);
break;
case 'json':
$this->showSingleJsonList($this->list);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* require authentication if it is a write action or user is ambiguous
*
*/
function requiresAuth()
{
return parent::requiresAuth() ||
$this->create || $this->delete;
}
/**
* Update a list
*
* @return boolean success
*/
function handlePut()
{
if($this->auth_user->id != $this->list->tagger) {
// TRANS: Client error displayed when trying to update another user's list.
$this->clientError(_('You cannot update lists that do not belong to you.'), 401);
}
$new_list = clone($this->list);
$new_list->tag = common_canonical_tag($this->arg('name'));
$new_list->description = common_canonical_tag($this->arg('description'));
$new_list->private = ($this->arg('mode') === 'private') ? true : false;
$result = $new_list->update($this->list);
if(!$result) {
// TRANS: Client error displayed when an unknown error occurs updating a list.
$this->clientError(_('An error occured.'), 503);
}
switch($this->format) {
case 'xml':
$this->showSingleXmlList($new_list);
break;
case 'json':
$this->showSingleJsonList($new_list);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Delete a list
*
* @return boolean success
*/
function handleDelete()
{
if($this->auth_user->id != $this->list->tagger) {
// TRANS: Client error displayed when trying to delete another user's list.
$this->clientError(_('You cannot delete lists that do not belong to you.'), 401);
}
$record = clone($this->list);
$this->list->delete();
switch($this->format) {
case 'xml':
$this->showSingleXmlList($record);
break;
case 'json':
$this->showSingleJsonList($record);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Indicate that this resource is not read-only.
*
* @return boolean is_read-only=false
*/
function isReadOnly($args)
{
return false;
}
/**
* When was the list (people tag) last updated?
*
* @return String time_last_modified
*/
function lastModified()
{
if(!empty($this->list)) {
return strtotime($this->list->modified);
}
return null;
}
/**
* An entity tag for this list
*
* Returns an Etag based on the action name, language, user ID and
* timestamps of the first and last list the user has joined
*
* @return string etag
*/
function etag()
{
if (!empty($this->list)) {
return '"' . implode(
':',
array($this->arg('action'),
common_language(),
$this->user->id,
strtotime($this->list->created),
strtotime($this->list->modified))
)
. '"';
}
return null;
}
}

112
actions/apilistmember.php Normal file
View File

@ -0,0 +1,112 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* API method to check if a user belongs to a list.
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Action handler for Twitter list_memeber methods
*
* @category API
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @see ApiBareAuthAction
*/
class ApiListMemberAction extends ApiBareAuthAction
{
/**
* Set the flags for handling the request. Show the profile if this
* is a GET request AND the profile is a member of the list, add a member
* if it is a POST, remove the profile from the list if method is DELETE
* or if method is POST and an argument _method is set to DELETE. Act
* like we don't know if the current user has no access to the list.
*
* Takes parameters:
* - user: the user id or nickname
* - list_id: the id of the tag or the tag itself
* - id: the id of the member being looked for/added/removed
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->target = $this->getTargetProfile($this->arg('id'));
$this->list = $this->getTargetList($this->arg('user'), $this->arg('list_id'));
if (empty($this->list)) {
// TRANS: Client error displayed when referring to a non-existing list.
$this->clientError(_('List not found.'), 404);
}
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed when referring to a non-existing user.
$this->clientError(_('No such user.'), 404);
}
return true;
}
/**
* Handle the request
*
* @return boolean success flag
*/
protected function handle()
{
parent::handle();
$arr = array('tagger' => $this->list->tagger,
'tag' => $this->list->tag,
'tagged' => $this->target->id);
$ptag = Profile_tag::pkeyGet($arr);
if(empty($ptag)) {
// TRANS: Client error displayed when referring to a non-list member.
$this->clientError(_('The specified user is not a member of this list.'));
}
$user = $this->twitterUserArray($this->target, true);
switch($this->format) {
case 'xml':
$this->showTwitterXmlUser($user, 'user', true);
break;
case 'json':
$this->showSingleJsonUser($user);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
return true;
}
}

131
actions/apilistmembers.php Normal file
View File

@ -0,0 +1,131 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* List/add/remove list members.
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR . '/lib/apilistusers.php';
class ApiListMembersAction extends ApiListUsersAction
{
/**
* Add a user to a list (tag someone)
*
* @return boolean success
*/
function handlePost()
{
if($this->auth_user->id != $this->list->tagger) {
// TRANS: Client error displayed when trying to add members to a list without having the right to do so.
$this->clientError(_('You are not allowed to add members to this list.'), 401);
}
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed when trying to modify list members without specifying them.
$this->clientError(_('You must specify a member.'));
}
$result = Profile_tag::setTag($this->auth_user->id,
$this->target->id, $this->list->tag);
if(empty($result)) {
// TRANS: Client error displayed when an unknown error occurs viewing list members.
$this->clientError(_('An error occured.'), 500);
}
switch($this->format) {
case 'xml':
$this->showSingleXmlList($this->list);
break;
case 'json':
$this->showSingleJsonList($this->list);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Remove a user from a list (untag someone)
*
* @return boolean success
*/
function handleDelete()
{
if($this->auth_user->id != $this->list->tagger) {
// TRANS: Client error displayed when trying to remove members from a list without having the right to do so.
$this->clientError(_('You are not allowed to remove members from this list.'), 401);
}
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed when trying to modify list members without specifying them.
$this->clientError(_('You must specify a member.'));
}
$args = array('tagger' => $this->auth_user->id,
'tagged' => $this->target->id,
'tag' => $this->list->tag);
$ptag = Profile_tag::pkeyGet($args);
if (empty($ptag)) {
// TRANS: Client error displayed when trying to remove a list member that is not part of a list.
$this->clientError(_('The user you are trying to remove from the list is not a member.'));
}
if (!$ptag->delete()) {
// TRANS: Client error displayed when an unknown error occurs viewing list members.
$this->clientError(_('An error occured.'), 500);
}
switch($this->format) {
case 'xml':
$this->showSingleXmlList($this->list);
break;
case 'json':
$this->showSingleJsonList($this->list);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
return true;
}
/**
* List the members of a list (people tagged)
*/
function getUsers()
{
$fn = array($this->list, 'getTagged');
list($this->users, $this->next_cursor, $this->prev_cursor) =
Profile_list::getAtCursor($fn, array(), $this->cursor, 20);
}
}

View File

@ -0,0 +1,124 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Get a list of lists a user belongs to. (people tags for a user)
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Action handler for API method to list lists a user belongs to.
* (people tags for a user)
*
* @category API
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @see ApiBareAuthAction
*/
class ApiListMembershipsAction extends ApiBareAuthAction
{
var $lists = array();
var $cursor = -1;
var $next_cursor = 0;
var $prev_cursor = 0;
/**
* Prepare for running the action
* Take arguments for running:s
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->cursor = (int) $this->arg('cursor', -1);
$user = $this->getTargetUser($this->arg('user'));
if (!($user instanceof User)) {
// TRANS: Client error displayed trying to perform an action related to a non-existing user.
$this->clientError(_('No such user.'), 404);
}
$this->target = $user->getProfile();
$this->getLists();
return true;
}
/**
* Handle the request
*
* Show the lists
*
* @return void
*/
protected function handle()
{
parent::handle();
switch($this->format) {
case 'xml':
$this->showXmlLists($this->lists, $this->next_cursor, $this->prev_cursor);
break;
case 'json':
$this->showJsonLists($this->lists, $this->next_cursor, $this->prev_cursor);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'));
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
function getLists()
{
$fn = array($this->target, 'getOtherTags');
# 20 lists
list($this->lists, $this->next_cursor, $this->prev_cursor) =
Profile_list::getAtCursor($fn, array($this->scoped), $this->cursor, 20);
}
}

232
actions/apilists.php Normal file
View File

@ -0,0 +1,232 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* List existing lists or create a new list.
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Action handler for Twitter list_memeber methods
*
* @category API
* @package StatusNet
* @author Shashi Gowda <connect2shashi@gmail.com>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @see ApiBareAuthAction
*/
class ApiListsAction extends ApiBareAuthAction
{
var $lists = null;
var $cursor = 0;
var $next_cursor = 0;
var $prev_cursor = 0;
var $create = false;
/**
* Set the flags for handling the request. List lists created by user if this
* is a GET request, create a new list if it is a POST request.
*
* Takes parameters:
* - user: the user id or nickname
* Parameters for POST request
* - name: name of the new list (the people tag itself)
* - mode: (optional) mode for the new list private/public
* - description: (optional) description for the list
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->create = ($_SERVER['REQUEST_METHOD'] == 'POST');
if (!$this->create) {
$this->user = $this->getTargetUser($this->arg('user'));
if (!($user instanceof User)) {
// TRANS: Client error displayed trying to perform an action related to a non-existing user.
$this->clientError(_('No such user.'), 404);
}
$this->target = $user->getProfile();
$this->getLists();
}
return true;
}
/**
* require authentication if it is a write action or user is ambiguous
*
*/
function requiresAuth()
{
return parent::requiresAuth() ||
$this->create || $this->delete;
}
/**
* Handle request:
* Show the lists the user has created if the request method is GET
* Create a new list by diferring to handlePost() if it is POST.
*/
protected function handle()
{
parent::handle();
if($this->create) {
return $this->handlePost();
}
switch($this->format) {
case 'xml':
$this->showXmlLists($this->lists, $this->next_cursor, $this->prev_cursor);
break;
case 'json':
$this->showJsonLists($this->lists, $this->next_cursor, $this->prev_cursor);
break;
default:
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
404,
$this->format
);
break;
}
}
/**
* Create a new list
*
* @return boolean success
*/
function handlePost()
{
$name=$this->arg('name');
if(empty($name)) {
// mimick twitter
// TRANS: Client error displayed when trying to create a list without a name.
print _("A list must have a name.");
exit(1);
}
// twitter creates a new list by appending a number to the end
// if the list by the given name already exists
// it makes more sense to return the existing list instead
$private = null;
if ($this->arg('mode') === 'public') {
$private = false;
} else if ($this->arg('mode') === 'private') {
$private = true;
}
$list = Profile_list::ensureTag($this->auth_user->id,
$this->arg('name'),
$this->arg('description'),
$private);
if (empty($list)) {
return false;
}
switch($this->format) {
case 'xml':
$this->showSingleXmlList($list);
break;
case 'json':
$this->showSingleJsonList($list);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
return true;
}
/**
* Get lists
*/
function getLists()
{
$cursor = (int) $this->arg('cursor', -1);
// twitter fixes count at 20
// there is no argument named count
$count = 20;
$fn = array($this->target, 'getLists');
list($this->lists,
$this->next_cursor,
$this->prev_cursor) = Profile_list::getAtCursor($fn, array($this->scoped), $cursor, $count);
}
function isReadOnly($args)
{
return false;
}
function lastModified()
{
if (!$this->create && !empty($this->lists) && (count($this->lists) > 0)) {
return strtotime($this->lists[0]->created);
}
return null;
}
/**
* An entity tag for this list of lists
*
* Returns an Etag based on the action name, language, user ID and
* timestamps of the first and last list the user has joined
*
* @return string etag
*/
function etag()
{
if (!$this->create && !empty($this->lists) && (count($this->lists) > 0)) {
$last = count($this->lists) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_language(),
$this->target->id,
strtotime($this->lists[0]->created),
strtotime($this->lists[$last]->created))
)
. '"';
}
return null;
}
}

View File

@ -0,0 +1,87 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Check if a user is subscribed to a list
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
class ApiListSubscriberAction extends ApiBareAuthAction
{
var $list = null;
function prepare($args)
{
parent::prepare($args);
$this->target = $this->getTargetProfile($this->arg('id'));
$this->list = $this->getTargetList($this->arg('user'), $this->arg('list_id'));
if (empty($this->list)) {
// TRANS: Client error displayed trying to perform an action related to a non-existing list.
$this->clientError(_('List not found.'), 404);
}
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed trying to perform an action related to a non-existing user.
$this->clientError(_('No such user.'), 404);
}
return true;
}
function handle($args)
{
parent::handle($args);
$arr = array('profile_tag_id' => $this->list->id,
'profile_id' => $this->target->id);
$sub = Profile_tag_subscription::pkeyGet($arr);
if(empty($sub)) {
// TRANS: Client error displayed when a membership check for a user is nagative.
$this->clientError(_('The specified user is not a subscriber of this list.'));
}
$user = $this->twitterUserArray($this->target, true);
switch($this->format) {
case 'xml':
$this->showTwitterXmlUser($user, 'user', true);
break;
case 'json':
$this->showSingleJsonUser($user);
break;
default:
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
404,
$this->format
);
break;
}
}
}

View File

@ -0,0 +1,102 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show/add/remove list subscribers.
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR . '/lib/apilistusers.php';
class ApiListSubscribersAction extends ApiListUsersAction
{
/**
* Subscribe to list
*
* @return boolean success
*/
function handlePost()
{
$result = Profile_tag_subscription::add($this->list,
$this->auth_user);
if(empty($result)) {
// TRANS: Client error displayed when an unknown error occurs in the list subscribers action.
$this->clientError(_('An error occured.'), 500);
}
switch($this->format) {
case 'xml':
$this->showSingleXmlList($this->list);
break;
case 'json':
$this->showSingleJsonList($this->list);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
function handleDelete()
{
$args = array('profile_tag_id' => $this->list->id,
'profile_id' => $this->auth_user->id);
$ptag = Profile_tag_subscription::pkeyGet($args);
if(empty($ptag)) {
// TRANS: Client error displayed when trying to unsubscribe from a non-subscribed list.
$this->clientError(_('You are not subscribed to this list.'));
}
$result = Profile_tag_subscription::remove($this->list, $this->auth_user);
if (empty($result)) {
// TRANS: Client error displayed when an unknown error occurs unsubscribing from a list.
$this->clientError(_('An error occured.'), 500);
}
switch($this->format) {
case 'xml':
$this->showSingleXmlList($this->list);
break;
case 'json':
$this->showSingleJsonList($this->list);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
return true;
}
function getUsers()
{
$fn = array($this->list, 'getSubscribers');
list($this->users, $this->next_cursor, $this->prev_cursor) =
Profile_list::getAtCursor($fn, array(), $this->cursor, 20);
}
}

View File

@ -0,0 +1,110 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Get a list of lists a user is subscribed to.
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
class ApiListSubscriptionsAction extends ApiBareAuthAction
{
var $lists = array();
var $cursor = -1;
var $next_cursor = 0;
var $prev_cursor = 0;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->cursor = (int) $this->arg('cursor', -1);
$user = $this->getTargetUser($this->arg('user'));
if (!($user instanceof User)) {
// TRANS: Client error displayed trying to perform an action related to a non-existing user.
$this->clientError(_('No such user.'), 404);
}
$this->target = $user->getProfile();
$this->getLists();
return true;
}
/**
* Handle the request
*
* Show the lists
*
* @return void
*/
protected function handle()
{
parent::handle();
switch($this->format) {
case 'xml':
$this->showXmlLists($this->lists, $this->next_cursor, $this->prev_cursor);
break;
case 'json':
$this->showJsonLists($this->lists, $this->next_cursor, $this->prev_cursor);
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'));
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
function getLists()
{
$fn = array($this->target, 'getTagSubscriptions');
# 20 lists
list($this->lists, $this->next_cursor, $this->prev_cursor) =
Profile_list::getAtCursor($fn, array(), $this->cursor, 20);
}
}

195
actions/apimediaupload.php Normal file
View File

@ -0,0 +1,195 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Upload an image via the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Upload an image via the API. Returns a shortened URL for the image
* to the user. Apparently modelled after a former Twitpic API.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiMediaUploadAction extends ApiAuthAction
{
protected $needPost = true;
protected function prepare(array $args=array())
{
parent::prepare($args);
// fallback to xml for older clients etc
if (empty($this->format)) {
$this->format = 'xml';
}
if (!in_array($this->format, ['json', 'xml'])) {
throw new ClientException('This API call does not support the format '._ve($this->format));
}
return true;
}
protected function handle()
{
parent::handle();
// Workaround for PHP returning empty $_POST and $_FILES when POST
// length > post_max_size in php.ini
if (empty($_FILES)
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
throw new ClientException(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
}
try {
$upload = MediaFile::fromUpload('media', $this->scoped);
} catch (NoUploadedMediaException $e) {
common_debug('No media file was uploaded to the _FILES array');
$fh = tmpfile();
if ($this->arg('media')) {
common_debug('Found media parameter which we hope contains a media file!');
fwrite($fh, $this->arg('media'));
} elseif ($this->arg('media_data')) {
common_debug('Found media_data parameter which we hope contains a base64-encoded media file!');
fwrite($fh, base64_decode($this->arg('media_data')));
} else {
common_debug('No media|media_data POST parameter was supplied');
fclose($fh);
throw $e;
}
common_debug('MediaFile importing the uploaded file with fromFilehandle');
$upload = MediaFile::fromFilehandle($fh, $this->scoped);
}
common_debug('MediaFile completed and saved us fileRecord with id=='._ve($upload->fileRecord->id));
// Thumbnails will be generated/cached on demand when accessed (such as with /attachment/:id/thumbnail)
$this->showResponse($upload);
}
/**
* Show a Twitpic-like response with the ID of the media file
* and a (hopefully) shortened URL for it.
*
* @param MediaFile $upload the uploaded file
*
* @return void
*/
protected function showResponse(MediaFile $upload)
{
$this->initDocument($this->format);
switch ($this->format) {
case 'json':
return $this->showResponseJson($upload);
case 'xml':
return $this->showResponseXml($upload);
default:
throw new ClientException('This API call does not support the format '._ve($this->format));
}
$this->endDocument($this->format);
}
protected function showResponseJson(MediaFile $upload)
{
$enc = $upload->fileRecord->getEnclosure();
// note that we use media_id instead of mediaid which XML users might've gotten used to (nowadays we service media_id in both!)
$output = [
'media_id' => $upload->fileRecord->id,
'media_id_string' => (string)$upload->fileRecord->id,
'media_url' => $upload->shortUrl(),
'size' => $upload->fileRecord->size,
];
if (common_get_mime_media($enc->mimetype) === 'image') {
$output['image'] = [
'w' => $enc->width,
'h' => $enc->height,
'image_type' => $enc->mimetype,
];
}
print json_encode($output);
}
protected function showResponseXml(MediaFile $upload)
{
$this->elementStart('rsp', array('stat' => 'ok', 'xmlns:atom'=>Activity::ATOM));
$this->element('mediaid', null, $upload->fileRecord->id);
$this->element('mediaurl', null, $upload->shortUrl());
$this->element('media_url', null, $upload->shortUrl());
$this->element('size', null, $upload->fileRecord->size);
$enclosure = $upload->fileRecord->getEnclosure();
$this->element('atom:link', array('rel' => 'enclosure',
'href' => $enclosure->url,
'type' => $enclosure->mimetype));
// Twitter specific metadata expected in response since Twitter's Media upload API v1.1 (even though Twitter doesn't use XML)
$this->element('media_id', null, $upload->fileRecord->id);
$this->element('media_id_string', null, (string)$upload->fileRecord->id);
if (common_get_mime_media($enclosure->mimetype) === 'image') {
$this->element('image', ['w'=>$enclosure->width, 'h'=>$enclosure->height, 'image_type'=>$enclosure->mimetype]);
}
$this->elementEnd('rsp');
}
/**
* Overrided clientError to show a more Twitpic-like error
*
* @param String $msg an error message
*/
function clientError($msg, $code=400, $format=null)
{
$this->initDocument($this->format);
switch ($this->format) {
case 'json':
$error = ['errors' => array()];
$error['errors'][] = ['message'=>$msg, 'code'=>131];
print json_encode($error);
break;
case 'xml':
$this->elementStart('rsp', array('stat' => 'fail'));
// @todo add in error code
$errAttr = array('msg' => $msg);
$this->element('err', $errAttr, null);
$this->elementEnd('rsp');
break;
}
$this->endDocument($this->format);
exit;
}
}

View File

@ -0,0 +1,122 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Action for getting OAuth token credentials (exchange an authorized
* request token for an access token)
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Action for getting OAuth token credentials (exchange an authorized
* request token for an access token)
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiOAuthAccessTokenAction extends ApiOAuthAction
{
protected $reqToken = null;
protected $verifier = null;
/**
* Class handler.
*
* @param array $args array of arguments
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$datastore = new ApiGNUsocialOAuthDataStore();
$server = new OAuthServer($datastore);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$server->add_signature_method($hmac_method);
$atok = $app = null;
// XXX: Insist that oauth_token and oauth_verifier be populated?
// Spec doesn't say they MUST be.
try {
$req = OAuthRequest::from_request();
$this->reqToken = $req->get_parameter('oauth_token');
$this->verifier = $req->get_parameter('oauth_verifier');
$app = $datastore->getAppByRequestToken($this->reqToken);
$atok = $server->fetch_access_token($req);
} catch (Exception $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
common_debug(var_export($req, true));
$code = $e->getCode();
$this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text');
}
if (empty($atok)) {
// Token exchange failed -- log it
$msg = sprintf(
'API OAuth - Failure exchanging OAuth request token for access token, '
. 'request token = %s, verifier = %s',
$this->reqToken,
$this->verifier
);
common_log(LOG_WARNING, $msg);
// TRANS: Client error given from the OAuth API when the request token or verifier is invalid.
$this->clientError(_('Invalid request token or verifier.'), 400, 'text');
} else {
common_log(
LOG_INFO,
sprintf(
"Issued access token '%s' for application %d (%s).",
$atok->key,
$app->id,
$app->name
)
);
$this->showAccessToken($atok);
}
}
/*
* Display OAuth token credentials
*
* @param OAuthToken token the access token
*/
function showAccessToken($token)
{
header('Content-Type: application/x-www-form-urlencoded');
print $token;
}
}

View File

@ -0,0 +1,707 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Authorize an OAuth request token
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010-2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Authorize an OAuth request token
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiOAuthAuthorizeAction extends ApiOAuthAction
{
var $oauthTokenParam;
var $reqToken;
var $callback;
var $app;
var $nickname;
var $password;
var $store;
/**
* Is this a read-only action?
*
* @return boolean false
*/
function isReadOnly($args)
{
return false;
}
function prepare($args)
{
parent::prepare($args);
$this->nickname = $this->trimmed('nickname');
$this->password = $this->arg('password');
$this->oauthTokenParam = $this->arg('oauth_token');
$this->mode = $this->arg('mode');
$this->store = new ApiGNUsocialOAuthDataStore();
try {
$this->app = $this->store->getAppByRequestToken($this->oauthTokenParam);
} catch (Exception $e) {
$this->clientError($e->getMessage());
}
return true;
}
/**
* Handle input, produce output
*
* Switches on request method; either shows the form or handles its input.
*
* @param array $args $_REQUEST data
*
* @return void
*/
function handle($args)
{
parent::handle($args);
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePost();
} else {
// Make sure a oauth_token parameter was provided
if (empty($this->oauthTokenParam)) {
// TRANS: Client error given when no oauth_token was passed to the OAuth API.
$this->clientError(_('No oauth_token parameter provided.'));
} else {
// Check to make sure the token exists
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
if (empty($this->reqToken)) {
// TRANS: Client error given when an invalid request token was passed to the OAuth API.
$this->clientError(_('Invalid request token.'));
} else {
// Check to make sure we haven't already authorized the token
if ($this->reqToken->state != 0) {
// TRANS: Client error given when an invalid request token was passed to the OAuth API.
$this->clientError(_('Request token already authorized.'));
}
}
}
// make sure there's an app associated with this token
if (empty($this->app)) {
// TRANS: Client error given when an invalid request token was passed to the OAuth API.
$this->clientError(_('Invalid request token.'));
}
$name = $this->app->name;
$this->showForm();
}
}
function handlePost()
{
// check session token for CSRF protection.
$token = $this->trimmed('token');
if (!$token || $token != common_session_token()) {
$this->showForm(
// TRANS: Form validation error in API OAuth authorisation because of an invalid session token.
_('There was a problem with your session token. Try again, please.'));
return;
}
// check creds
$user = null;
if (!common_logged_in()) {
// XXX Force credentials check?
// @fixme this should probably use a unified login form handler
$user = null;
if (Event::handle('StartOAuthLoginCheck', array($this, &$user))) {
$user = common_check_user($this->nickname, $this->password);
}
Event::handle('EndOAuthLoginCheck', array($this, &$user));
if (empty($user)) {
// TRANS: Form validation error given when an invalid username and/or password was passed to the OAuth API.
$this->showForm(_("Invalid nickname / password!"));
return;
}
} else {
$user = common_current_user();
}
// fetch the token
$this->reqToken = $this->store->getTokenByKey($this->oauthTokenParam);
assert(!empty($this->reqToken));
if ($this->arg('allow')) {
// mark the req token as authorized
try {
$this->store->authorize_token($this->oauthTokenParam);
} catch (Exception $e) {
$this->serverError($e->getMessage());
}
common_log(
LOG_INFO,
sprintf(
"API OAuth - User %d (%s) has authorized request token %s for OAuth application %d (%s).",
$user->id,
$user->nickname,
$this->reqToken->tok,
$this->app->id,
$this->app->name
)
);
$tokenAssoc = new Oauth_token_association();
$tokenAssoc->profile_id = $user->id;
$tokenAssoc->application_id = $this->app->id;
$tokenAssoc->token = $this->oauthTokenParam;
$tokenAssoc->created = common_sql_now();
$result = $tokenAssoc->insert();
if (!$result) {
common_log_db_error($tokenAssoc, 'INSERT', __FILE__);
// TRANS: Server error displayed when a database action fails.
$this->serverError(_('Database error inserting oauth_token_association.'));
}
$callback = $this->getCallback();
if (!empty($callback) && $this->reqToken->verified_callback != 'oob') {
$targetUrl = $this->buildCallbackUrl(
$callback,
array(
'oauth_token' => $this->oauthTokenParam,
'oauth_verifier' => $this->reqToken->verifier // 1.0a
)
);
common_log(LOG_INFO, "Redirecting to callback: $targetUrl");
// Redirect the user to the provided OAuth callback
common_redirect($targetUrl, 303);
} elseif ($this->app->type == 2) {
// Strangely, a web application seems to want to do the OOB
// workflow. Because no callback was specified anywhere.
common_log(
LOG_WARNING,
sprintf(
"API OAuth - No callback provided for OAuth web client ID %s (%s) "
. "during authorization step. Falling back to OOB workflow.",
$this->app->id,
$this->app->name
)
);
}
// Otherwise, inform the user that the rt was authorized
$this->showAuthorized();
} else if ($this->arg('cancel')) {
common_log(
LOG_INFO,
sprintf(
"API OAuth - User %d (%s) refused to authorize request token %s for OAuth application %d (%s).",
$user->id,
$user->nickname,
$this->reqToken->tok,
$this->app->id,
$this->app->name
)
);
try {
$this->store->revoke_token($this->oauthTokenParam, 0);
} catch (Exception $e) {
$this->ServerError($e->getMessage());
}
$callback = $this->getCallback();
// If there's a callback available, inform the consumer the user
// has refused authorization
if (!empty($callback) && $this->reqToken->verified_callback != 'oob') {
$targetUrl = $this->buildCallbackUrl(
$callback,
array(
'oauth_problem' => 'user_refused',
)
);
common_log(LOG_INFO, "Redirecting to callback: $targetUrl");
// Redirect the user to the provided OAuth callback
common_redirect($targetUrl, 303);
}
// otherwise inform the user that authorization for the rt was declined
$this->showCanceled();
} else {
// TRANS: Client error given on when invalid data was passed through a form in the OAuth API.
$this->clientError(_('Unexpected form submission.'));
}
}
/**
* Show body - override to add a special CSS class for the authorize
* page's "desktop mode" (minimal display)
*
* Calls template methods
*
* @return nothing
*/
function showBody()
{
$bodyClasses = array();
if ($this->desktopMode()) {
$bodyClasses[] = 'oauth-desktop-mode';
}
if (common_current_user()) {
$bodyClasses[] = 'user_in';
}
$attrs = array('id' => strtolower($this->trimmed('action')));
if (!empty($bodyClasses)) {
$attrs['class'] = implode(' ', $bodyClasses);
}
$this->elementStart('body', $attrs);
$this->elementStart('div', array('id' => 'wrap'));
if (Event::handle('StartShowHeader', array($this))) {
$this->showHeader();
Event::handle('EndShowHeader', array($this));
}
$this->showCore();
if (Event::handle('StartShowFooter', array($this))) {
$this->showFooter();
Event::handle('EndShowFooter', array($this));
}
$this->elementEnd('div');
$this->showScripts();
$this->elementEnd('body');
}
function showForm($error=null)
{
$this->error = $error;
$this->showPage();
}
function showScripts()
{
parent::showScripts();
if (!common_logged_in()) {
$this->autofocus('nickname');
}
}
/**
* Title of the page
*
* @return string title of the page
*/
function title()
{
// TRANS: Title for a page where a user can confirm/deny account access by an external application.
return _('An application would like to connect to your account');
}
/**
* Shows the authorization form.
*
* @return void
*/
function showContent()
{
$this->elementStart('form', array('method' => 'post',
'id' => 'form_apioauthauthorize',
'class' => 'form_settings',
'action' => common_local_url('ApiOAuthAuthorize')));
$this->elementStart('fieldset');
$this->element('legend', array('id' => 'apioauthauthorize_allowdeny'),
// TRANS: Fieldset legend.
_('Allow or deny access'));
$this->hidden('token', common_session_token());
$this->hidden('mode', $this->mode);
$this->hidden('oauth_token', $this->oauthTokenParam);
$this->hidden('oauth_callback', $this->callback);
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
$this->elementStart('p');
if (!empty($this->app->icon) && $this->app->name != 'anonymous') {
$this->element('img', array('src' => $this->app->icon));
}
$access = ($this->app->access_type & Oauth_application::$writeAccess) ?
'access and update' : 'access';
if ($this->app->name == 'anonymous') {
// Special message for the anonymous app and consumer.
// TRANS: User notification of external application requesting account access.
// TRANS: %3$s is the access type requested (read-write or read-only), %4$s is the StatusNet sitename.
$msg = _('An application would like the ability ' .
'to <strong>%3$s</strong> your %4$s account data. ' .
'You should only give access to your %4$s account ' .
'to third parties you trust.');
} else {
// TRANS: User notification of external application requesting account access.
// TRANS: %1$s is the application name requesting access, %2$s is the organisation behind the application,
// TRANS: %3$s is the access type requested, %4$s is the StatusNet sitename.
$msg = _('The application <strong>%1$s</strong> by ' .
'<strong>%2$s</strong> would like the ability ' .
'to <strong>%3$s</strong> your %4$s account data. ' .
'You should only give access to your %4$s account ' .
'to third parties you trust.');
}
$this->raw(sprintf($msg,
$this->app->name,
$this->app->organization,
$access,
common_config('site', 'name')));
$this->elementEnd('p');
$this->elementEnd('li');
$this->elementEnd('ul');
// quickie hack
$button = false;
if (!common_logged_in()) {
if (Event::handle('StartOAuthLoginForm', array($this, &$button))) {
$this->elementStart('fieldset');
// TRANS: Fieldset legend.
$this->element('legend', null, _m('LEGEND','Account'));
$this->elementStart('ul', 'form_data');
$this->elementStart('li');
// TRANS: Field label on OAuth API authorisation form.
$this->input('nickname', _('Nickname'));
$this->elementEnd('li');
$this->elementStart('li');
// TRANS: Field label on OAuth API authorisation form.
$this->password('password', _('Password'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
}
Event::handle('EndOAuthLoginForm', array($this, &$button));
}
$this->element('input', array('id' => 'cancel_submit',
'class' => 'submit submit form_action-primary',
'name' => 'cancel',
'type' => 'submit',
// TRANS: Button text that when clicked will cancel the process of allowing access to an account
// TRANS: by an external application.
'value' => _m('BUTTON','Cancel')));
$this->element('input', array('id' => 'allow_submit',
'class' => 'submit submit form_action-secondary',
'name' => 'allow',
'type' => 'submit',
// TRANS: Button text that when clicked will allow access to an account by an external application.
'value' => $button ? $button : _m('BUTTON','Allow')));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
/**
* Instructions for using the form
*
* For "remembered" logins, we make the user re-login when they
* try to change settings. Different instructions for this case.
*
* @return void
*/
function getInstructions()
{
// TRANS: Form instructions.
return _('Authorize access to your account information.');
}
/**
* A local menu
*
* Shows different login/register actions.
*
* @return void
*/
function showLocalNav()
{
// NOP
}
/*
* Checks to see if a the "mode" parameter is present in the request
* and set to "desktop". If it is, the page is meant to be displayed in
* a small frame of another application, and we should suppress the
* header, aside, and footer.
*/
function desktopMode()
{
if (isset($this->mode) && $this->mode == 'desktop') {
return true;
} else {
return false;
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showHeader()
{
if ($this->desktopMode() == false) {
parent::showHeader();
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showAside()
{
if ($this->desktopMode() == false) {
parent::showAside();
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showFooter()
{
if ($this->desktopMode() == false) {
parent::showFooter();
}
}
/**
* Show site notice.
*
* @return nothing
*/
function showSiteNotice()
{
// NOP
}
/**
* Show notice form.
*
* Show the form for posting a new notice
*
* @return nothing
*/
function showNoticeForm()
{
// NOP
}
/*
* Show a nice message confirming the authorization
* operation was canceled.
*
* @return nothing
*/
function showCanceled()
{
$info = new InfoAction(
// TRANS: Header for user notification after revoking OAuth access to an application.
_('Authorization canceled.'),
sprintf(
// TRANS: User notification after revoking OAuth access to an application.
// TRANS: %s is an OAuth token.
_('The request token %s has been revoked.'),
$this->oauthTokenParam
)
);
$info->showPage();
}
/*
* Show a nice message that the authorization was successful.
* If the operation is out-of-band, show a pin.
*
* @return nothing
*/
function showAuthorized()
{
$title = null;
$msg = null;
if ($this->app->name == 'anonymous') {
$title =
// TRANS: Title of the page notifying the user that an anonymous client application was successfully authorized to access the user's account with OAuth.
_('You have successfully authorized the application');
$msg =
// TRANS: Message notifying the user that an anonymous client application was successfully authorized to access the user's account with OAuth.
_('Please return to the application and enter the following security code to complete the process.');
} else {
$title = sprintf(
// TRANS: Title of the page notifying the user that the client application was successfully authorized to access the user's account with OAuth.
// TRANS: %s is the authorised application name.
_('You have successfully authorized %s'),
$this->app->name
);
$msg = sprintf(
// TRANS: Message notifying the user that the client application was successfully authorized to access the user's account with OAuth.
// TRANS: %s is the authorised application name.
_('Please return to %s and enter the following security code to complete the process.'),
$this->app->name
);
}
if ($this->reqToken->verified_callback == 'oob') {
$pin = new ApiOAuthPinAction(
$title,
$msg,
$this->reqToken->verifier,
$this->desktopMode()
);
$pin->showPage();
} else {
// NOTE: This would only happen if an application registered as
// a web application but sent in 'oob' for the oauth_callback
// parameter. Usually web apps will send in a callback and
// not use the pin-based workflow.
$info = new InfoAction(
$title,
$msg,
$this->oauthTokenParam,
$this->reqToken->verifier
);
$info->showPage();
}
}
/*
* Figure out what the callback should be
*/
function getCallback()
{
$callback = null;
// Return the verified callback if we have one
if ($this->reqToken->verified_callback != 'oob') {
$callback = $this->reqToken->verified_callback;
// Otherwise return the callback that was provided when
// registering the app
if (empty($callback)) {
common_debug(
"No verified callback found for request token, using application callback: "
. $this->app->callback_url,
__FILE__
);
$callback = $this->app->callback_url;
}
}
return $callback;
}
/*
* Properly format the callback URL and parameters so it's
* suitable for a redirect in the OAuth dance
*
* @param string $url the URL
* @param array $params an array of parameters
*
* @return string $url a URL to use for redirecting to
*/
function buildCallbackUrl($url, $params)
{
foreach ($params as $k => $v) {
$url = $this->appendQueryVar(
$url,
OAuthUtil::urlencode_rfc3986($k),
OAuthUtil::urlencode_rfc3986($v)
);
}
return $url;
}
/*
* Append a new query parameter after any existing query
* parameters.
*
* @param string $url the URL
* @prarm string $k the parameter name
* @param string $v value of the paramter
*
* @return string $url the new URL with added parameter
*/
function appendQueryVar($url, $k, $v) {
$url = preg_replace('/(.*)(\?|&)' . $k . '=[^&]+?(&)(.*)/i', '$1$2$4', $url . '&');
$url = substr($url, 0, -1);
if (strpos($url, '?') === false) {
return ($url . '?' . $k . '=' . $v);
} else {
return ($url . '&' . $k . '=' . $v);
}
}
}

172
actions/apioauthpin.php Normal file
View File

@ -0,0 +1,172 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Action for displaying an OAuth verifier pin
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Class for displaying an OAuth verifier pin
*
* XXX: I'm pretty sure we don't need to check the logged in state here. -- Zach
*
* @category Action
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiOAuthPinAction extends InfoAction
{
function __construct($title, $message, $verifier, $desktopMode = false)
{
$this->verifier = $verifier;
$this->title = $title;
$this->desktopMode = $desktopMode;
parent::__construct($title, $message);
}
/**
* Show body - override to add a special CSS class for the pin pages's
* "desktop mode" (minimal display)
*
* Calls template methods
*
* @return nothing
*/
function showBody()
{
$bodyClasses = array();
if ($this->desktopMode) {
$bodyClasses[] = 'oauth-desktop-mode';
}
if (common_current_user()) {
$bodyClasses[] = 'user_in';
}
$attrs = array('id' => strtolower($this->trimmed('action')));
if (!empty($bodyClasses)) {
$attrs['class'] = implode(' ', $bodyClasses);
}
$this->elementStart('body', $attrs);
$this->elementStart('div', array('id' => 'wrap'));
if (Event::handle('StartShowHeader', array($this))) {
$this->showHeader();
Event::handle('EndShowHeader', array($this));
}
$this->showCore();
if (Event::handle('StartShowFooter', array($this))) {
$this->showFooter();
Event::handle('EndShowFooter', array($this));
}
$this->elementEnd('div');
$this->showScripts();
$this->elementEnd('body');
}
/**
* A local menu
*
* Shows different login/register actions.
*
* @return void
*/
function showLocalNav()
{
// NOP
}
/*
* Override - suppress output in "desktop" mode
*/
function showHeader()
{
if ($this->desktopMode == false) {
parent::showHeader();
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showAside()
{
if ($this->desktopMode == false) {
parent::showAside();
}
}
/*
* Override - suppress output in "desktop" mode
*/
function showFooter()
{
if ($this->desktopMode == false) {
parent::showFooter();
}
}
/**
* Show site notice.
*
* @return nothing
*/
function showSiteNotice()
{
// NOP
}
/**
* Show notice form.
*
* Show the form for posting a new notice
*
* @return nothing
*/
function showNoticeForm()
{
// NOP
}
/**
* Display content.
*
* @return nothing
*/
function showContent()
{
$this->element('div', array('class' => 'info'), $this->message);
$this->element('div', array('id' => 'oauth_pin'), $this->verifier);
}
}

View File

@ -0,0 +1,152 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Issue temporary OAuth credentials (a request token)
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Issue temporary OAuth credentials (a request token)
*
* @category API
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiOAuthRequestTokenAction extends ApiOAuthAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{
parent::prepare($args);
// XXX: support "force_login" parameter like Twitter? (Forces the user to enter
// their credentials to ensure the correct users account is authorized.)
return true;
}
/**
* Handle a request for temporary OAuth credentials
*
* Make sure the request is kosher, then emit a set of temporary
* credentials -- AKA an unauthorized request token.
*
* @param array $args array of arguments
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$datastore = new ApiGNUsocialOAuthDataStore();
$server = new OAuthServer($datastore);
$hmac_method = new OAuthSignatureMethod_HMAC_SHA1();
$server->add_signature_method($hmac_method);
try {
$req = OAuthRequest::from_request();
// verify callback
if (!$this->verifyCallback($req->get_parameter('oauth_callback'))) {
throw new OAuthException(
"You must provide a valid URL or 'oob' in oauth_callback.",
400
);
}
// check signature and issue a new request token
$token = $server->fetch_request_token($req);
common_log(
LOG_INFO,
sprintf(
"API OAuth - Issued request token %s for consumer %s with oauth_callback %s",
$token->key,
$req->get_parameter('oauth_consumer_key'),
"'" . $req->get_parameter('oauth_callback') ."'"
)
);
// return token to the client
$this->showRequestToken($token);
} catch (OAuthException $e) {
common_log(LOG_WARNING, 'API OAuthException - ' . $e->getMessage());
// Return 401 for for bad credentials or signature problems,
// and 400 for missing or unsupported parameters
$code = $e->getCode();
$this->clientError($e->getMessage(), empty($code) ? 401 : $code, 'text');
}
}
/*
* Display temporary OAuth credentials
*/
function showRequestToken($token)
{
header('Content-Type: application/x-www-form-urlencoded');
print $token;
print '&oauth_callback_confirmed=true';
}
/* Make sure the callback parameter contains either a real URL
* or the string 'oob'.
*
* @todo Check for evil/banned URLs here
*
* @return boolean true or false
*/
function verifyCallback($callback)
{
if ($callback == "oob") {
common_debug("OAuth request token requested for out of band client.");
// XXX: Should we throw an error if a client is registered as a
// web application but requests the pin based workflow? For now I'm
// allowing the workflow to proceed and issuing a pin. --Zach
return true;
} else {
return filter_var($callback, FILTER_VALIDATE_URL);
}
}
}

392
actions/apisearchatom.php Normal file
View File

@ -0,0 +1,392 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Action for showing Twitter-like Atom search results
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Search
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Action for outputting search results in Twitter compatible Atom
* format.
*
* TODO: abstract Atom stuff into a ruseable base class like
* RSS10Action.
*
* @category Search
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*
* @see ApiPrivateAuthAction
*/
class ApiSearchAtomAction extends ApiPrivateAuthAction
{
var $cnt;
var $query;
var $lang;
var $rpp;
var $page;
var $since_id;
var $geocode;
/**
* Constructor
*
* Just wraps the Action constructor.
*
* @param string $output URI to output to, default = stdout
* @param boolean $indent Whether to indent output, default true
*
* @see Action::__construct
*/
function __construct($output='php://output', $indent=null)
{
parent::__construct($output, $indent);
}
/**
* Do we need to write to the database?
*
* @return boolean true
*/
function isReadonly()
{
return true;
}
/**
* Read arguments and initialize members
*
* @param array $args Arguments from $_REQUEST
*
* @return boolean success
*/
function prepare($args)
{
parent::prepare($args);
$this->query = $this->trimmed('q');
$this->lang = $this->trimmed('lang');
$this->rpp = $this->trimmed('rpp');
if (!$this->rpp) {
$this->rpp = 15;
}
if ($this->rpp > 100) {
$this->rpp = 100;
}
$this->page = $this->trimmed('page');
if (!$this->page) {
$this->page = 1;
}
// TODO: Suppport max_id -- we need to tweak the backend
// Search classes to support it.
$this->since_id = $this->trimmed('since_id');
$this->geocode = $this->trimmed('geocode');
// TODO: Also, language and geocode
return true;
}
/**
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
function handle($args)
{
parent::handle($args);
common_debug("In apisearchatom handle()");
$this->showAtom();
}
/**
* Get the notices to output as results. This also sets some class
* attrs so we can use them to calculate pagination, and output
* since_id and max_id.
*
* @return array an array of Notice objects sorted in reverse chron
*/
function getNotices()
{
// TODO: Support search operators like from: and to:, boolean, etc.
$notices = array();
$notice = new Notice();
// lcase it for comparison
$q = strtolower($this->query);
$search_engine = $notice->getSearchEngine('notice');
$search_engine->set_sort_mode('chron');
$search_engine->limit(($this->page - 1) * $this->rpp,
$this->rpp + 1, true);
if (false === $search_engine->query($q)) {
$this->cnt = 0;
} else {
$this->cnt = $notice->find();
}
$cnt = 0;
$this->max_id = 0;
if ($this->cnt > 0) {
while ($notice->fetch()) {
++$cnt;
if (!$this->max_id) {
$this->max_id = $notice->id;
}
if ($this->since_id && $notice->id <= $this->since_id) {
break;
}
if ($cnt > $this->rpp) {
break;
}
$notices[] = clone($notice);
}
}
return $notices;
}
/**
* Output search results as an Atom feed
*
* @return void
*/
function showAtom()
{
$notices = $this->getNotices();
$this->initAtom();
$this->showFeed();
foreach ($notices as $n) {
$profile = $n->getProfile();
// Don't show notices from deleted users
if (!empty($profile)) {
$this->showEntry($n);
}
}
$this->endAtom();
}
/**
* Show feed specific Atom elements
*
* @return void
*/
function showFeed()
{
// TODO: A9 OpenSearch stuff like search.twitter.com?
$server = common_config('site', 'server');
$sitename = common_config('site', 'name');
// XXX: Use xmlns:statusnet instead?
$this->elementStart('feed',
array('xmlns' => 'http://www.w3.org/2005/Atom',
// XXX: xmlns:twitter causes Atom validation to fail
// It's used for the source attr on notices
'xmlns:twitter' => 'http://api.twitter.com/',
'xml:lang' => 'en-US')); // XXX Other locales ?
$taguribase = TagURI::base();
$this->element('id', null, "tag:$taguribase:search/$server");
$site_uri = common_path(false);
$search_uri = $site_uri . 'api/search.atom?q=' . urlencode($this->query);
if ($this->rpp != 15) {
$search_uri .= '&rpp=' . $this->rpp;
}
// FIXME: this alternate link is not quite right because our
// web-based notice search doesn't support a rpp (responses per
// page) param yet
$this->element('link', array('type' => 'text/html',
'rel' => 'alternate',
'href' => $site_uri . 'search/notice?q=' .
urlencode($this->query)));
// self link
$self_uri = $search_uri;
$self_uri .= ($this->page > 1) ? '&page=' . $this->page : '';
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'self',
'href' => $self_uri));
// @todo Needs i18n?
$this->element('title', null, "$this->query - $sitename Search");
$this->element('updated', null, common_date_iso8601('now'));
// XXX: The below "rel" links are not valid Atom, but it's what
// Twitter does...
// refresh link
$refresh_uri = $search_uri . "&since_id=" . $this->max_id;
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'refresh',
'href' => $refresh_uri));
// pagination links
if ($this->cnt > $this->rpp) {
$next_uri = $search_uri . "&max_id=" . $this->max_id .
'&page=' . ($this->page + 1);
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'next',
'href' => $next_uri));
}
if ($this->page > 1) {
$previous_uri = $search_uri . "&max_id=" . $this->max_id .
'&page=' . ($this->page - 1);
$this->element('link', array('type' => 'application/atom+xml',
'rel' => 'previous',
'href' => $previous_uri));
}
}
/**
* Build an Atom entry similar to search.twitter.com's based on
* a given notice
*
* @param Notice $notice the notice to use
*
* @return void
*/
function showEntry($notice)
{
$server = common_config('site', 'server');
$profile = $notice->getProfile();
$nurl = common_local_url('shownotice', array('notice' => $notice->id));
$this->elementStart('entry');
$taguribase = TagURI::base();
$this->element('id', null, "tag:$taguribase:$notice->id");
$this->element('published', null, common_date_w3dtf($notice->created));
$this->element('link', array('type' => 'text/html',
'rel' => 'alternate',
'href' => $nurl));
$this->element('title', null, common_xml_safe_str(trim($notice->content)));
$this->element('content', array('type' => 'html'), $notice->getRendered());
$this->element('updated', null, common_date_w3dtf($notice->created));
$this->element('link', array('type' => 'image/png',
// XXX: Twitter uses rel="image" (not valid)
'rel' => 'related',
'href' => $profile->avatarUrl()));
// @todo: Here is where we'd put in a link to an atom feed for threads
$source = null;
$ns = $notice->getSource();
if ($ns instanceof Notice_source) {
if (!empty($ns->name) && !empty($ns->url)) {
$source = '<a href="'
. htmlspecialchars($ns->url)
. '" rel="nofollow">'
. htmlspecialchars($ns->name)
. '</a>';
} else {
$source = $ns->code;
}
}
$this->element("twitter:source", null, $source);
$this->elementStart('author');
$name = $profile->nickname;
if ($profile->fullname) {
// @todo Needs proper i18n?
$name .= ' (' . $profile->fullname . ')';
}
$this->element('name', null, $name);
$this->element('uri', null, common_profile_uri($profile));
$this->elementEnd('author');
$this->elementEnd('entry');
}
/**
* Initialize the Atom output, send headers
*
* @return void
*/
function initAtom()
{
header('Content-Type: application/atom+xml; charset=utf-8');
$this->startXml();
}
/**
* End the Atom feed
*
* @return void
*/
function endAtom()
{
$this->elementEnd('feed');
}
}

136
actions/apisearchjson.php Normal file
View File

@ -0,0 +1,136 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Action for showing Twitter-like JSON search results
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Search
* @package GNUsocial
* @author Zach Copley <zach@status.net>
* @copyright 2008-2010 StatusNet, Inc.
* @copyright 2013 Free Software Foundation, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Action handler for Twitter-compatible API search
*
* @category Search
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @see ApiAction
*/
class ApiSearchJSONAction extends ApiPrivateAuthAction
{
var $query;
var $lang;
var $rpp;
var $page;
var $since_id;
var $limit;
var $geocode;
/**
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean true if nothing goes wrong
*/
function prepare($args)
{
parent::prepare($args);
$this->query = $this->trimmed('q');
$this->lang = $this->trimmed('lang');
$this->rpp = $this->trimmed('rpp');
if (!$this->rpp) {
$this->rpp = 15;
}
if ($this->rpp > 100) {
$this->rpp = 100;
}
$this->page = $this->trimmed('page');
if (!$this->page) {
$this->page = 1;
}
// TODO: Suppport max_id -- we need to tweak the backend
// Search classes to support it.
$this->since_id = $this->trimmed('since_id');
$this->geocode = $this->trimmed('geocode');
return true;
}
/**
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showResults();
}
/**
* Show search results
*
* @return void
*/
function showResults()
{
// TODO: Support search operators like from: and to:, boolean, etc.
$notice = new Notice();
$this->notices = array();
$search_engine = $notice->getSearchEngine('notice');
$search_engine->set_sort_mode('chron');
$search_engine->limit(($this->page - 1) * $this->rpp, $this->rpp + 1);
if ($search_engine->query($this->query)) {
$cnt = $notice->find();
$this->notices = $notice->fetchAll();
}
$this->showJsonTimeline($this->notices);
}
/**
* Do we need to write to the database?
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
}

View File

@ -0,0 +1,156 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Destroy a notice through the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Tom Blankenship <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Deletes one of the authenticating user's statuses (notices).
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Tom Blankenship <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiStatusesDestroyAction extends ApiAuthAction
{
var $status = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{
parent::prepare($args);
$this->user = $this->auth_user;
$this->notice_id = (int)$this->trimmed('id');
if (empty($notice_id)) {
$this->notice_id = (int)$this->arg('id');
}
$this->notice = Notice::getKV((int)$this->notice_id);
return true;
}
/**
* Handle the request
*
* Delete the notice and all related replies
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
function handle($args)
{
parent::handle($args);
if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(
// TRANS: Client error displayed when coming across a non-supported API method.
_('API method not found.'),
404
);
return;
}
if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
$this->clientError(
// TRANS: Client error displayed trying to delete a status not using POST or DELETE.
// TRANS: POST and DELETE should not be translated.
_('This method requires a POST or DELETE.'),
400,
$this->format
);
return;
}
if (empty($this->notice)) {
$this->clientError(
// TRANS: Client error displayed trying to delete a status with an invalid ID.
_('No status found with that ID.'),
404, $this->format
);
return;
}
if ($this->user->id == $this->notice->profile_id) {
if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) {
$this->notice->deleteAs($this->scoped);
Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice));
}
$this->showNotice();
} else {
$this->clientError(
// TRANS: Client error displayed trying to delete a status of another user.
_('You may not delete another user\'s status.'),
403,
$this->format
);
}
}
/**
* Show the deleted notice
*
* @return void
*/
function showNotice()
{
if (!empty($this->notice)) {
if ($this->format == 'xml') {
$this->showSingleXmlStatus($this->notice);
} elseif ($this->format == 'json') {
$this->show_single_json_status($this->notice);
}
}
}
}

224
actions/apistatusesshow.php Normal file
View File

@ -0,0 +1,224 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show a notice (as a Twitter-style status)
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Tom Blankenship <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Returns the notice specified by id as a Twitter-style status and inline user
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Tom Blankenship <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiStatusesShowAction extends ApiPrivateAuthAction
{
var $notice_id = null;
var $notice = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
// 'id' is an undocumented parameter in Twitter's API. Several
// clients make use of it, so we support it too.
// show.json?id=12345 takes precedence over /show/12345.json
$this->notice_id = (int)$this->trimmed('id');
$this->notice = null;
try {
$this->notice = Notice::getByID($this->notice_id);
} catch (NoResultException $e) {
// No such notice was found, maybe it was deleted?
$deleted = null;
Event::handle('IsNoticeDeleted', array($this->notice_id, &$deleted));
if ($deleted === true) {
// TRANS: Client error displayed trying to show a deleted notice.
throw new ClientException(_('Notice deleted.'), 410);
}
// TRANS: Client error displayed trying to show a non-existing notice.
throw new ClientException(_('No such notice.'), 404);
}
if (!$this->notice->inScope($this->scoped)) {
// TRANS: Client exception thrown when trying a view a notice the user has no access to.
throw new ClientException(_('Access restricted.'), 403);
}
return true;
}
/**
* Handle the request
*
* Check the format and show the notice
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json', 'atom'))) {
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
$this->showNotice();
break;
case 'DELETE':
$this->deleteNotice();
break;
default:
// TRANS: Client error displayed calling an unsupported HTTP error in API status show.
$this->clientError(_('HTTP method not supported.'), 405);
}
}
/**
* Show the notice
*
* @return void
*/
function showNotice()
{
switch ($this->format) {
case 'xml':
$this->showSingleXmlStatus($this->notice);
break;
case 'json':
$this->show_single_json_status($this->notice);
break;
case 'atom':
$this->showSingleAtomStatus($this->notice);
break;
default:
// TRANS: Exception thrown requesting an unsupported notice output format.
// TRANS: %s is the requested output format.
throw new Exception(sprintf(_("Unsupported format: %s."), $this->format));
}
}
/**
* We expose AtomPub here, so non-GET/HEAD reqs must be read/write.
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD');
}
/**
* When was this notice last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
return strtotime($this->notice->created);
}
/**
* An entity tag for this notice
*
* Returns an Etag based on the action name, language, and
* timestamps of the notice
*
* @return string etag
*/
function etag()
{
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->notice->id,
strtotime($this->notice->created))
)
. '"';
}
function deleteNotice()
{
if ($this->format != 'atom') {
// TRANS: Client error displayed when trying to delete a notice not using the Atom format.
$this->clientError(_('Can only delete using the Atom format.'));
}
if (empty($this->auth_user) ||
($this->notice->profile_id != $this->auth_user->id &&
!$this->auth_user->hasRight(Right::DELETEOTHERSNOTICE))) {
// TRANS: Client error displayed when a user has no rights to delete notices of other users.
$this->clientError(_('Cannot delete this notice.'), 403);
}
if (Event::handle('StartDeleteOwnNotice', array($this->auth_user, $this->notice))) {
$this->notice->deleteAs($this->scoped);
Event::handle('EndDeleteOwnNotice', array($this->auth_user, $this->notice));
}
// @fixme is there better output we could do here?
header('HTTP/1.1 200 OK');
header('Content-Type: text/plain');
// TRANS: Confirmation of notice deletion in API. %d is the ID (number) of the deleted notice.
print(sprintf(_('Deleted notice %d'), $this->notice->id));
print("\n");
}
}

View File

@ -0,0 +1,366 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Post a notice (update your status) through the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Tom Blankenship <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
/* External API usage documentation. Please update when you change how this method works. */
/*! @page statusesupdate statuses/update
@section Description
Updates the authenticating user's status. Requires the status parameter specified below.
Request must be a POST.
@par URL pattern
/api/statuses/update.:format
@par Formats (:format)
xml, json
@par HTTP Method(s)
POST
@par Requires Authentication
Yes
@param status (Required) The URL-encoded text of the status update.
@param source (Optional) The source application name, if using HTTP authentication or an anonymous OAuth consumer.
@param in_reply_to_status_id (Optional) The ID of an existing status that the update is in reply to.
@param lat (Optional) The latitude the status refers to.
@param long (Optional) The longitude the status refers to.
@param media (Optional) a media upload, such as an image or movie file.
@sa @ref authentication
@sa @ref apiroot
@subsection usagenotes Usage notes
@li The URL pattern is relative to the @ref apiroot.
@li If the @e source parameter is not supplied the source of the status will default to 'api'. When authenticated via a registered OAuth application, the application's registered name and URL will always override the source parameter.
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
to encode the latitude and longitude (see example response below <georss:point>).
@li Data uploaded via the @e media parameter should be multipart/form-data encoded.
@subsection exampleusage Example usage
@verbatim
curl -u username:password http://example.com/api/statuses/update.xml -d status='Howdy!' -d lat='30.468' -d long='-94.743'
@endverbatim
@subsection exampleresponse Example response
@verbatim
<?xml version="1.0" encoding="UTF-8"?>
<status>
<text>Howdy!</text>
<truncated>false</truncated>
<created_at>Tue Mar 30 23:28:05 +0000 2010</created_at>
<in_reply_to_status_id/>
<source>api</source>
<id>26668724</id>
<in_reply_to_user_id/>
<in_reply_to_screen_name/>
<geo xmlns:georss="http://www.georss.org/georss">
<georss:point>30.468 -94.743</georss:point>
</geo>
<favorited>false</favorited>
<user>
<id>25803</id>
<name>Jed Sanders</name>
<screen_name>jedsanders</screen_name>
<location>Hoop and Holler, Texas</location>
<description>I like to think of myself as America's Favorite.</description>
<profile_image_url>http://avatar.example.com/25803-48-20080924200604.png</profile_image_url>
<url>http://jedsanders.net</url>
<protected>false</protected>
<followers_count>5</followers_count>
<profile_background_color/>
<profile_text_color/>
<profile_link_color/>
<profile_sidebar_fill_color/>
<profile_sidebar_border_color/>
<friends_count>2</friends_count>
<created_at>Wed Sep 24 20:04:00 +0000 2008</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url/>
<profile_background_tile>false</profile_background_tile>
<statuses_count>70</statuses_count>
<following>true</following>
<notifications>true</notifications>
</user>
</status>
@endverbatim
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Updates the authenticating user's status (posts a notice).
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Tom Blankenship <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiStatusesUpdateAction extends ApiAuthAction
{
protected $needPost = true;
var $status = null;
var $in_reply_to_status_id = null;
var $lat = null;
var $lon = null;
var $media_ids = array(); // file_id in the keys
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->status = $this->trimmed('status');
$this->lat = $this->trimmed('lat');
$this->lon = $this->trimmed('long');
$matches = array();
common_debug(get_called_class().': media_ids=='._ve($this->trimmed('media_ids')));
if (preg_match_all('/\d+/', $this->trimmed('media_ids'), $matches) !== false) {
foreach (array_unique($matches[0]) as $match) {
try {
$this->media_ids[$match] = File::getByID($match);
} catch (EmptyIdException $e) {
// got a zero from the client, at least Twidere does this on occasion
} catch (NoResultException $e) {
// File ID was not found. Do we abort and report to the client?
}
}
}
$this->in_reply_to_status_id
= intval($this->trimmed('in_reply_to_status_id'));
return true;
}
/**
* Handle the request
*
* Make a new notice for the update, save it, and show it
*
* @return void
*/
protected function handle()
{
parent::handle();
// Workaround for PHP returning empty $_POST and $_FILES when POST
// length > post_max_size in php.ini
if (empty($_FILES)
&& empty($_POST)
&& ($_SERVER['CONTENT_LENGTH'] > 0)
) {
// TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
// TRANS: %s is the number of bytes of the CONTENT_LENGTH.
$msg = _m('The server was unable to handle that much POST data (%s byte) due to its current configuration.',
'The server was unable to handle that much POST data (%s bytes) due to its current configuration.',
intval($_SERVER['CONTENT_LENGTH']));
$this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
}
if (empty($this->status)) {
// TRANS: Client error displayed when the parameter "status" is missing.
$this->clientError(_('Client must provide a \'status\' parameter with a value.'));
}
if (is_null($this->scoped)) {
// TRANS: Client error displayed when updating a status for a non-existing user.
$this->clientError(_('No such user.'), 404);
}
/* Do not call shortenLinks until the whole notice has been build */
// Check for commands
$inter = new CommandInterpreter();
$cmd = $inter->handle_command($this->auth_user, $this->status);
if ($cmd) {
if ($this->supported($cmd)) {
$cmd->execute(new Channel());
}
// Cmd not supported? Twitter just returns your latest status.
// And, it returns your last status whether the cmd was successful
// or not!
$this->notice = $this->auth_user->getCurrentNotice();
} else {
$reply_to = null;
if (!empty($this->in_reply_to_status_id)) {
// Check whether notice actually exists
$reply = Notice::getKV($this->in_reply_to_status_id);
if ($reply) {
$reply_to = $this->in_reply_to_status_id;
} else {
// TRANS: Client error displayed when replying to a non-existing notice.
$this->clientError(_('Parent notice not found.'), 404);
}
}
foreach(array_keys($this->media_ids) as $media_id) {
// FIXME: Validation on this... Worst case is that if someone sends bad media_ids then
// we'll fill the notice with non-working links, so no real harm, done, but let's fix.
// The File objects are in the array, so we could get URLs from them directly.
$this->status .= ' ' . common_local_url('attachment', array('attachment' => $media_id));
}
$upload = null;
try {
$upload = MediaFile::fromUpload('media', $this->scoped);
$this->status .= ' ' . $upload->shortUrl();
/* Do not call shortenLinks until the whole notice has been build */
} catch (NoUploadedMediaException $e) {
// There was no uploaded media for us today.
}
/* Do call shortenlinks here & check notice length since notice is about to be saved & sent */
$status_shortened = $this->auth_user->shortenLinks($this->status);
if (Notice::contentTooLong($status_shortened)) {
if ($upload instanceof MediaFile) {
$upload->delete();
}
// TRANS: Client error displayed exceeding the maximum notice length.
// TRANS: %d is the maximum lenth for a notice.
$msg = _m('Maximum notice size is %d character, including attachment URL.',
'Maximum notice size is %d characters, including attachment URL.',
Notice::maxContent());
/* Use HTTP 413 error code (Request Entity Too Large)
* instead of basic 400 for better understanding
*/
$this->clientError(sprintf($msg, Notice::maxContent()), 413);
}
$content = html_entity_decode($status_shortened, ENT_NOQUOTES, 'UTF-8');
$options = array('reply_to' => $reply_to);
if ($this->scoped->shareLocation()) {
$locOptions = Notice::locationOptions($this->lat,
$this->lon,
null,
null,
$this->scoped);
$options = array_merge($options, $locOptions);
}
try {
$this->notice = Notice::saveNew(
$this->scoped->id,
$content,
$this->source,
$options
);
} catch (Exception $e) {
$this->clientError($e->getMessage(), $e->getCode());
}
if (isset($upload)) {
$upload->attachToNotice($this->notice);
}
}
$this->showNotice();
}
/**
* Show the resulting notice
*
* @return void
*/
function showNotice()
{
if (!empty($this->notice)) {
if ($this->format == 'xml') {
$this->showSingleXmlStatus($this->notice);
} elseif ($this->format == 'json') {
$this->show_single_json_status($this->notice);
}
}
}
/**
* Is this command supported when doing an update from the API?
*
* @param string $cmd the command to check for
*
* @return boolean true or false
*/
function supported($cmd)
{
static $cmdlist = array('SubCommand', 'UnsubCommand',
'OnCommand', 'OffCommand', 'JoinCommand', 'LeaveCommand');
$supported = null;
if (Event::handle('CommandSupportedAPI', array($cmd, &$supported))) {
$supported = $supported || in_array(get_class($cmd), $cmdlist);
}
return $supported;
}
}

View File

@ -0,0 +1,254 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Base class for showing subscription information in the API
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* This class outputs a list of profiles as Twitter-style user and status objects.
* It is used by the API methods /api/statuses/(friends|followers). To support the
* social graph methods it also can output a simple list of IDs.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
abstract class ApiSubscriptionsAction extends ApiBareAuthAction
{
var $profiles = null;
var $tag = null;
var $lite = null;
var $ids_only = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->tag = $this->arg('tag');
// Note: Twitter no longer supports 'lite'
$this->lite = $this->arg('lite');
$this->ids_only = $this->arg('ids_only');
// If called as a social graph method, show 5000 per page, otherwise 100
$this->count = isset($this->ids_only) ?
5000 : (int)$this->arg('count', 100);
$this->target = $this->getTargetProfile($this->arg('id'));
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed when requesting a list of followers for a non-existing user.
$this->clientError(_('No such user.'), 404);
}
$this->profiles = $this->getProfiles();
return true;
}
/**
* Handle the request
*
* Show the profiles
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
$this->initDocument($this->format);
if (isset($this->ids_only)) {
$this->showIds();
} else {
$this->showProfiles(isset($this->lite) ? false : true);
}
$this->endDocument($this->format);
}
/**
* Get profiles related to the type of subscriber/subscription action
*
* @return array Profiles
*/
abstract protected function getProfiles();
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest profile in the stream
*/
function lastModified()
{
if (!empty($this->profiles) && (count($this->profiles) > 0)) {
return strtotime($this->profiles[0]->created);
}
return null;
}
/**
* An entity tag for this action
*
* Returns an Etag based on the action name, language, user ID, and
* timestamps of the first and last profiles in the subscriptions list
* There's also an indicator to show whether this action is being called
* as /api/statuses/(friends|followers) or /api/(friends|followers)/ids
*
* @return string etag
*/
function etag()
{
if (!empty($this->profiles) && (count($this->profiles) > 0)) {
$last = count($this->profiles) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->target->id,
// Caching tags.
isset($this->ids_only) ? 'IDs' : 'Profiles',
strtotime($this->profiles[0]->created),
strtotime($this->profiles[$last]->created))
)
. '"';
}
return null;
}
/**
* Show the profiles as Twitter-style useres and statuses
*
* @param boolean $include_statuses Whether to include the latest status
* with each user. Default true.
*
* @return void
*/
function showProfiles($include_statuses = true)
{
switch ($this->format) {
case 'xml':
$this->elementStart('users', array('type' => 'array',
'xmlns:statusnet' => 'http://status.net/schema/api/1/'));
foreach ($this->profiles as $profile) {
$this->showProfile(
$profile,
$this->format,
null,
$include_statuses
);
}
$this->elementEnd('users');
break;
case 'json':
$arrays = array();
foreach ($this->profiles as $profile) {
$arrays[] = $this->twitterUserArray(
$profile,
$include_statuses
);
}
print json_encode($arrays);
break;
default:
// TRANS: Client error displayed when requesting profiles of followers in an unsupported format.
$this->clientError(_('Unsupported format.'));
break;
}
}
/**
* Show the IDs of the profiles only. 5000 per page. To support
* the 'social graph' methods: /api/(friends|followers)/ids
*
* @return void
*/
function showIds()
{
switch ($this->format) {
case 'xml':
$this->elementStart('ids');
foreach ($this->profiles as $profile) {
$this->element('id', null, $profile->id);
}
$this->elementEnd('ids');
break;
case 'json':
$ids = array();
foreach ($this->profiles as $profile) {
$ids[] = (int)$profile->id;
}
print json_encode($ids);
break;
default:
// TRANS: Client error displayed when requesting IDs of followers in an unsupported format.
$this->clientError(_('Unsupported format.'));
break;
}
}
}

View File

@ -0,0 +1,344 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show the friends timeline
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
/* External API usage documentation. Please update when you change how this method works. */
/*! @page friendstimeline statuses/friends_timeline
@section Description
Returns the 20 most recent statuses posted by the authenticating
user and that user's friends. This is the equivalent of "You and
friends" page in the web interface.
@par URL patterns
@li /api/statuses/friends_timeline.:format
@li /api/statuses/friends_timeline/:id.:format
@par Formats (:format)
xml, json, rss, atom
@par ID (:id)
username, user id
@par HTTP Method(s)
GET
@par Requires Authentication
Sometimes (see: @ref authentication)
@param user_id (Optional) Specifies a user by ID
@param screen_name (Optional) Specifies a user by screename (nickname)
@param since_id (Optional) Returns only statuses with an ID greater
than (that is, more recent than) the specified ID.
@param max_id (Optional) Returns only statuses with an ID less than
(that is, older than) or equal to the specified ID.
@param count (Optional) Specifies the number of statuses to retrieve.
@param page (Optional) Specifies the page of results to retrieve.
@sa @ref authentication
@sa @ref apiroot
@subsection usagenotes Usage notes
@li The URL pattern is relative to the @ref apiroot.
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
to encode the latitude and longitude (see example response below <georss:point>).
@subsection exampleusage Example usage
@verbatim
curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2
@endverbatim
@subsection exampleresponse Example response
@verbatim
<?xml version="1.0"?>
<statuses type="array">
<status>
<text>back from the !yul !drupal meet with Evolving Web folk, @anarcat, @webchick and others, and an interesting refresher on SQL indexing</text>
<truncated>false</truncated>
<created_at>Wed Mar 31 01:33:02 +0000 2010</created_at>
<in_reply_to_status_id/>
<source>&lt;a href="http://code.google.com/p/microblog-purple/"&gt;mbpidgin&lt;/a&gt;</source>
<id>26674201</id>
<in_reply_to_user_id/>
<in_reply_to_screen_name/>
<geo/>
<favorited>false</favorited>
<user>
<id>246</id>
<name>Mark</name>
<screen_name>lambic</screen_name>
<location>Montreal, Canada</location>
<description>Geek</description>
<profile_image_url>http://avatar.identi.ca/246-48-20080702141545.png</profile_image_url>
<url>http://lambic.co.uk</url>
<protected>false</protected>
<followers_count>73</followers_count>
<profile_background_color>#F0F2F5</profile_background_color>
<profile_text_color/>
<profile_link_color>#002E6E</profile_link_color>
<profile_sidebar_fill_color>#CEE1E9</profile_sidebar_fill_color>
<profile_sidebar_border_color/>
<friends_count>58</friends_count>
<created_at>Wed Jul 02 14:12:15 +0000 2008</created_at>
<favourites_count>2</favourites_count>
<utc_offset>-14400</utc_offset>
<time_zone>US/Eastern</time_zone>
<profile_background_image_url/>
<profile_background_tile>false</profile_background_tile>
<statuses_count>933</statuses_count>
<following>false</following>
<notifications>false</notifications>
</user>
</status>
</statuses>
@endverbatim
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns the most recent notices (default 20) posted by the target user.
* This is the equivalent of 'You and friends' page accessed via Web.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiTimelineFriendsAction extends ApiBareAuthAction
{
var $notices = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->target = $this->getTargetProfile($this->arg('id'));
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed when requesting dents of a user and friends for a user that does not exist.
$this->clientError(_('No such user.'), 404);
}
$this->notices = $this->getNotices();
return true;
}
/**
* Handle the request
*
* Just show the notices
*
* @return void
*/
protected function handle()
{
parent::handle();
$this->showTimeline();
}
/**
* Show the timeline of notices
*
* @return void
*/
function showTimeline()
{
$sitename = common_config('site', 'name');
// TRANS: Title of API timeline for a user and friends.
// TRANS: %s is a username.
$title = sprintf(_("%s and friends"), $this->target->nickname);
$taguribase = TagURI::base();
$id = "tag:$taguribase:FriendsTimeline:" . $this->target->id;
$subtitle = sprintf(
// TRANS: Message is used as a subtitle. %1$s is a user nickname, %2$s is a site name.
_('Updates from %1$s and friends on %2$s!'),
$this->target->nickname,
$sitename
);
$logo = $this->target->avatarUrl(AVATAR_PROFILE_SIZE);
$link = common_local_url('all',
array('nickname' => $this->target->nickname));
$self = $this->getSelfUri();
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$logo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id);
$atom->setTitle($title);
$atom->setSubtitle($subtitle);
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
break;
case 'json':
$this->showJsonTimeline($this->notices);
break;
case 'as':
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
$doc = new ActivityStreamJSONDocument($this->auth_user, $title);
$doc->addLink($link, 'alternate', 'text/html');
$doc->addItemsFromNotices($this->notices);
$this->raw($doc->asString());
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Get notices
*
* @return array notices
*/
function getNotices()
{
$notices = array();
$stream = new InboxNoticeStream($this->target, $this->scoped);
$notice = $stream->getNotices(($this->page-1) * $this->count,
$this->count,
$this->since_id,
$this->max_id);
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
return strtotime($this->notices[0]->created);
}
return null;
}
/**
* An entity tag for this stream
*
* Returns an Etag based on the action name, language, user ID, and
* timestamps of the first and last notice in the timeline
*
* @return string etag
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
$last = count($this->notices) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->target->id,
strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created))
)
. '"';
}
return null;
}
}

View File

@ -0,0 +1,221 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show a group's notices
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns the most recent notices (default 20) posted to the group specified by ID
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiTimelineGroupAction extends ApiPrivateAuthAction
{
var $group = null;
var $notices = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->group = $this->getTargetGroup($this->arg('id'));
return true;
}
/**
* Handle the request
*
* Just show the notices
*
* @return void
*/
protected function handle()
{
parent::handle();
if (empty($this->group)) {
// TRANS: Client error displayed requesting most recent notices to a group for a non-existing group.
$this->clientError(_('Group not found.'), 404);
}
$this->notices = $this->getNotices();
$this->showTimeline();
}
/**
* Show the timeline of notices
*
* @return void
*/
function showTimeline()
{
// We'll pull common formatting out of this for other formats
$atom = new AtomGroupNoticeFeed($this->group, $this->auth_user);
$self = $this->getSelfUri();
$link = common_local_url('showgroup',
array('nickname' => $this->group->nickname));
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline(
$this->notices,
$atom->title,
$this->group->homeUrl(),
$atom->subtitle,
null,
$atom->logo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
break;
case 'json':
$this->showJsonTimeline($this->notices);
break;
case 'as':
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
$doc = new ActivityStreamJSONDocument($this->auth_user);
$doc->setTitle($atom->title);
$doc->addLink($link, 'alternate', 'text/html');
$doc->addItemsFromNotices($this->notices);
$this->raw($doc->asString());
break;
default:
// TRANS: Client error displayed when trying to handle an unknown API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Get notices
*
* @return array notices
*/
function getNotices()
{
$notices = array();
$notice = $this->group->getNotices(
($this->page-1) * $this->count,
$this->count,
$this->since_id,
$this->max_id
);
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
return strtotime($this->notices[0]->created);
}
return null;
}
/**
* An entity tag for this stream
*
* Returns an Etag based on the action name, language, group ID and
* timestamps of the first and last notice in the timeline
*
* @return string etag
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
$last = count($this->notices) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->group->id,
strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created))
)
. '"';
}
return null;
}
}

249
actions/apitimelinehome.php Normal file
View File

@ -0,0 +1,249 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show the home timeline
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns the most recent notices (default 20) posted by the target user.
* This is the equivalent of 'You and friends' page accessed via Web.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiTimelineHomeAction extends ApiBareAuthAction
{
var $notices = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->target = $this->getTargetProfile($this->arg('id'));
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed when requesting most recent dents by user and friends for a non-existing user.
$this->clientError(_('No such user.'), 404);
}
$this->notices = $this->getNotices();
return true;
}
/**
* Handle the request
*
* Just show the notices
*
* @return void
*/
protected function handle()
{
parent::handle();
$this->showTimeline();
}
/**
* Show the timeline of notices
*
* @return void
*/
function showTimeline()
{
$sitename = common_config('site', 'name');
// TRANS: Timeline title for user and friends. %s is a user nickname.
$title = sprintf(_("%s and friends"), $this->target->nickname);
$taguribase = TagURI::base();
$id = "tag:$taguribase:HomeTimeline:" . $this->target->id;
$subtitle = sprintf(
// TRANS: Message is used as a subtitle. %1$s is a user nickname, %2$s is a site name.
_('Updates from %1$s and friends on %2$s!'),
$this->target->nickname, $sitename
);
$logo = $this->target->avatarUrl(AVATAR_PROFILE_SIZE);
$link = common_local_url('all',
array('nickname' => $this->target->nickname));
$self = $this->getSelfUri();
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$logo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id);
$atom->setTitle($title);
$atom->setSubtitle($subtitle);
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
break;
case 'json':
$this->showJsonTimeline($this->notices);
break;
case 'as':
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
$doc = new ActivityStreamJSONDocument($this->auth_user);
$doc->setTitle($title);
$doc->addLink($link, 'alternate', 'text/html');
$doc->addItemsFromNotices($this->notices);
$this->raw($doc->asString());
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Get notices
*
* @return array notices
*/
function getNotices()
{
$notices = array();
$stream = new InboxNoticeStream($this->target, $this->scoped);
$notice = $stream->getNotices(($this->page-1) * $this->count,
$this->count,
$this->since_id,
$this->max_id);
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
return strtotime($this->notices[0]->created);
}
return null;
}
/**
* An entity tag for this stream
*
* Returns an Etag based on the action name, language, user ID, and
* timestamps of the first and last notice in the timeline
*
* @return string etag
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
$last = count($this->notices) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->target->id,
strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created))
)
. '"';
}
return null;
}
}

248
actions/apitimelinelist.php Normal file
View File

@ -0,0 +1,248 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show a list's notices
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
require_once INSTALLDIR . '/lib/atomlistnoticefeed.php';
/**
* Returns the most recent notices (default 20) posted to the list specified by ID
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiTimelineListAction extends ApiPrivateAuthAction
{
var $list = null;
var $notices = array();
var $next_cursor = 0;
var $prev_cursor = 0;
var $cursor = -1;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->cursor = (int) $this->arg('cursor', -1);
$this->list = $this->getTargetList($this->arg('user'), $this->arg('id'));
return true;
}
/**
* Handle the request
*
* Just show the notices
*
* @return void
*/
protected function handle()
{
parent::handle();
if (empty($this->list)) {
// TRANS: Client error displayed trying to perform an action related to a non-existing list.
$this->clientError(_('List not found.'), 404);
}
$this->getNotices();
$this->showTimeline();
}
/**
* Show the timeline of notices
*
* @return void
*/
function showTimeline()
{
// We'll pull common formatting out of this for other formats
$atom = new AtomListNoticeFeed($this->list, $this->auth_user);
$self = $this->getSelfUri();
switch($this->format) {
case 'xml':
$this->initDocument('xml');
$this->elementStart('statuses_list',
array('xmlns:statusnet' => 'http://status.net/schema/api/1/'));
$this->elementStart('statuses', array('type' => 'array'));
foreach ($this->notices as $n) {
$twitter_status = $this->twitterStatusArray($n);
$this->showTwitterXmlStatus($twitter_status);
}
$this->elementEnd('statuses');
$this->element('next_cursor', null, $this->next_cursor);
$this->element('previous_cursor', null, $this->prev_cursor);
$this->elementEnd('statuses_list');
$this->endDocument('xml');
break;
case 'rss':
$this->showRssTimeline(
$this->notices,
$atom->title,
$this->list->getUri(),
$atom->subtitle,
null,
$atom->logo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
try {
$atom->setId($self);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
} catch (Atom10FeedException $e) {
// TRANS: Server error displayed whe trying to get a timeline fails.
// TRANS: %s is the error message.
$this->serverError(sprintf(_('Could not generate feed for list - %s'), $e->getMessage()));
}
break;
case 'json':
$this->initDocument('json');
$statuses = array();
foreach ($this->notices as $n) {
$twitter_status = $this->twitterStatusArray($n);
array_push($statuses, $twitter_status);
}
$statuses_list = array('statuses' => $statuses,
'next_cursor' => $this->next_cusror,
'next_cursor_str' => strval($this->next_cusror),
'previous_cursor' => $this->prev_cusror,
'previous_cursor_str' => strval($this->prev_cusror)
);
$this->showJsonObjects($statuses_list);
$this->initDocument('json');
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Get notices
*
* @return array notices
*/
function getNotices()
{
$fn = array($this->list, 'getNotices');
list($this->notices, $this->next_cursor, $this->prev_cursor) =
Profile_list::getAtCursor($fn, array(), $this->cursor, 20);
if (!$this->notices) {
$this->notices = array();
}
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
return strtotime($this->notices[0]->created);
}
return null;
}
/**
* An entity tag for this stream
*
* Returns an Etag based on the action name, language, list ID and
* timestamps of the first and last notice in the timeline
*
* @return string etag
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
$last = count($this->notices) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_language(),
$this->list->id,
strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created))
)
. '"';
}
return null;
}
}

View File

@ -0,0 +1,254 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show notices mentioning a user (@nickname)
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns the most recent (default 20) mentions (status containing @nickname)
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiTimelineMentionsAction extends ApiBareAuthAction
{
var $notices = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->target = $this->getTargetProfile($this->arg('id'));
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed when requesting most recent mentions for a non-existing user.
$this->clientError(_('No such user.'), 404);
}
$this->notices = $this->getNotices();
return true;
}
/**
* Handle the request
*
* Just show the notices
*
* @return void
*/
protected function handle()
{
parent::handle();
$this->showTimeline();
}
/**
* Show the timeline of notices
*
* @return void
*/
function showTimeline()
{
$sitename = common_config('site', 'name');
$title = sprintf(
// TRANS: Title for timeline of most recent mentions of a user.
// TRANS: %1$s is the StatusNet sitename, %2$s is a user nickname.
_('%1$s / Updates mentioning %2$s'),
$sitename, $this->target->nickname
);
$taguribase = TagURI::base();
$id = "tag:$taguribase:Mentions:" . $this->target->id;
$logo = $this->target->avatarUrl(AVATAR_PROFILE_SIZE);
$link = common_local_url('replies',
array('nickname' => $this->target->nickname));
$self = $this->getSelfUri();
$subtitle = sprintf(
// TRANS: Subtitle for timeline of most recent mentions of a user.
// TRANS: %1$s is the StatusNet sitename, %2$s is a user nickname,
// TRANS: %3$s is a user's full name.
_('%1$s updates that reply to updates from %3$s / %2$s.'),
$sitename, $this->target->nickname, $this->target->getBestName()
);
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$logo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id);
$atom->setTitle($title);
$atom->setSubtitle($subtitle);
$atom->setLogo($logo);
$atom->setUpdated('now');
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
break;
case 'json':
$this->showJsonTimeline($this->notices);
break;
case 'as':
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
$doc = new ActivityStreamJSONDocument($this->auth_user);
$doc->setTitle($title);
$doc->addLink($link, 'alternate', 'text/html');
$doc->addItemsFromNotices($this->notices);
$this->raw($doc->asString());
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), $code = 404);
break;
}
}
/**
* Get notices
*
* @return array notices
*/
function getNotices()
{
$notices = array();
$stream = new ReplyNoticeStream($this->target->id, $this->scoped);
$notice = $stream->getNotices(($this->page - 1) * $this->count,
$this->count,
$this->since_id,
$this->max_id);
while ($notice->fetch()) {
$notices[] = clone($notice);
}
return $notices;
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
return strtotime($this->notices[0]->created);
}
return null;
}
/**
* An entity tag for this stream
*
* Returns an Etag based on the action name, language, user ID, and
* timestamps of the first and last notice in the timeline
*
* @return string etag
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
$last = count($this->notices) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->target->id,
strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created))
)
. '"';
}
return null;
}
}

View File

@ -0,0 +1,19 @@
<?php
if (!defined('GNUSOCIAL')) { exit(1); }
class ApiTimelineNetworkPublicAction extends ApiTimelinePublicAction
{
function title()
{
return sprintf(_("%s network public timeline"), common_config('site', 'name'));
}
protected function getStream()
{
if (!$this->scoped instanceof Profile && common_config('public', 'localonly')) {
$this->clientError(_('Network wide public feed is not permitted without authorization'), 403);
}
return new NetworkPublicNoticeStream($this->scoped);
}
}

View File

@ -0,0 +1,335 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show the public timeline
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns the most recent notices (default 20) posted by everybody
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
/* External API usage documentation. Please update when you change how this method works. */
/*! @page publictimeline statuses/public_timeline
@section Description
Returns the 20 most recent notices from users throughout the system who have
uploaded their own avatars. Depending on configuration, it may or may not
not include notices from automatic posting services.
@par URL patterns
@li /api/statuses/public_timeline.:format
@par Formats (:format)
xml, json, rss, atom
@par HTTP Method(s)
GET
@par Requires Authentication
No
@param since_id (Optional) Returns only statuses with an ID greater
than (that is, more recent than) the specified ID.
@param max_id (Optional) Returns only statuses with an ID less than
(that is, older than) or equal to the specified ID.
@param count (Optional) Specifies the number of statuses to retrieve.
@param page (Optional) Specifies the page of results to retrieve.
@sa @ref apiroot
@subsection usagenotes Usage notes
@li The URL pattern is relative to the @ref apiroot.
@li The XML response uses <a href="http://georss.org/Main_Page">GeoRSS</a>
to encode the latitude and longitude (see example response below <georss:point>).
@subsection exampleusage Example usage
@verbatim
curl http://identi.ca/api/statuses/friends_timeline/evan.xml?count=1&page=2
@endverbatim
@subsection exampleresponse Example response
@verbatim
<?xml version="1.0" encoding="UTF-8"?>
<statuses type="array">
<status>
<text>@skwashd oh, commbank reenabled me super quick both times. but disconcerting when you don't expect it though</text>
<truncated>false</truncated>
<created_at>Sat Apr 17 00:49:12 +0000 2010</created_at>
<in_reply_to_status_id>28838393</in_reply_to_status_id>
<source>xmpp</source>
<id>28838456</id>
<in_reply_to_user_id>39303</in_reply_to_user_id>
<in_reply_to_screen_name>skwashd</in_reply_to_screen_name>
<geo></geo>
<favorited>false</favorited>
<user>
<id>44517</id>
<name>joshua may</name>
<screen_name>notjosh</screen_name>
<location></location>
<description></description>
<profile_image_url>http://avatar.identi.ca/44517-48-20090321004106.jpeg</profile_image_url>
<url></url>
<protected>false</protected>
<followers_count>17</followers_count>
<profile_background_color></profile_background_color>
<profile_text_color></profile_text_color>
<profile_link_color></profile_link_color>
<profile_sidebar_fill_color></profile_sidebar_fill_color>
<profile_sidebar_border_color></profile_sidebar_border_color>
<friends_count>20</friends_count>
<created_at>Sat Mar 21 00:40:25 +0000 2009</created_at>
<favourites_count>0</favourites_count>
<utc_offset>0</utc_offset>
<time_zone>UTC</time_zone>
<profile_background_image_url></profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>100</statuses_count>
<following>false</following>
<notifications>false</notifications>
</user>
</status>
[....]
</statuses>
@endverbatim
*/
class ApiTimelinePublicAction extends ApiPrivateAuthAction
{
var $notices = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->notices = $this->getNotices();
return true;
}
/**
* Handle the request
*
* Just show the notices
*
* @return void
*/
protected function handle()
{
parent::handle();
$this->showTimeline();
}
function title()
{
// TRANS: Title for site timeline. %s is the GNU social sitename.
return sprintf(_("%s public timeline"), common_config('site', 'name'));
}
/**
* Show the timeline of notices
*
* @return void
*/
function showTimeline()
{
$nonapi_action = substr($this->action, strlen('apitimeline')); // Just so we don't need to set this explicitly
$sitelogo = (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png');
$title = $this->title();
$taguribase = TagURI::base();
$id = "tag:$taguribase:" . ucfirst($nonapi_action) . 'Timeline'; // Public or Networkpublic probably
$link = common_local_url($nonapi_action);
$self = $this->getSelfUri();
// TRANS: Subtitle for site timeline. %s is the GNU social sitename.
$subtitle = sprintf(_("%s updates from everyone!"), common_config('site', 'name'));
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$sitelogo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id);
$atom->setTitle($title);
$atom->setSubtitle($subtitle);
$atom->setLogo($sitelogo);
$atom->setUpdated('now');
$atom->addLink(common_local_url($nonapi_action));
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
break;
case 'json':
$this->showJsonTimeline($this->notices);
break;
case 'as':
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
$doc = new ActivityStreamJSONDocument($this->auth_user);
$doc->setTitle($title);
$doc->addLink($link, 'alternate', 'text/html');
$doc->addItemsFromNotices($this->notices);
$this->raw($doc->asString());
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), $code = 404);
break;
}
}
/**
* Get notices
*
* @return array notices
*/
function getNotices()
{
$notices = array();
$stream = $this->getStream();
$notice = $stream->getNotices(($this->page - 1) * $this->count,
$this->count,
$this->since_id,
$this->max_id);
$notices = $notice->fetchAll();
NoticeList::prefill($notices);
return $notices;
}
protected function getStream()
{
return new PublicNoticeStream($this->scoped);
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
return strtotime($this->notices[0]->created);
}
return null;
}
/**
* An entity tag for this stream
*
* Returns an Etag based on the action name, language, and
* timestamps of the first and last notice in the timeline
*
* @return string etag
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
$last = count($this->notices) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created))
)
. '"';
}
return null;
}
}

228
actions/apitimelinetag.php Normal file
View File

@ -0,0 +1,228 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show the latest notices for a given tag
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009-2010 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Returns the 20 most recent notices tagged by a given tag
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiTimelineTagAction extends ApiPrivateAuthAction
{
var $notices = null;
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->tag = $this->arg('tag');
$this->notices = $this->getNotices();
return true;
}
/**
* Handle the request
*
* Just show the notices
*
* @param array $args $_REQUEST data (unused)
*
* @return void
*/
protected function handle()
{
parent::handle();
$this->showTimeline();
}
/**
* Show the timeline of notices
*
* @return void
*/
function showTimeline()
{
$sitename = common_config('site', 'name');
$sitelogo = (common_config('site', 'logo')) ? common_config('site', 'logo') : Theme::path('logo.png');
// TRANS: Title for timeline with lastest notices with a given tag.
// TRANS: %s is the tag.
$title = sprintf(_("Notices tagged with %s"), $this->tag);
$subtitle = sprintf(
// TRANS: Subtitle for timeline with lastest notices with a given tag.
// TRANS: %1$s is the tag, $2$s is the StatusNet sitename.
_('Updates tagged with %1$s on %2$s!'),
$this->tag,
$sitename
);
$taguribase = TagURI::base();
$id = "tag:$taguribase:TagTimeline:".$this->tag;
$link = common_local_url(
'tag',
array('tag' => $this->tag)
);
$self = $this->getSelfUri();
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline(
$this->notices,
$title,
$link,
$subtitle,
null,
$sitelogo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom = new AtomNoticeFeed($this->auth_user);
$atom->setId($id);
$atom->setTitle($title);
$atom->setSubtitle($subtitle);
$atom->setLogo($sitelogo);
$atom->setUpdated('now');
$atom->addLink($link);
$atom->setSelfLink($self);
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
break;
case 'json':
$this->showJsonTimeline($this->notices);
break;
case 'as':
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
$doc = new ActivityStreamJSONDocument($this->auth_user);
$doc->setTitle($title);
$doc->addLink($link, 'alternate', 'text/html');
$doc->addItemsFromNotices($this->notices);
$this->raw($doc->asString());
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), $code = 404);
break;
}
}
/**
* Get notices
*
* @return array notices
*/
function getNotices()
{
$notice = Notice_tag::getStream($this->tag)->getNotices(($this->page - 1) * $this->count,
$this->count + 1,
$this->since_id,
$this->max_id);
return $notice->fetchAll();
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
return strtotime($this->notices[0]->created);
}
return null;
}
/**
* An entity tag for this stream
*
* Returns an Etag based on the action name, language, and
* timestamps of the first and last notice in the timeline
*
* @return string etag
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
$last = count($this->notices) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->auth_user),
common_language(),
$this->tag,
strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created))
)
. '"';
}
return null;
}
}

381
actions/apitimelineuser.php Normal file
View File

@ -0,0 +1,381 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show a user's timeline
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Returns the most recent notices (default 20) posted by the authenticating
* user. Another user's timeline can be requested via the id parameter. This
* is the API equivalent of the user profile web page.
*
* @category API
* @package StatusNet
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@status.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author mac65 <mac65@mac65.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <robin@millette.info>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiTimelineUserAction extends ApiBareAuthAction
{
var $notices = null;
var $next_id = null;
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$this->target = $this->getTargetProfile($this->arg('id'));
if (!($this->target instanceof Profile)) {
// TRANS: Client error displayed requesting most recent notices for a non-existing user.
$this->clientError(_('No such user.'), 404);
}
if (!$this->target->isLocal()) {
$this->serverError(_('Remote user timelines are not available here yet.'), 501);
}
$this->notices = $this->getNotices();
return true;
}
/**
* Handle the request
*
* Just show the notices
*
* @return void
*/
protected function handle()
{
parent::handle();
if ($this->isPost()) {
$this->handlePost();
} else {
$this->showTimeline();
}
}
/**
* Show the timeline of notices
*
* @return void
*/
function showTimeline()
{
// We'll use the shared params from the Atom stub
// for other feed types.
$atom = new AtomUserNoticeFeed($this->target->getUser(), $this->scoped);
$link = common_local_url(
'showstream',
array('nickname' => $this->target->getNickname())
);
$self = $this->getSelfUri();
// FriendFeed's SUP protocol
// Also added RSS and Atom feeds
$suplink = common_local_url('sup', null, null, $this->target->getID());
header('X-SUP-ID: ' . $suplink);
// paging links
$nextUrl = !empty($this->next_id)
? common_local_url('ApiTimelineUser',
array('format' => $this->format,
'id' => $this->target->getID()),
array('max_id' => $this->next_id))
: null;
$prevExtra = array();
if (!empty($this->notices)) {
assert($this->notices[0] instanceof Notice);
$prevExtra['since_id'] = $this->notices[0]->id;
}
$prevUrl = common_local_url('ApiTimelineUser',
array('format' => $this->format,
'id' => $this->target->getID()),
$prevExtra);
$firstUrl = common_local_url('ApiTimelineUser',
array('format' => $this->format,
'id' => $this->target->getID()));
switch($this->format) {
case 'xml':
$this->showXmlTimeline($this->notices);
break;
case 'rss':
$this->showRssTimeline(
$this->notices,
$atom->title,
$link,
$atom->subtitle,
$suplink,
$atom->logo,
$self
);
break;
case 'atom':
header('Content-Type: application/atom+xml; charset=utf-8');
$atom->setId($self);
$atom->setSelfLink($self);
// Add navigation links: next, prev, first
// Note: we use IDs rather than pages for navigation; page boundaries
// change too quickly!
if (!empty($this->next_id)) {
$atom->addLink($nextUrl,
array('rel' => 'next',
'type' => 'application/atom+xml'));
}
if (($this->page > 1 || !empty($this->max_id)) && !empty($this->notices)) {
$atom->addLink($prevUrl,
array('rel' => 'prev',
'type' => 'application/atom+xml'));
}
if ($this->page > 1 || !empty($this->since_id) || !empty($this->max_id)) {
$atom->addLink($firstUrl,
array('rel' => 'first',
'type' => 'application/atom+xml'));
}
$atom->addEntryFromNotices($this->notices);
$this->raw($atom->getString());
break;
case 'json':
$this->showJsonTimeline($this->notices);
break;
case 'as':
header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
$doc = new ActivityStreamJSONDocument($this->scoped);
$doc->setTitle($atom->title);
$doc->addLink($link, 'alternate', 'text/html');
$doc->addItemsFromNotices($this->notices);
if (!empty($this->next_id)) {
$doc->addLink($nextUrl,
array('rel' => 'next',
'type' => ActivityStreamJSONDocument::CONTENT_TYPE));
}
if (($this->page > 1 || !empty($this->max_id)) && !empty($this->notices)) {
$doc->addLink($prevUrl,
array('rel' => 'prev',
'type' => ActivityStreamJSONDocument::CONTENT_TYPE));
}
if ($this->page > 1 || !empty($this->since_id) || !empty($this->max_id)) {
$doc->addLink($firstUrl,
array('rel' => 'first',
'type' => ActivityStreamJSONDocument::CONTENT_TYPE));
}
$this->raw($doc->asString());
break;
default:
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
}
/**
* Get notices
*
* @return array notices
*/
function getNotices()
{
$notices = array();
$notice = $this->target->getNotices(($this->page-1) * $this->count,
$this->count + 1,
$this->since_id,
$this->max_id,
$this->scoped);
while ($notice->fetch()) {
if (count($notices) < $this->count) {
$notices[] = clone($notice);
} else {
$this->next_id = $notice->id;
break;
}
}
return $notices;
}
/**
* We expose AtomPub here, so non-GET/HEAD reqs must be read/write.
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
return ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD');
}
/**
* When was this feed last modified?
*
* @return string datestamp of the latest notice in the stream
*/
function lastModified()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
return strtotime($this->notices[0]->created);
}
return null;
}
/**
* An entity tag for this stream
*
* Returns an Etag based on the action name, language, user ID, and
* timestamps of the first and last notice in the timeline
*
* @return string etag
*/
function etag()
{
if (!empty($this->notices) && (count($this->notices) > 0)) {
$last = count($this->notices) - 1;
return '"' . implode(
':',
array($this->arg('action'),
common_user_cache_hash($this->scoped),
common_language(),
$this->target->getID(),
strtotime($this->notices[0]->created),
strtotime($this->notices[$last]->created))
)
. '"';
}
return null;
}
function handlePost()
{
if (!$this->scoped instanceof Profile ||
!$this->target->sameAs($this->scoped)) {
// TRANS: Client error displayed trying to add a notice to another user's timeline.
$this->clientError(_('Only the user can add to their own timeline.'), 403);
}
// Only handle posts for Atom
if ($this->format != 'atom') {
// TRANS: Client error displayed when using another format than AtomPub.
$this->clientError(_('Only accept AtomPub for Atom feeds.'));
}
$xml = trim(file_get_contents('php://input'));
if (empty($xml)) {
// TRANS: Client error displayed attempting to post an empty API notice.
$this->clientError(_('Atom post must not be empty.'));
}
$old = error_reporting(error_reporting() & ~(E_WARNING | E_NOTICE));
$dom = new DOMDocument();
$ok = $dom->loadXML($xml);
error_reporting($old);
if (!$ok) {
// TRANS: Client error displayed attempting to post an API that is not well-formed XML.
$this->clientError(_('Atom post must be well-formed XML.'));
}
if ($dom->documentElement->namespaceURI != Activity::ATOM ||
$dom->documentElement->localName != 'entry') {
// TRANS: Client error displayed when not using an Atom entry.
$this->clientError(_('Atom post must be an Atom entry.'));
}
$activity = new Activity($dom->documentElement);
common_debug('AtomPub: Ignoring right now, but this POST was made to collection: '.$activity->id);
// Reset activity data so we can handle it in the same functions as with OStatus
// because we don't let clients set their own UUIDs... Not sure what AtomPub thinks
// about that though.
$activity->id = null;
$activity->actor = null; // not used anyway, we use $this->target
$activity->objects[0]->id = null;
$stored = null;
if (Event::handle('StartAtomPubNewActivity', array($activity, $this->target, &$stored))) {
// TRANS: Client error displayed when not using the POST verb. Do not translate POST.
throw new ClientException(_('Could not handle this Atom Activity.'));
}
if (!$stored instanceof Notice) {
throw new ServerException('Server did not create a Notice object from handled AtomPub activity.');
}
Event::handle('EndAtomPubNewActivity', array($activity, $this->target, $stored));
header('HTTP/1.1 201 Created');
header("Location: " . common_local_url('ApiStatusesShow', array('id' => $stored->getID(),
'format' => 'atom')));
$this->showSingleAtomStatus($stored);
}
}

85
actions/apitrends.php Normal file
View File

@ -0,0 +1,85 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* List of replies
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Search
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @copyright 2008-2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Returns the top ten queries that are currently trending
*
* @category Search
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*
* @see ApiAction
*/
class ApiTrendsAction extends ApiPrivateAuthAction
{
var $callback;
/**
* Initialization.
*
* @param array $args Web and URL arguments
*
* @return boolean false if user doesn't exist
*/
function prepare($args)
{
parent::prepare($args);
return true;
}
/**
* Handle a request
*
* @param array $args Arguments from $_REQUEST
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$this->showTrends();
}
/**
* Output the trends
*
* @return void
*/
function showTrends()
{
// TRANS: Server error for unfinished API method showTrends.
$this->serverError(_('API method under construction.'), 501);
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show a user's followers (subscribers)
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Ouputs the authenticating user's followers (subscribers), each with
* current Twitter-style status inline. They are ordered by the order
* in which they subscribed to the user, 100 at a time.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiUserFollowersAction extends ApiSubscriptionsAction
{
/**
* Get the user's subscribers (followers) as an array of profiles
*
* @return array Profiles
*/
protected function getProfiles()
{
$offset = ($this->page - 1) * $this->count;
$limit = $this->count + 1;
$subs = null;
if (isset($this->tag)) {
$subs = $this->target->getTaggedSubscribers(
$this->tag, $offset, $limit
);
} else {
$subs = $this->target->getSubscribers(
$offset,
$limit
);
}
$profiles = array();
while ($subs->fetch()) {
$profiles[] = clone($subs);
}
return $profiles;
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show a user's friends (subscriptions)
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Ouputs the authenticating user's friends (subscriptions), each with
* current Twitter-style status inline. They are ordered by the date
* in which the user subscribed to them, 100 at a time.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiUserFriendsAction extends ApiSubscriptionsAction
{
/**
* Get the user's subscriptions (friends) as an array of profiles
*
* @return array Profiles
*/
protected function getProfiles()
{
$offset = ($this->page - 1) * $this->count;
$limit = $this->count + 1;
$subs = null;
if (isset($this->tag)) {
$subs = $this->target->getTaggedSubscriptions(
$this->tag, $offset, $limit
);
} else {
$subs = $this->target->getSubscribed(
$offset,
$limit
);
}
$profiles = array();
while ($subs->fetch()) {
$profiles[] = clone($subs);
}
return $profiles;
}
}

View File

@ -0,0 +1,117 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Return a user's avatar image
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Brion Vibber <brion@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Ouputs avatar URL for a user, specified by screen name.
* Unlike most API endpoints, this returns an HTTP redirect rather than direct data.
*
* @category API
* @package StatusNet
* @author Brion Vibber <brion@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiUserProfileImageAction extends ApiPrivateAuthAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$user = User::getKV('nickname', $this->arg('screen_name'));
if (!($user instanceof User)) {
// TRANS: Client error displayed when requesting user information for a non-existing user.
$this->clientError(_('User not found.'), 404);
}
$this->target = $user->getProfile();
$this->size = $this->arg('size');
return true;
}
/**
* Handle the request
*
* Check the format and show the user info
*
* @return void
*/
protected function handle()
{
parent::handle();
$size = $this->avatarSize();
$url = $this->target->avatarUrl($size);
// We don't actually output JSON or XML data -- redirect!
common_redirect($url, 302);
}
/**
* Get the appropriate pixel size for an avatar based on the request...
*
* @return int
*/
private function avatarSize()
{
switch ($this->size) {
case 'mini':
return AVATAR_MINI_SIZE; // 24x24
case 'bigger':
return AVATAR_PROFILE_SIZE; // Twitter does 73x73, but we do 96x96
case 'normal': // fall through
default:
return AVATAR_STREAM_SIZE; // 48x48
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

125
actions/apiusershow.php Normal file
View File

@ -0,0 +1,125 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show a user's profile information
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author mac65 <mac65@mac65.com>
* @author Zach Copley <zach@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Ouputs information for a user, specified by ID or screen name.
* The user's most recent status will be returned inline.
*
* @category API
* @package StatusNet
* @author Dan Moore <dan@moore.cx>
* @author Evan Prodromou <evan@status.net>
* @author mac65 <mac65@mac65.com>
* @author Zach Copley <zach@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApiUserShowAction extends ApiPrivateAuthAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
$email = $this->arg('email');
// XXX: email field deprecated in Twitter's API
if (!empty($email)) {
$user = User::getKV('email', $email);
} else {
$user = $this->getTargetUser($this->arg('id'));
}
if (!($user instanceof User)) {
// TRANS: Client error displayed when requesting user information for a non-existing user.
$this->clientError(_('User not found.'), 404);
}
$this->target = $user->getProfile();
return true;
}
/**
* Handle the request
*
* Check the format and show the user info
*
* @return void
*/
protected function handle()
{
parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
// TRANS: Client error displayed when coming across a non-supported API method.
$this->clientError(_('API method not found.'), 404);
}
$twitter_user = $this->twitterUserArray($this->target, true);
if ($this->format == 'xml') {
$this->initDocument('xml');
$this->showTwitterXmlUser($twitter_user, 'user', true);
$this->endDocument('xml');
} elseif ($this->format == 'json') {
$this->initDocument('json');
$this->showJsonObjects($twitter_user);
$this->endDocument('json');
}
}
/**
* Return true if read only.
*
* MAY override
*
* @param array $args other arguments
*
* @return boolean is read only action?
*/
function isReadOnly($args)
{
return true;
}
}

184
actions/approvegroup.php Normal file
View File

@ -0,0 +1,184 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Leave a group
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Leave a group
*
* This is the action for leaving a group. It works more or less like the subscribe action
* for users.
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApprovegroupAction extends Action
{
var $group = null;
/**
* Prepare to run
*/
function prepare($args)
{
parent::prepare($args);
if (!common_logged_in()) {
// TRANS: Client error displayed when trying to leave a group while not logged in.
$this->clientError(_('You must be logged in to leave a group.'));
}
$nickname_arg = $this->trimmed('nickname');
$id = intval($this->arg('id'));
if ($id) {
$this->group = User_group::getKV('id', $id);
} else if ($nickname_arg) {
$nickname = common_canonical_nickname($nickname_arg);
// Permanent redirect on non-canonical nickname
if ($nickname_arg != $nickname) {
$args = array('nickname' => $nickname);
common_redirect(common_local_url('leavegroup', $args), 301);
}
$local = Local_group::getKV('nickname', $nickname);
if (!$local) {
// TRANS: Client error displayed when trying to leave a non-local group.
$this->clientError(_('No such group.'), 404);
}
$this->group = User_group::getKV('id', $local->group_id);
} else {
// TRANS: Client error displayed when trying to leave a group without providing a group name or group ID.
$this->clientError(_('No nickname or ID.'), 404);
}
if (!$this->group) {
// TRANS: Client error displayed when trying to leave a non-existing group.
$this->clientError(_('No such group.'), 404);
}
$cur = common_current_user();
if (empty($cur)) {
// TRANS: Client error displayed trying to approve group membership while not logged in.
$this->clientError(_('Must be logged in.'), 403);
}
if ($this->arg('profile_id')) {
if ($cur->isAdmin($this->group)) {
$this->profile = Profile::getKV('id', $this->arg('profile_id'));
} else {
// TRANS: Client error displayed trying to approve group membership while not a group administrator.
$this->clientError(_('Only group admin can approve or cancel join requests.'), 403);
}
} else {
// TRANS: Client error displayed trying to approve group membership without specifying a profile to approve.
$this->clientError(_('Must specify a profile.'));
}
$this->request = Group_join_queue::pkeyGet(array('profile_id' => $this->profile->id,
'group_id' => $this->group->id));
if (empty($this->request)) {
// TRANS: Client error displayed trying to approve group membership for a non-existing request.
// TRANS: %s is a nickname.
$this->clientError(sprintf(_('%s is not in the moderation queue for this group.'), $this->profile->nickname), 403);
}
$this->approve = (bool)$this->arg('approve');
$this->cancel = (bool)$this->arg('cancel');
if (!$this->approve && !$this->cancel) {
// TRANS: Client error displayed trying to approve/deny group membership.
$this->clientError(_('Internal error: received neither cancel nor abort.'));
}
if ($this->approve && $this->cancel) {
// TRANS: Client error displayed trying to approve/deny group membership.
$this->clientError(_('Internal error: received both cancel and abort.'));
}
return true;
}
/**
* Handle the request
*
* On POST, add the current user to the group
*
* @param array $args unused
*
* @return void
*/
function handle($args)
{
parent::handle($args);
try {
if ($this->approve) {
$this->request->complete();
} elseif ($this->cancel) {
$this->request->abort();
}
} catch (Exception $e) {
common_log(LOG_ERR, "Exception canceling group sub: " . $e->getMessage());
// TRANS: Server error displayed when cancelling a queued group join request fails.
// TRANS: %1$s is the leaving user's nickname, $2$s is the group nickname for which the leave failed.
$this->serverError(sprintf(_('Could not cancel request for user %1$s to join group %2$s.'),
$this->profile->nickname, $this->group->nickname));
return;
}
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
// TRANS: Title for leave group page after group join request is approved/disapproved.
// TRANS: %1$s is the user nickname, %2$s is the group nickname.
$this->element('title', null, sprintf(_m('TITLE','%1$s\'s request for %2$s'),
$this->profile->nickname,
$this->group->nickname));
$this->elementEnd('head');
$this->elementStart('body');
if ($this->approve) {
// TRANS: Message on page for group admin after approving a join request.
$this->element('p', 'success', _('Join request approved.'));
} elseif ($this->cancel) {
// TRANS: Message on page for group admin after rejecting a join request.
$this->element('p', 'success', _('Join request canceled.'));
}
$this->elementEnd('body');
$this->endHTML();
} else {
common_redirect(common_local_url('groupmembers', array('nickname' => $this->group->nickname)), 303);
}
}
}

144
actions/approvesub.php Normal file
View File

@ -0,0 +1,144 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Approve group subscription request
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
/**
* Leave a group
*
* This is the action for leaving a group. It works more or less like the subscribe action
* for users.
*
* @category Group
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ApprovesubAction extends Action
{
var $profile = null;
/**
* Prepare to run
*/
function prepare($args)
{
parent::prepare($args);
$cur = common_current_user();
if (empty($cur)) {
// TRANS: Client error displayed trying to approve group membership while not logged in.
$this->clientError(_('Must be logged in.'), 403);
}
if ($this->arg('profile_id')) {
$this->profile = Profile::getKV('id', $this->arg('profile_id'));
} else {
// TRANS: Client error displayed trying to approve subscriptionswithout specifying a profile to approve.
$this->clientError(_('Must specify a profile.'));
}
$this->request = Subscription_queue::pkeyGet(array('subscriber' => $this->profile->id,
'subscribed' => $cur->id));
if (empty($this->request)) {
// TRANS: Client error displayed trying to approve subscription for a non-existing request.
// TRANS: %s is a user nickname.
$this->clientError(sprintf(_('%s is not in the moderation queue for your subscriptions.'), $this->profile->nickname), 403);
}
$this->approve = (bool)$this->arg('approve');
$this->cancel = (bool)$this->arg('cancel');
if (!$this->approve && !$this->cancel) {
// TRANS: Client error displayed trying to approve/deny subscription.
$this->clientError(_('Internal error: received neither cancel nor abort.'));
}
if ($this->approve && $this->cancel) {
// TRANS: Client error displayed trying to approve/deny subscription
$this->clientError(_('Internal error: received both cancel and abort.'));
}
return true;
}
/**
* Handle the request
*
* On POST, add the current user to the group
*
* @param array $args unused
*
* @return void
*/
function handle($args)
{
parent::handle($args);
$cur = common_current_user();
try {
if ($this->approve) {
$this->request->complete();
} elseif ($this->cancel) {
$this->request->abort();
}
} catch (Exception $e) {
common_log(LOG_ERR, "Exception canceling sub: " . $e->getMessage());
// TRANS: Server error displayed when cancelling a queued subscription request fails.
// TRANS: %1$s is the leaving user's nickname, $2$s is the nickname for which the leave failed.
$this->serverError(sprintf(_('Could not cancel or approve request for user %1$s to join group %2$s.'),
$this->profile->nickname, $cur->nickname));
return;
}
if ($this->boolean('ajax')) {
$this->startHTML('text/xml;charset=utf-8');
$this->elementStart('head');
// TRANS: Title for subscription approval ajax return
// TRANS: %1$s is the approved user's nickname
$this->element('title', null, sprintf(_m('TITLE','%1$s\'s request'),
$this->profile->nickname));
$this->elementEnd('head');
$this->elementStart('body');
if ($this->approve) {
// TRANS: Message on page for user after approving a subscription request.
$this->element('p', 'success', _('Subscription approved.'));
} elseif ($this->cancel) {
// TRANS: Message on page for user after rejecting a subscription request.
$this->element('p', 'success', _('Subscription canceled.'));
}
$this->elementEnd('body');
$this->endHTML();
} else {
common_redirect(common_local_url('subqueue', array('nickname' =>
$cur->nickname)),
303);
}
}
}

View File

@ -0,0 +1,278 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* Feed of group memberships for a user, in ActivityStreams format
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category AtomPub
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* Feed of group memberships for a user, in ActivityStreams format
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class AtompubmembershipfeedAction extends AtompubAction
{
private $_profile = null;
private $_memberships = null;
protected function atompubPrepare()
{
$this->_profile = Profile::getKV('id', $this->trimmed('profile'));
if (!$this->_profile instanceof Profile) {
// TRANS: Client exception.
throw new ClientException(_('No such profile.'), 404);
}
$this->_memberships = Group_member::byMember($this->_profile->id,
$this->offset,
$this->limit);
return true;
}
protected function handleGet()
{
return $this->showFeed();
}
protected function handlePost()
{
return $this->addMembership();
}
/**
* Show a feed of favorite activity streams objects
*
* @return void
*/
function showFeed()
{
header('Content-Type: application/atom+xml; charset=utf-8');
$url = common_local_url('AtomPubMembershipFeed',
array('profile' => $this->_profile->id));
$feed = new Atom10Feed(true);
$feed->addNamespace('activity',
'http://activitystrea.ms/spec/1.0/');
$feed->addNamespace('poco',
'http://portablecontacts.net/spec/1.0');
$feed->addNamespace('media',
'http://purl.org/syndication/atommedia');
$feed->id = $url;
$feed->setUpdated('now');
$feed->addAuthor($this->_profile->getBestName(),
$this->_profile->getURI());
// TRANS: Title for group membership feed.
// TRANS: %s is a username.
$feed->setTitle(sprintf(_('Group memberships of %s'),
$this->_profile->getBestName()));
// TRANS: Subtitle for group membership feed.
// TRANS: %1$s is a username, %2$s is the StatusNet sitename.
$feed->setSubtitle(sprintf(_('Groups %1$s is a member of on %2$s'),
$this->_profile->getBestName(),
common_config('site', 'name')));
$feed->addLink(common_local_url('usergroups',
array('nickname' =>
$this->_profile->nickname)));
$feed->addLink($url,
array('rel' => 'self',
'type' => 'application/atom+xml'));
// If there's more...
if ($this->page > 1) {
$feed->addLink($url,
array('rel' => 'first',
'type' => 'application/atom+xml'));
$feed->addLink(common_local_url('AtomPubMembershipFeed',
array('profile' =>
$this->_profile->id),
array('page' =>
$this->page - 1)),
array('rel' => 'prev',
'type' => 'application/atom+xml'));
}
if ($this->_memberships->N > $this->count) {
$feed->addLink(common_local_url('AtomPubMembershipFeed',
array('profile' =>
$this->_profile->id),
array('page' =>
$this->page + 1)),
array('rel' => 'next',
'type' => 'application/atom+xml'));
}
$i = 0;
while ($this->_memberships->fetch()) {
// We get one more than needed; skip that one
$i++;
if ($i > $this->count) {
break;
}
$act = $this->_memberships->asActivity();
$feed->addEntryRaw($act->asString(false, false, false));
}
$this->raw($feed->getString());
}
/**
* add a new favorite
*
* @return void
*/
function addMembership()
{
// XXX: Refactor this; all the same for atompub
if (empty($this->auth_user) ||
$this->auth_user->id != $this->_profile->id) {
// TRANS: Client exception thrown when trying subscribe someone else to a group.
throw new ClientException(_("Cannot add someone else's".
" membership."), 403);
}
$xml = file_get_contents('php://input');
$dom = DOMDocument::loadXML($xml);
if ($dom->documentElement->namespaceURI != Activity::ATOM ||
$dom->documentElement->localName != 'entry') {
// TRANS: Client error displayed when not using an Atom entry.
throw new ClientException(_('Atom post must be an Atom entry.'));
return;
}
$activity = new Activity($dom->documentElement);
$membership = null;
if (Event::handle('StartAtomPubNewActivity', array(&$activity))) {
if ($activity->verb != ActivityVerb::JOIN) {
// TRANS: Client error displayed when not using the join verb.
throw new ClientException(_('Can only handle join activities.'));
}
$groupObj = $activity->objects[0];
if ($groupObj->type != ActivityObject::GROUP) {
// TRANS: Client exception thrown when trying to join something which is not a group
throw new ClientException(_('Can only join groups.'));
}
$group = User_group::getKV('uri', $groupObj->id);
if (empty($group)) {
// XXX: import from listed URL or something
// TRANS: Client exception thrown when trying to subscribe to a non-existing group.
throw new ClientException(_('Unknown group.'));
}
$old = Group_member::pkeyGet(array('profile_id' => $this->auth_user->id,
'group_id' => $group->id));
if (!empty($old)) {
// TRANS: Client exception thrown when trying to subscribe to an already subscribed group.
throw new ClientException(_('Already a member.'));
}
$profile = $this->auth_user->getProfile();
if (Group_block::isBlocked($group, $profile)) {
// XXX: import from listed URL or something
// TRANS: Client exception thrown when trying to subscribe to group while blocked from that group.
throw new ClientException(_('Blocked by admin.'));
}
$this->auth_user->joinGroup($group);
Event::handle('EndAtomPubNewActivity', array($activity, $membership));
}
if (!empty($membership)) {
$act = $membership->asActivity();
header('Content-Type: application/atom+xml; charset=utf-8');
header('Content-Location: ' . $act->selfLink);
$this->startXML();
$this->raw($act->asString(true, true, true));
$this->endXML();
}
}
/**
* Return last modified, if applicable.
*
* MAY override
*
* @return string last modified http header
*/
function lastModified()
{
// For comparison with If-Last-Modified
// If not applicable, return null
return null;
}
/**
* Return etag, if applicable.
*
* MAY override
*
* @return string etag http header
*/
function etag()
{
return null;
}
}

View File

@ -0,0 +1,159 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* Show a single membership as an Activity Streams entry
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category AtomPub
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* Show (or delete) a single membership event as an ActivityStreams entry
*
* @category AtomPub
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class AtompubshowmembershipAction extends AtompubAction
{
private $_private = null;
private $_group = null;
private $_membership = null;
protected function atompubPrepare()
{
$this->_profile = Profile::getKV('id', $this->trimmed('profile'));
if (!$this->_profile instanceof Profile) {
// TRANS: Client exception.
throw new ClientException(_('No such profile.'), 404);
}
$this->_group = User_group::getKV('id', $this->trimmed('group'));
if (!$this->_group instanceof User_group) {
// TRANS: Client exception thrown when referencing a non-existing group.
throw new ClientException(_('No such group.'), 404);
}
$kv = array('group_id' => $groupId,
'profile_id' => $this->_profile->id);
$this->_membership = Group_member::pkeyGet($kv);
if (!$this->_membership instanceof Group_member) {
// TRANS: Client exception thrown when trying to show membership of a non-subscribed group
throw new ClientException(_('Not a member.'), 404);
}
return true;
}
protected function handleGet() {
return $this->showMembership();
}
protected function handleDelete() {
return $this->deleteMembership();
}
/**
* show a single membership
*
* @return void
*/
function showMembership()
{
$activity = $this->_membership->asActivity();
header('Content-Type: application/atom+xml; charset=utf-8');
$this->startXML();
$this->raw($activity->asString(true, true, true));
$this->endXML();
return;
}
/**
* Delete the membership (leave the group)
*
* @return void
*/
function deleteMembership()
{
if (empty($this->auth_user) ||
$this->auth_user->id != $this->_profile->id) {
// TRANS: Client exception thrown when deleting someone else's membership.
throw new ClientException(_("Cannot delete someone else's".
" membership."), 403);
}
$this->auth_user->leaveGroup($this->_group);
return;
}
/**
* Return last modified, if applicable.
*
* Because the representation depends on the profile and group,
* our last modified value is the maximum of their mod time
* with the actual membership's mod time.
*
* @return string last modified http header
*/
function lastModified()
{
return max(strtotime($this->_profile->modified),
strtotime($this->_group->modified),
strtotime($this->_membership->modified));
}
/**
* Return etag, if applicable.
*
* A "weak" Etag including the profile and group id as well as
* the admin flag and ctime of the membership.
*
* @return string etag http header
*/
function etag()
{
$ctime = strtotime($this->_membership->created);
$adminflag = ($this->_membership->is_admin) ? 't' : 'f';
return 'W/"' . implode(':', array('AtomPubShowMembership',
$this->_profile->id,
$this->_group->id,
$adminflag,
$ctime)) . '"';
}
}

View File

@ -0,0 +1,184 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* Single subscription
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category AtomPub
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* Show a single subscription
*
* @category AtomPub
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class AtompubshowsubscriptionAction extends AtompubAction
{
private $_subscriber = null;
private $_subscribed = null;
private $_subscription = null;
protected function atompubPrepare()
{
$subscriberId = $this->trimmed('subscriber');
$this->_subscriber = Profile::getKV('id', $subscriberId);
if (!$this->_subscriber instanceof Profile) {
// TRANS: Client exception thrown when trying to display a subscription for a non-existing profile ID.
// TRANS: %d is the non-existing profile ID number.
throw new ClientException(sprintf(_('No such profile id: %d.'),
$subscriberId), 404);
}
$subscribedId = $this->trimmed('subscribed');
$this->_subscribed = Profile::getKV('id', $subscribedId);
if (!$this->_subscribed instanceof Profile) {
// TRANS: Client exception thrown when trying to display a subscription for a non-existing profile ID.
// TRANS: %d is the non-existing profile ID number.
throw new ClientException(sprintf(_('No such profile id: %d.'),
$subscribedId), 404);
}
$this->_subscription = Subscription::pkeyGet(array('subscriber' => $subscriberId,
'subscribed' => $subscribedId));
if (!$this->_subscription instanceof Subscription) {
// TRANS: Client exception thrown when trying to display a subscription for a non-subscribed profile ID.
// TRANS: %1$d is the non-existing subscriber ID number, $2$d is the ID of the profile that was not subscribed to.
$msg = sprintf(_('Profile %1$d not subscribed to profile %2$d.'),
$subscriberId, $subscribedId);
throw new ClientException($msg, 404);
}
return true;
}
protected function handleGet()
{
$this->showSubscription();
}
protected function handleDelete()
{
$this->deleteSubscription();
}
/**
* Show the subscription in ActivityStreams Atom format.
*
* @return void
*/
function showSubscription()
{
$activity = $this->_subscription->asActivity();
header('Content-Type: application/atom+xml; charset=utf-8');
$this->startXML();
$this->raw($activity->asString(true, true, true));
$this->endXML();
}
/**
* Delete the subscription
*
* @return void
*/
function deleteSubscription()
{
if (!$this->scoped instanceof Profile ||
$this->scoped->id != $this->_subscriber->id) {
// TRANS: Client exception thrown when trying to delete a subscription of another user.
throw new ClientException(_("Cannot delete someone else's subscription."), 403);
}
Subscription::cancel($this->_subscriber, $this->_subscribed);
}
/**
* Is this action read only?
*
* @param array $args other arguments
*
* @return boolean true
*/
function isReadOnly($args)
{
if ($_SERVER['REQUEST_METHOD'] == 'DELETE') {
return false;
}
return true;
}
/**
* Return last modified, if applicable.
*
* @return string last modified http header
*/
function lastModified()
{
return max(strtotime($this->_subscriber->modified),
strtotime($this->_subscribed->modified),
strtotime($this->_subscription->modified));
}
/**
* Etag for this object
*
* @return string etag http header
*/
function etag()
{
$mtime = strtotime($this->_subscription->modified);
return 'W/"' . implode(':', array('AtomPubShowSubscription',
$this->_subscriber->id,
$this->_subscribed->id,
$mtime)) . '"';
}
/**
* Does this require authentication?
*
* @return boolean true if delete, else false
*/
function requiresAuth()
{
if ($_SERVER['REQUEST_METHOD'] == 'DELETE') {
return true;
} else {
return false;
}
}
}

View File

@ -0,0 +1,254 @@
<?php
/**
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010, StatusNet, Inc.
*
* AtomPub subscription feed
*
* PHP version 5
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Cache
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL') && !defined('STATUSNET')) { exit(1); }
/**
* Subscription feed class for AtomPub
*
* Generates a list of the user's subscriptions
*
* @category AtomPub
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2010 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
class AtompubsubscriptionfeedAction extends AtompubAction
{
private $_profile = null;
private $_subscriptions = null;
protected function atompubPrepare()
{
$subscriber = $this->trimmed('subscriber');
$this->_profile = Profile::getKV('id', $subscriber);
if (!$this->_profile instanceof Profile) {
// TRANS: Client exception thrown when trying to display a subscription for a non-existing profile ID.
// TRANS: %d is the non-existing profile ID number.
throw new ClientException(sprintf(_('No such profile id: %d.'),
$subscriber), 404);
}
$this->_subscriptions = Subscription::bySubscriber($this->_profile->id,
$this->offset,
$this->limit);
return true;
}
protected function handleGet()
{
$this->showFeed();
}
protected function handlePost()
{
$this->addSubscription();
}
/**
* Show the feed of subscriptions
*
* @return void
*/
function showFeed()
{
header('Content-Type: application/atom+xml; charset=utf-8');
$url = common_local_url('AtomPubSubscriptionFeed',
array('subscriber' => $this->_profile->id));
$feed = new Atom10Feed(true);
$feed->addNamespace('activity',
'http://activitystrea.ms/spec/1.0/');
$feed->addNamespace('poco',
'http://portablecontacts.net/spec/1.0');
$feed->addNamespace('media',
'http://purl.org/syndication/atommedia');
$feed->addNamespace('georss',
'http://www.georss.org/georss');
$feed->id = $url;
$feed->setUpdated('now');
$feed->addAuthor($this->_profile->getBestName(),
$this->_profile->getURI());
// TRANS: Title for Atom subscription feed.
// TRANS: %s is a user nickname.
$feed->setTitle(sprintf(_("%s subscriptions"),
$this->_profile->getBestName()));
// TRANS: Subtitle for Atom subscription feed.
// TRANS: %1$s is a user nickname, %s$s is the StatusNet sitename.
$feed->setSubtitle(sprintf(_("People %1\$s has subscribed to on %2\$s"),
$this->_profile->getBestName(),
common_config('site', 'name')));
$feed->addLink(common_local_url('subscriptions',
array('nickname' =>
$this->_profile->nickname)));
$feed->addLink($url,
array('rel' => 'self',
'type' => 'application/atom+xml'));
// If there's more...
if ($this->page > 1) {
$feed->addLink($url,
array('rel' => 'first',
'type' => 'application/atom+xml'));
$feed->addLink(common_local_url('AtomPubSubscriptionFeed',
array('subscriber' =>
$this->_profile->id),
array('page' =>
$this->page - 1)),
array('rel' => 'prev',
'type' => 'application/atom+xml'));
}
if ($this->_subscriptions->N > $this->count) {
$feed->addLink(common_local_url('AtomPubSubscriptionFeed',
array('subscriber' =>
$this->_profile->id),
array('page' =>
$this->page + 1)),
array('rel' => 'next',
'type' => 'application/atom+xml'));
}
$i = 0;
// XXX: This is kind of inefficient
while ($this->_subscriptions->fetch()) {
// We get one more than needed; skip that one
$i++;
if ($i > $this->count) {
break;
}
$act = $this->_subscriptions->asActivity();
$feed->addEntryRaw($act->asString(false, false, false));
}
$this->raw($feed->getString());
}
/**
* Add a new subscription
*
* Handling the POST method for AtomPub
*
* @return void
*/
function addSubscription()
{
if (empty($this->auth_user) ||
$this->auth_user->id != $this->_profile->id) {
// TRANS: Client exception thrown when trying to subscribe another user.
throw new ClientException(_("Cannot add someone else's".
" subscription."), 403);
}
$xml = file_get_contents('php://input');
$dom = DOMDocument::loadXML($xml);
if ($dom->documentElement->namespaceURI != Activity::ATOM ||
$dom->documentElement->localName != 'entry') {
// TRANS: Client error displayed when not using an Atom entry.
$this->clientError(_('Atom post must be an Atom entry.'));
}
$activity = new Activity($dom->documentElement);
$sub = null;
if (Event::handle('StartAtomPubNewActivity', array(&$activity))) {
if ($activity->verb != ActivityVerb::FOLLOW) {
// TRANS: Client error displayed when not using the follow verb.
$this->clientError(_('Can only handle Follow activities.'));
}
$person = $activity->objects[0];
if ($person->type != ActivityObject::PERSON) {
// TRANS: Client exception thrown when subscribing to an object that is not a person.
$this->clientError(_('Can only follow people.'));
}
// XXX: OStatus discovery (maybe)
try {
$profile = Profile::fromUri($person->id);
} catch (UnknownUriException $e) {
// TRANS: Client exception thrown when subscribing to a non-existing profile.
// TRANS: %s is the unknown profile ID.
$this->clientError(sprintf(_('Unknown profile %s.'), $person->id));
}
try {
$sub = Subscription::start($this->_profile, $profile);
} catch (AlreadyFulfilledException $e) {
// 409 Conflict
$this->clientError($e->getMessage(), 409);
}
Event::handle('EndAtomPubNewActivity', array($activity, $sub));
}
if (!empty($sub)) {
$act = $sub->asActivity();
header('Content-Type: application/atom+xml; charset=utf-8');
header('Content-Location: ' . $act->selfLink);
$this->startXML();
$this->raw($act->asString(true, true, true));
$this->endXML();
}
}
}

140
actions/attachment.php Normal file
View File

@ -0,0 +1,140 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show notice attachments
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Personal
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Show notice attachments
*
* @category Personal
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class AttachmentAction extends ManagedAction
{
/**
* Attachment object to show
*/
var $attachment = null;
/**
* Load attributes based on database arguments
*
* Loads all the DB stuff
*
* @param array $args $_REQUEST array
*
* @return success flag
*/
protected function prepare(array $args=array())
{
parent::prepare($args);
if ($id = $this->trimmed('attachment')) {
$this->attachment = File::getKV($id);
}
if (!$this->attachment instanceof File) {
// TRANS: Client error displayed trying to get a non-existing attachment.
$this->clientError(_('No such attachment.'), 404);
}
return true;
}
/**
* Is this action read-only?
*
* @return boolean true
*/
function isReadOnly($args)
{
return true;
}
/**
* Title of the page
*
* @return string title of the page
*/
function title()
{
$a = new Attachment($this->attachment);
return $a->title();
}
public function showPage()
{
if (empty($this->attachment->filename)) {
// if it's not a local file, gtfo
common_redirect($this->attachment->url, 303);
}
parent::showPage();
}
/**
* Fill the content area of the page
*
* Shows a single notice list item.
*
* @return void
*/
function showContent()
{
$ali = new Attachment($this->attachment, $this);
$cnt = $ali->show();
}
/**
* Don't show page notice
*
* @return void
*/
function showPageNoticeBlock()
{
}
/**
* Show aside: this attachments appears in what notices
*
* @return void
*/
function showSections() {
$ns = new AttachmentNoticeSection($this);
$ns->show();
if (!common_config('performance', 'high')) {
$atcs = new AttachmentTagCloudSection($this);
$atcs->show();
}
}
}

View File

@ -0,0 +1,67 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show notice attachments
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Personal
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Show notice attachments
*
* @category Personal
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class Attachment_thumbnailAction extends AttachmentAction
{
protected $thumb_w = null; // max width
protected $thumb_h = null; // max height
protected $thumb_c = null; // crop?
protected function doPreparation()
{
parent::doPreparation();
$this->thumb_w = $this->int('w');
$this->thumb_h = $this->int('h');
$this->thumb_c = $this->boolean('c');
}
public function showPage()
{
// Returns a File_thumbnail object or throws exception if not available
try {
$thumbnail = $this->attachment->getThumbnail($this->thumb_w, $this->thumb_h, $this->thumb_c);
} catch (UseFileAsThumbnailException $e) {
common_redirect($e->file->getUrl(), 302);
}
common_redirect(File_thumbnail::url($thumbnail->filename), 302);
}
}

View File

@ -0,0 +1,92 @@
<?php
/**
* Retrieve user avatar by nickname action class.
*
* PHP version 5
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Robin Millette <millette@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Retrieve user avatar by nickname action class.
*
* @category Action
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @author Robin Millette <millette@status.net>
* @author Mikael Nordfeldth <mmn@hethane.se>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://www.gnu.org/software/social/
*/
class AvatarbynicknameAction extends Action
{
/**
* Class handler.
*
* @param array $args query arguments
*
* @return boolean false if nickname or user isn't found
*/
protected function handle()
{
parent::handle();
$nickname = $this->trimmed('nickname');
if (!$nickname) {
// TRANS: Client error displayed trying to get an avatar without providing a nickname.
$this->clientError(_('No nickname.'));
}
$size = $this->trimmed('size') ?: 'original';
$user = User::getKV('nickname', $nickname);
if (!$user) {
// TRANS: Client error displayed trying to get an avatar for a non-existing user.
$this->clientError(_('No such user.'));
}
$profile = $user->getProfile();
if (!$profile) {
// TRANS: Error message displayed when referring to a user without a profile.
$this->clientError(_('User has no profile.'));
}
if ($size === 'original') {
try {
$avatar = Avatar::getUploaded($profile);
$url = $avatar->displayUrl();
} catch (NoAvatarException $e) {
$url = Avatar::defaultImage(AVATAR_PROFILE_SIZE);
}
} else {
$url = $profile->avatarUrl($size);
}
common_redirect($url, 302);
}
function isReadOnly($args)
{
return true;
}
}

394
actions/avatarsettings.php Normal file
View File

@ -0,0 +1,394 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Upload an avatar
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Settings
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Upload an avatar
*
* We use jCrop plugin for jQuery to crop the image after upload.
*
* @category Settings
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @author Zach Copley <zach@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class AvatarsettingsAction extends SettingsAction
{
var $mode = null;
var $imagefile = null;
var $filename = null;
/**
* Title of the page
*
* @return string Title of the page
*/
function title()
{
// TRANS: Title for avatar upload page.
return _('Avatar');
}
/**
* Instructions for use
*
* @return instructions for use
*/
function getInstructions()
{
// TRANS: Instruction for avatar upload page.
// TRANS: %s is the maximum file size, for example "500b", "10kB" or "2MB".
return sprintf(_('You can upload your personal avatar. The maximum file size is %s.'),
ImageFile::maxFileSize());
}
/**
* Content area of the page
*
* Shows a form for uploading an avatar. Currently overrides FormAction's showContent
* since we haven't made classes out of AvatarCropForm and AvatarUploadForm.
*
* @return void
*/
function showContent()
{
if ($this->mode == 'crop') {
$this->showCropForm();
} else {
$this->showUploadForm();
}
}
function showUploadForm()
{
$user = common_current_user();
$profile = $user->getProfile();
if (!$profile) {
common_log_db_error($user, 'SELECT', __FILE__);
// TRANS: Error message displayed when referring to a user without a profile.
$this->serverError(_('User has no profile.'));
}
$this->elementStart('form', array('enctype' => 'multipart/form-data',
'method' => 'post',
'id' => 'form_settings_avatar',
'class' => 'form_settings',
'action' =>
common_local_url('avatarsettings')));
$this->elementStart('fieldset');
// TRANS: Avatar upload page form legend.
$this->element('legend', null, _('Avatar settings'));
$this->hidden('token', common_session_token());
if (Event::handle('StartAvatarFormData', array($this))) {
$this->elementStart('ul', 'form_data');
try {
$original = Avatar::getUploaded($profile);
$this->elementStart('li', array('id' => 'avatar_original',
'class' => 'avatar_view'));
// TRANS: Header on avatar upload page for thumbnail of originally uploaded avatar (h2).
$this->element('h2', null, _("Original"));
$this->elementStart('div', array('id'=>'avatar_original_view'));
$this->element('img', array('src' => $original->displayUrl(),
'width' => $original->width,
'height' => $original->height,
'alt' => $user->nickname));
$this->elementEnd('div');
$this->elementEnd('li');
} catch (NoAvatarException $e) {
// No original avatar found!
}
try {
$avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
$this->elementStart('li', array('id' => 'avatar_preview',
'class' => 'avatar_view'));
// TRANS: Header on avatar upload page for thumbnail of to be used rendition of uploaded avatar (h2).
$this->element('h2', null, _("Preview"));
$this->elementStart('div', array('id'=>'avatar_preview_view'));
$this->element('img', array('src' => $avatar->displayUrl(),
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
'alt' => $user->nickname));
$this->elementEnd('div');
if (!empty($avatar->filename)) {
// TRANS: Button on avatar upload page to delete current avatar.
$this->submit('delete', _m('BUTTON','Delete'));
}
$this->elementEnd('li');
} catch (NoAvatarException $e) {
// No previously uploaded avatar to preview.
}
$this->elementStart('li', array ('id' => 'settings_attach'));
$this->element('input', array('name' => 'MAX_FILE_SIZE',
'type' => 'hidden',
'id' => 'MAX_FILE_SIZE',
'value' => ImageFile::maxFileSizeInt()));
$this->element('input', array('name' => 'avatarfile',
'type' => 'file',
'id' => 'avatarfile'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementStart('ul', 'form_actions');
$this->elementStart('li');
// TRANS: Button on avatar upload page to upload an avatar.
$this->submit('upload', _m('BUTTON','Upload'));
$this->elementEnd('li');
$this->elementEnd('ul');
}
Event::handle('EndAvatarFormData', array($this));
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
function showCropForm()
{
$user = common_current_user();
$profile = $user->getProfile();
if (!$profile) {
common_log_db_error($user, 'SELECT', __FILE__);
// TRANS: Error message displayed when referring to a user without a profile.
$this->serverError(_('User has no profile.'));
}
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_avatar',
'class' => 'form_settings',
'action' =>
common_local_url('avatarsettings')));
$this->elementStart('fieldset');
// TRANS: Avatar upload page crop form legend.
$this->element('legend', null, _('Avatar settings'));
$this->hidden('token', common_session_token());
$this->elementStart('ul', 'form_data');
$this->elementStart('li',
array('id' => 'avatar_original',
'class' => 'avatar_view'));
// TRANS: Header on avatar upload crop form for thumbnail of originally uploaded avatar (h2).
$this->element('h2', null, _('Original'));
$this->elementStart('div', array('id'=>'avatar_original_view'));
$this->element('img', array('src' => Avatar::url($this->filedata['filename']),
'width' => $this->filedata['width'],
'height' => $this->filedata['height'],
'alt' => $user->nickname));
$this->elementEnd('div');
$this->elementEnd('li');
$this->elementStart('li',
array('id' => 'avatar_preview',
'class' => 'avatar_view'));
// TRANS: Header on avatar upload crop form for thumbnail of to be used rendition of uploaded avatar (h2).
$this->element('h2', null, _('Preview'));
$this->elementStart('div', array('id'=>'avatar_preview_view'));
$this->element('img', array('src' => Avatar::url($this->filedata['filename']),
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
'alt' => $user->nickname));
$this->elementEnd('div');
foreach (array('avatar_crop_x', 'avatar_crop_y',
'avatar_crop_w', 'avatar_crop_h') as $crop_info) {
$this->element('input', array('name' => $crop_info,
'type' => 'hidden',
'id' => $crop_info));
}
// TRANS: Button on avatar upload crop form to confirm a selected crop as avatar.
$this->submit('crop', _m('BUTTON','Crop'));
$this->elementEnd('li');
$this->elementEnd('ul');
$this->elementEnd('fieldset');
$this->elementEnd('form');
}
protected function doPost()
{
if (Event::handle('StartAvatarSaveForm', array($this))) {
if ($this->trimmed('upload')) {
return $this->uploadAvatar();
} else if ($this->trimmed('crop')) {
return $this->cropAvatar();
} else if ($this->trimmed('delete')) {
return $this->deleteAvatar();
} else {
// TRANS: Unexpected validation error on avatar upload form.
throw new ClientException(_('Unexpected form submission.'));
}
Event::handle('EndAvatarSaveForm', array($this));
}
}
/**
* Handle an image upload
*
* Does all the magic for handling an image upload, and crops the
* image by default.
*
* @return void
*/
function uploadAvatar()
{
// ImageFile throws exception if something goes wrong, which we'll
// pick up and show as an error message above the form.
$imagefile = ImageFile::fromUpload('avatarfile');
$type = $imagefile->preferredType();
$filename = Avatar::filename($this->scoped->getID(),
image_type_to_extension($type),
null,
'tmp'.common_timestamp());
$filepath = Avatar::path($filename);
$imagefile = $imagefile->copyTo($filepath);
$filedata = array('filename' => $filename,
'filepath' => $filepath,
'width' => $imagefile->width,
'height' => $imagefile->height,
'type' => $type);
$_SESSION['FILEDATA'] = $filedata;
$this->filedata = $filedata;
$this->mode = 'crop';
// TRANS: Avatar upload form instruction after uploading a file.
return _('Pick a square area of the image to be your avatar.');
}
/**
* Handle the results of jcrop.
*
* @return void
*/
public function cropAvatar()
{
$filedata = $_SESSION['FILEDATA'];
if (empty($filedata)) {
// TRANS: Server error displayed if an avatar upload went wrong somehow server side.
throw new ServerException(_('Lost our file data.'));
}
$file_d = min($filedata['width'], $filedata['height']);
$dest_x = $this->arg('avatar_crop_x') ? $this->arg('avatar_crop_x'):0;
$dest_y = $this->arg('avatar_crop_y') ? $this->arg('avatar_crop_y'):0;
$dest_w = $this->arg('avatar_crop_w') ? $this->arg('avatar_crop_w'):$file_d;
$dest_h = $this->arg('avatar_crop_h') ? $this->arg('avatar_crop_h'):$file_d;
$size = intval(min($dest_w, $dest_h, common_config('avatar', 'maxsize')));
$box = array('width' => $size, 'height' => $size,
'x' => $dest_x, 'y' => $dest_y,
'w' => $dest_w, 'h' => $dest_h);
$imagefile = new ImageFile(null, $filedata['filepath']);
$filename = Avatar::filename($this->scoped->getID(), image_type_to_extension($imagefile->preferredType()),
$size, common_timestamp());
try {
$imagefile->resizeTo(Avatar::path($filename), $box);
} catch (UseFileAsThumbnailException $e) {
common_debug('Using uploaded avatar directly without resizing, copying it to: '.$filename);
if (!copy($filedata['filepath'], Avatar::path($filename))) {
common_debug('Tried to copy image file '.$filedata['filepath'].' to destination '.Avatar::path($filename));
throw new ServerException('Could not copy file to destination.');
}
}
if ($this->scoped->setOriginal($filename)) {
@unlink($filedata['filepath']);
unset($_SESSION['FILEDATA']);
$this->mode = 'upload';
// TRANS: Success message for having updated a user avatar.
return _('Avatar updated.');
}
// TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
throw new ServerException(_('Failed updating avatar.'));
}
/**
* Get rid of the current avatar.
*
* @return void
*/
function deleteAvatar()
{
Avatar::deleteFromProfile($this->scoped);
// TRANS: Success message for deleting a user avatar.
return _('Avatar deleted.');
}
/**
* Add the jCrop stylesheet
*
* @return void
*/
function showStylesheets()
{
parent::showStylesheets();
$this->cssLink('js/extlib/jquery-jcrop/css/jcrop.css','base','screen, projection, tv');
}
/**
* Add the jCrop scripts
*
* @return void
*/
function showScripts()
{
parent::showScripts();
if ($this->mode == 'crop') {
$this->script('extlib/jquery-jcrop/jcrop.js');
$this->script('jcrop.go.js');
}
$this->autofocus('avatarfile');
}
}

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