Compare commits
1 次程式碼提交
作者 | SHA1 | 提交日期 | |
---|---|---|---|
|
bdd55f611f |
@ -1,3 +0,0 @@
|
||||
((nil . ((php-project-root . auto)
|
||||
(phpstan-executable . (root . "bin/phpstan"))
|
||||
)
|
43
.env
43
.env
@ -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 ###
|
@ -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/social
|
81
.gitignore
vendored
81
.gitignore
vendored
@ -1,64 +1,19 @@
|
||||
|
||||
###> 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/
|
||||
logs/
|
||||
log/
|
||||
run/
|
||||
config.php
|
||||
/file
|
||||
|
||||
notes
|
||||
|
||||
|
||||
.test_coverage_report
|
||||
.phpunit_cache
|
||||
.htaccess
|
||||
httpd.conf
|
||||
dataobject.ini
|
||||
*.bak
|
||||
*.orig
|
||||
*.rej
|
||||
TODO.rym
|
||||
config-*.php
|
||||
good-config.php
|
||||
*.mo
|
||||
/vendor/
|
||||
|
@ -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' => true,
|
||||
// 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__),
|
||||
);
|
139
CHANGELOG.md
Normal file
139
CHANGELOG.md
Normal file
@ -0,0 +1,139 @@
|
||||
# GNU social - Log of Changes
|
||||
|
||||
## 2.0.0 - THIS. IS. GNU SOCIAL!!! [WIP]
|
||||
|
||||
Release name chosen after 300 by Frank Miller where the main protagonist Leonidas, King of Sparta, declines peace with the
|
||||
Persians, after being disrespected, by shouting at the Persian Messenger "This is Sparta!" and kicking him into a large well
|
||||
proceeded by the killing of the other Persian messengers.
|
||||
|
||||
### Major changes from previous release:
|
||||
|
||||
Load and Storage:
|
||||
- New media handling system
|
||||
- GS is now structurely divided in includes and public
|
||||
- OEmbed upgraded to Embed plugin (Now we provide Open Graph information too)
|
||||
|
||||
General:
|
||||
- Composer was integrated
|
||||
|
||||
Modules:
|
||||
- Restored built-in plugins
|
||||
- New modules system: core plugins and plugins physically separated
|
||||
- Refactor of Plugin API to better illustrate the idea of modules
|
||||
- Bug fixes of core modules logic
|
||||
|
||||
#### TODO before alpha:
|
||||
|
||||
Load and Storage:
|
||||
- Upgrade STOMP queue
|
||||
- Add Redis based caching and queues
|
||||
- Review memcached based cache
|
||||
- Port PEAR DB to PDO_DataObject
|
||||
- Support PostgreSQL
|
||||
|
||||
Network:
|
||||
- Port PEAR HTTP to Guzzle
|
||||
- Port PEAR Mail to PHPSendMail
|
||||
- Add OAuth2 support (deprecate OAuth1)
|
||||
- Add shinny new Plugins management interface for sysadmins together with a new doc for devs
|
||||
|
||||
Federation:
|
||||
- Add ActivityPub support
|
||||
- Fix audience targeting
|
||||
- Add Group Actor Type
|
||||
- OstatusSub: Remote follow OS and AP profiles via OStatusSub
|
||||
- ActorLists: Allow to create collections of Actors and to interact with them - supports both OS and AP
|
||||
- The Free Network: Automagically migrate internal remote profiles between Free Network protocols (check Nodeinfo)
|
||||
- Enable the search box to import remote notices and profiles
|
||||
|
||||
General:
|
||||
- Fix failling unit tests
|
||||
- Improve Cronish
|
||||
- Run session garbage collection
|
||||
- Cleanup Email Registration
|
||||
- Refactoring of confirmation codes
|
||||
- Refactoring of Exceptions
|
||||
|
||||
Modules:
|
||||
- Document conversion of older plugins to the new GS 2
|
||||
- Create installer for v2 plugins
|
||||
- Introduce new metadata for plugins (category and thumb)
|
||||
- Improve plugin management tool (add install form and better UI that makes use of new metadata)
|
||||
- Add plugin management tool as a install step
|
||||
- Allow to install remote plugins and suggest popular trusted ones
|
||||
|
||||
## v1.20.9release - The Invicta Crusade
|
||||
|
||||
Release name chosen after Porto city. Porto is one of the oldest cities in Europe and thanks to its fierce resistance
|
||||
during two battles and sieges in history, it has earned the epithet of ‘Cidade Invicta’ (Invincible City). The dev team
|
||||
behind this release studies in Porto, Portugal.
|
||||
|
||||
Dropped Support for PHP5.6.x. Minimum PHP version now is 7.0.0.
|
||||
|
||||
Major changes from previous release:
|
||||
|
||||
- Various patches on PEAR related components
|
||||
- Various database related improvements
|
||||
- Improved XMPP support
|
||||
- Added Nodeinfo support
|
||||
- Various i18n and l10n bug fixes
|
||||
- Improvements on Internal Session Handler
|
||||
- Improvements on OpenID support
|
||||
- Improved Media handling and safer upload
|
||||
- Redirect to previous page after login
|
||||
- Initial work on full conversion to PHP7
|
||||
- Initial work on a better documentation
|
||||
- Allow login with email
|
||||
- Various bug fixes
|
||||
|
||||
## v1.2.0beta4 - The good reign of PHP5
|
||||
|
||||
Dropped support for PHP5.4.
|
||||
|
||||
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)
|
||||
|
||||
## v1.1.3release - The Spanish Invasion
|
||||
|
||||
New this version
|
||||
|
||||
This is a security fix and bug fix release since 1.1.3-beta2. All 1.1.x sites should upgrade to this version.
|
||||
|
||||
So far it includes the following changes:
|
||||
|
||||
- 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
|
||||
- Backing up a user's account now appears to work as it should
|
||||
|
||||
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)
|
95
CODE_OF_CONDUCT.md
Normal file
95
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,95 @@
|
||||
## Code of Conduct
|
||||
|
||||
### Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
### Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
### Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
### Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
### Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at mattl@gnu.org. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
### Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
||||
|
||||
## The Code of Conflict
|
||||
|
||||
GNU social has a high submission standard and we want to keep quality code in the
|
||||
codebase and bad code out of it. As such your code will be closely scrutinized,
|
||||
and you might take this criticism personally. Please understand that this is
|
||||
meant to keep the standards of the codebase up, and isn't meant personally. All
|
||||
the same, this isn't an excuse for poor behaviour, and a reviewer shouldn't be
|
||||
misbehaving towards submitters.
|
||||
|
||||
|
||||
If however, anyone feels personally abused, threatened, or otherwise
|
||||
uncomfortable due to this process, that is not acceptable. If so, please
|
||||
contact the project team at mattl@gnu.org, and they will work to resolve the issue
|
||||
to the best of their ability.
|
||||
|
||||
As a reviewer of code, please strive to keep things civil and focused on the
|
||||
technical issues involved. We are all humans, and frustrations can be high on
|
||||
both sides of the process. Try to keep in mind the immortal words of Bill and
|
||||
Ted, "Be excellent to each other."
|
1
CONTRIBUTING.md
Normal file
1
CONTRIBUTING.md
Normal file
@ -0,0 +1 @@
|
||||
GNU social's contributing resources and instructions are made available at [DOCUMENTATION/DEVELOPERS](https://notabug.org/diogo/gnu-social/src/nightly/DOCUMENTATION/DEVELOPERS).
|
661
COPYING
Normal file
661
COPYING
Normal file
@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
112
CREDITS.md
112
CREDITS.md
@ -3,66 +3,60 @@ 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
|
||||
oversight; please let mattl@gnu.org know if
|
||||
anyone's been overlooked in error.
|
||||
|
||||
Current team
|
||||
------------
|
||||
* Alexei Sorokin
|
||||
* Matt Lee
|
||||
* Mikael Nordfeldth
|
||||
* Diogo Cordeiro
|
||||
* Eliseu Amaro
|
||||
* Hugo Sales
|
||||
|
||||
V2 team
|
||||
-------
|
||||
* Diogo Cordeiro
|
||||
* Alexei Sorokin
|
||||
* Bruno Casteleiro
|
||||
* Miguel Dantas
|
||||
* Alexei Sorokin
|
||||
|
||||
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
|
||||
* Garret Buell
|
||||
* Mike Cochrane
|
||||
* Matthew Gregg
|
||||
* Sean Murphy
|
||||
* Stéphane Bérubé
|
||||
* Steven DuBois
|
||||
* 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
|
||||
* Craig Andrews
|
||||
* Donald Robertson
|
||||
* Deb Nicholson
|
||||
* Ian Denhart
|
||||
* Steven DuBois
|
||||
* Blaine Cook
|
||||
* Henry Story
|
||||
* Melvin Carvalho
|
||||
* chimo
|
||||
* Akio
|
||||
* Maiyannah Bishop
|
||||
* Bob Mottram
|
||||
* David Yip
|
||||
* Neil E Hodges
|
||||
* Moonman
|
||||
* Normandy
|
||||
* Verius
|
||||
* Alexei Sorokin
|
||||
* Daniel Supernault
|
||||
|
||||
Credits for StatusNet
|
||||
--------------
|
||||
@ -71,24 +65,24 @@ Leads
|
||||
* 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
|
||||
* Robin Millette
|
||||
* Brion Vibber
|
||||
* James Walker
|
||||
* Samantha Doherty
|
||||
* Florian Biree
|
||||
* Erik Stambaugh
|
||||
* 'drry'
|
||||
* Gina Haeussge
|
||||
* Tryggvi Björgvinsson
|
||||
* Adrian Lang
|
||||
* Ori Avtalion
|
||||
* Meitar Moscovitz
|
||||
* Ken Sheppardson
|
||||
* Simon Waters, Surevine
|
||||
* Joshua Judson Rosen (rozzin)
|
||||
|
||||
Translators
|
||||
-----------
|
||||
|
61
DOCUMENTATION/DEVELOPERS/CONTRIBUTING/boilerplate.php
Normal file
61
DOCUMENTATION/DEVELOPERS/CONTRIBUTING/boilerplate.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
// GNU social 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.
|
||||
//
|
||||
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Description of this file.
|
||||
*
|
||||
* @package samples
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
|
||||
namespace samples;
|
||||
|
||||
defined('GNUSOCIAL') || die();
|
||||
|
||||
require_once __DIR__ . DIRECTORY_SEPARATOR . 'SampleHandler.php';
|
||||
|
||||
/**
|
||||
* Description of this class.
|
||||
*
|
||||
* @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
class MySampleClass
|
||||
{
|
||||
/**
|
||||
* Constructor for the sample class.
|
||||
*
|
||||
* @param string $dummy_word just because.
|
||||
* @param int $result another just because.
|
||||
*/
|
||||
public function __construct(string $dummy_word = '', ?int $result = null)
|
||||
{
|
||||
global $demo;
|
||||
$this->niceWorld();
|
||||
}
|
||||
|
||||
/**
|
||||
* How cool is this function.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function niceWorld(): string
|
||||
{
|
||||
return 'hello, world.';
|
||||
}
|
||||
}
|
275
DOCUMENTATION/DEVELOPERS/CONTRIBUTING/coding_standards.md
Normal file
275
DOCUMENTATION/DEVELOPERS/CONTRIBUTING/coding_standards.md
Normal file
@ -0,0 +1,275 @@
|
||||
GNU social Coding Style
|
||||
===========================
|
||||
|
||||
Please comply with [PSR-2](https://www.php-fig.org/psr/psr-2/) and the following standard when working on GNU social
|
||||
if you want your patches accepted and modules included in supported releases.
|
||||
|
||||
If you see code which doesn't comply with the below, please fix it :)
|
||||
|
||||
|
||||
Strings
|
||||
-------------------------------------------------------------------------------
|
||||
Use `'` instead of `"` for strings, where substitutions aren't required.
|
||||
This is a performance issue, and prevents a lot of inconsistent coding styles.
|
||||
When using substitutions, use curly braces around your variables - like so:
|
||||
|
||||
$var = "my_var: {$my_var}";
|
||||
|
||||
|
||||
Comments and Documentation
|
||||
-------------------------------------------------------------------------------
|
||||
Comments go on the line ABOVE the code, NOT to the right of the code, unless it is very short.
|
||||
All functions and methods are to be documented using PhpDocumentor - https://docs.phpdoc.org/guides/
|
||||
|
||||
File Headers
|
||||
-------------------------------------------------------------------------------
|
||||
File headers follow a consistent format, as such:
|
||||
|
||||
// This file is part of GNU social - https://www.gnu.org/software/social
|
||||
//
|
||||
// GNU social 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.
|
||||
//
|
||||
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Description of this file.
|
||||
*
|
||||
* @package samples
|
||||
* @author Diogo Cordeiro <diogo@fc.up.pt>
|
||||
* @copyright 2019 Free Software Foundation, Inc http://www.fsf.org
|
||||
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
||||
*/
|
||||
|
||||
Please use it.
|
||||
|
||||
A few notes:
|
||||
|
||||
* The description of the file doesn't have to be exhaustive. Rather it's
|
||||
meant to be a short summary of what's in this file and what it does. Try
|
||||
to keep it to 1-5 lines. You can get more in-depth when documenting
|
||||
individual functions!
|
||||
|
||||
* You'll probably see files with multiple authors, this is by
|
||||
design - many people contributed to GNU social or its forebears! If you
|
||||
are modifying an existing file, APPEND your own author line, and update
|
||||
the copyright year if needed. Do not replace existing ones.
|
||||
|
||||
You may find `boilerplate.php` useful when creating a new file from scratch.
|
||||
|
||||
Paragraph spacing
|
||||
-------------------------------------------------------------------------------
|
||||
Where-ever possible, try to keep the lines to 80 characters. Don't
|
||||
sacrifice readability for it though - if it makes more sense to have it in
|
||||
one longer line, and it's more easily read that way, that's fine.
|
||||
|
||||
With assignments, avoid breaking them down into multiple lines unless
|
||||
neccesary, except for enumerations and arrays.
|
||||
|
||||
|
||||
'If' statements format
|
||||
-------------------------------------------------------------------------------
|
||||
Use switch statements where many else if's are going to be used. Switch/case is faster
|
||||
|
||||
if ($var == 'example') {
|
||||
echo 'This is only an example';
|
||||
} else {
|
||||
echo 'This is not a test. This is the real thing';
|
||||
}
|
||||
|
||||
Do NOT make if statements like this:
|
||||
|
||||
if ($var == 'example'){ echo 'An example'; }
|
||||
|
||||
OR this
|
||||
|
||||
if($var = 'example')
|
||||
echo "An {$var}";
|
||||
|
||||
|
||||
Associative arrays
|
||||
-------------------------------------------------------------------------------
|
||||
Always use `[]` instead of `array()`. Associative arrays must be written in the
|
||||
following manner:
|
||||
|
||||
$array = [
|
||||
'var' => 'value',
|
||||
'var2' => 'value2'
|
||||
];
|
||||
|
||||
Note that spaces are preferred around the '=>'.
|
||||
|
||||
|
||||
A note about shorthands
|
||||
-------------------------------------------------------------------------------
|
||||
Some short hands are evil:
|
||||
|
||||
- Use the long format for `<?php`. Do NOT use `<?`.
|
||||
- Use the long format for `<?php` echo. Do NOT use `<?=`.
|
||||
|
||||
|
||||
Naming conventions
|
||||
-------------------------------------------------------------------------------
|
||||
Respect PSR2 first.
|
||||
|
||||
- Classes use PascalCase (e.g. MyClass).
|
||||
- Functions/Methods use camelCase (e.g. myFunction).
|
||||
- Variables use snake_case (e.g. my_variable).
|
||||
|
||||
A note on variable names, etc. It must be possible to understand what is meant
|
||||
without neccesarialy seeing it in context, because the code that calls something
|
||||
might not always make it clear.
|
||||
|
||||
So if you have something like:
|
||||
|
||||
$notice->post($contents);
|
||||
|
||||
Well I can easily tell what you're doing there because the names are straight-
|
||||
forward and clear.
|
||||
|
||||
Something like this:
|
||||
|
||||
foo->bar();
|
||||
|
||||
Is much less clear.
|
||||
|
||||
Also, whereever possible, avoid ambiguous terms. For example, don't use text
|
||||
as a term for a variable. Call back to "contents" above.
|
||||
|
||||
|
||||
Comparisons
|
||||
-------------------------------------------------------------------------------
|
||||
Always use symbol based comparison operators (&&, ||) instead of text based
|
||||
operators (AND, OR) as they are evaluated in different orders and at different
|
||||
speeds. This is will prevent any confusion or strange results.
|
||||
|
||||
|
||||
Use English
|
||||
-------------------------------------------------------------------------------
|
||||
All variables, classes, methods, functions and comments must be in English.
|
||||
Bad english is easier to work with than having to babelfish code to work out
|
||||
how it works.
|
||||
|
||||
|
||||
Encoding
|
||||
-------------------------------------------------------------------------------
|
||||
Files should be in UTF-8 encoding with UNIX line endings.
|
||||
|
||||
|
||||
No ending tag
|
||||
-------------------------------------------------------------------------------
|
||||
Files should not end with an ending php tag "?>". Any whitespace after the
|
||||
closing tag is sent to the browser and cause errors, so don't include them.
|
||||
|
||||
|
||||
Nesting Functions
|
||||
-------------------------------------------------------------------------------
|
||||
Avoid, if at all possible. When not possible, document the living daylights
|
||||
out of why you're nesting it. It's not always avoidable, but PHP 5 has a lot
|
||||
of obscure problems that come up with using nested functions.
|
||||
|
||||
If you must use a nested function, be sure to have robust error-handling.
|
||||
This is a must and submissions including nested functions that do not have
|
||||
robust error handling will be rejected and you'll be asked to add it.
|
||||
|
||||
|
||||
Scoping
|
||||
-------------------------------------------------------------------------------
|
||||
Properly enforcing scope of functions is something many PHP programmers don't
|
||||
do, but should.
|
||||
|
||||
In general:
|
||||
* Variables unique to a class should be protected and use interfacing to
|
||||
change them. This allows for input validation and making sure we don't have
|
||||
injection, especially when something's exposed to the API, that any program
|
||||
can use, and not all of them are going to be be safe and trusted.
|
||||
|
||||
* Variables not unique to a class should be validated prior to every call,
|
||||
which is why it's generally not a good idea to re-use stuff across classes
|
||||
unless there's significant performance gains to doing so.
|
||||
|
||||
* Classes should protect functions that they do not want overriden, but they
|
||||
should avoid protecting the constructor and destructor and related helper
|
||||
functions as this prevents proper inheritance.
|
||||
|
||||
|
||||
Typecasting
|
||||
-------------------------------------------------------------------------------
|
||||
PHP is a soft-typed language and it falls to us developers to make sure that
|
||||
we are using the proper inputs. Where ever possible use explicit type casting.
|
||||
Where it in't, you're going to have to make sure that you check all your
|
||||
inputs before you pass them.
|
||||
|
||||
All outputs should be cast as an explicit PHP type.
|
||||
|
||||
Not properly typecasting is a shooting offence. Soft types let programmers
|
||||
get away with a lot of lazy code, but lazy code is buggy code, and frankly, I
|
||||
don't want it in GNU social if it's going to be buggy.
|
||||
|
||||
|
||||
Consistent exception handling
|
||||
-------------------------------------------------------------------------------
|
||||
Consistency is key to good code to begin with, but it is especially important
|
||||
to be consistent with how we handle errors. GNU social has a variety of built-
|
||||
in exception classes. Use them, wherever it's possible and appropriate, and
|
||||
they will do the heavy lifting for you.
|
||||
|
||||
Additionally, ensure you clean up any and all records and variables that need
|
||||
cleanup in a function using try { } finally { } even if you do not plan on
|
||||
catching exceptions (why wouldn't you, though? That's silly.)
|
||||
|
||||
If you do not call an exception handler, you must, at a minimum, record errors
|
||||
to the log using common_log(level, message)
|
||||
|
||||
Ensure all possible control flows of a function have exception handling and
|
||||
cleanup, where appropriate. Don't leave endpoints with unhandled exceptions.
|
||||
Try not to leave something in an error state if it's avoidable.
|
||||
|
||||
|
||||
Return values
|
||||
-------------------------------------------------------------------------------
|
||||
All functions must return a value. Every single one. This is not optional.
|
||||
|
||||
If you are simply making a procedure call, for example as part of a helper
|
||||
function, then return boolean TRUE on success, and the exception on failure.
|
||||
|
||||
When returning the exception, return the whole nine yards, which is to say the
|
||||
actual PHP exception object, not just an error message.
|
||||
|
||||
All return values not the above should be type cast, and you should sanitize
|
||||
anything returned to ensure it fits into the cast. You might technically make
|
||||
an integer a string, for instance, but you should be making sure that integer
|
||||
SHOULD be a string, if you're returning it, and that it is a valid return
|
||||
value.
|
||||
|
||||
A vast majority of programming errors come down to not checking your inputs
|
||||
and outputs properly, so please try to do so as best and thoroughly as you can.
|
||||
|
||||
|
||||
Layout and Location of files
|
||||
-------------------------------------------------------------------------------
|
||||
`/actions/` contains files that determine what happens when something "happens":
|
||||
for instance, when someone favourites or repeats a notice. Code that is
|
||||
related to a "happening" should go here.
|
||||
|
||||
`/classes/` contains abstract definitions of certain "things" in the codebase
|
||||
such as a user or notice. If you're making a new "thing", it goes here.
|
||||
|
||||
`/lib/` is basically the back-end. Actions will call something in here to get
|
||||
stuff done usually, which in turn will probably manipulate information stored
|
||||
in one or more records represented by a class.
|
||||
|
||||
`/extlib/` is where external libraries are located. If you include a new
|
||||
external library, it goes here.
|
||||
|
||||
`/plugins/` This is a great way to modularize your own new features. If you want
|
||||
to create new core features for GNU social, it is probably best to create a
|
||||
module unless you absolutely must override or modify the core behaviours.
|
@ -0,0 +1,32 @@
|
||||
Submission Checklist
|
||||
================================================================================
|
||||
This document serves as a handy checklist for submitted merges and patches to
|
||||
the GNU social project. Following it isn't a gaurantee a patch will be accepted,
|
||||
but it will help you avoid common problems.
|
||||
|
||||
1. Ensure all code control paths in all functions return a value.
|
||||
|
||||
2. Ensure all exceptions are trapped in an exception class, or minimally,
|
||||
written to the log with common_log
|
||||
|
||||
3. Ensure the coding format standards are adhered to (see coding_standards.md)
|
||||
|
||||
4. Ensure that any new class that deals in public data has a corresponding new
|
||||
API endpoint.
|
||||
|
||||
5. Ensure that all new API endpoints sanitize inputs and outputs properly.
|
||||
|
||||
6. Ensure that your version of the code works with PHP 7 on a standard
|
||||
LAMP and LEMP stack (Linux+Apache+MariaDB+PHP and Linux+nginx+MariaDB+PHP)
|
||||
|
||||
7. If implementing new database functions, ensure they work with MariaDB
|
||||
and postgreSQL.
|
||||
|
||||
8. Ensure all data that federates does so properly and has mechanisms to
|
||||
catch and accomodate for federation transmission failure.
|
||||
|
||||
9. Ensure that nothing is left in an error state when it is avoidable.
|
||||
|
||||
10. Ensure that all code submitted is properly documented.
|
||||
|
||||
11. Ensure that there are no PHP Strict Standards or Parse errors in the code.
|
1498
DOCUMENTATION/DEVELOPERS/EVENTS.txt
Normal file
1498
DOCUMENTATION/DEVELOPERS/EVENTS.txt
Normal file
文件差異過大導致無法顯示
Load Diff
355
DOCUMENTATION/DEVELOPERS/Plugins/README.md
Normal file
355
DOCUMENTATION/DEVELOPERS/Plugins/README.md
Normal file
@ -0,0 +1,355 @@
|
||||
Plugin Development
|
||||
=======================
|
||||
|
||||
SamplePlugin.php
|
||||
-----------------------
|
||||
|
||||
Each plugin requires a main class to interact with the GNU social system.
|
||||
|
||||
The main class usually extends the Plugin class that comes with GNU social.
|
||||
|
||||
The class has standard-named methods that will be called when certain events
|
||||
happen in the code base. These methods have names like 'onX' where X is an
|
||||
event name (see EVENTS.txt for the list of available events). Event handlers
|
||||
have pre-defined arguments, based on which event they're handling. A typical
|
||||
event handler:
|
||||
|
||||
```php
|
||||
function onSomeEvent($paramA, &$paramB)
|
||||
{
|
||||
if ($paramA == 'jed') {
|
||||
throw new Exception(sprintf(_m("Invalid parameter %s"), $paramA));
|
||||
}
|
||||
$paramB = 'spock';
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Event Handlers
|
||||
-----------------------
|
||||
|
||||
Event handlers must return a Boolean value.
|
||||
|
||||
If they return false, all other event handlers for this event (in other plug-in)
|
||||
will be skipped, and in some cases the default processing for that event would
|
||||
be skipped. This is great for replacing the default action of an event.
|
||||
|
||||
If the handler returns true, processing of other event handlers and the default
|
||||
processing will continue. This is great for extending existing functionality.
|
||||
|
||||
If the handler throws an exception, processing will stop, and the exception's
|
||||
error will be shown to the user.
|
||||
|
||||
Installation
|
||||
------------------
|
||||
|
||||
To install a plugin (like this one), site admins add the following code to their
|
||||
config.php file:
|
||||
|
||||
```php
|
||||
addPlugin('Sample');
|
||||
```
|
||||
|
||||
Plugins must be installed in one of the following directories:
|
||||
|
||||
* local/plugins/{$pluginclass}.php
|
||||
* local/plugins/{$name}/{$pluginclass}.php
|
||||
* local/{$pluginclass}.php
|
||||
* local/{$name}/{$pluginclass}.php
|
||||
* plugins/{$pluginclass}.php
|
||||
* plugins/{$name}/{$pluginclass}.php
|
||||
|
||||
Here, `{$name}` is the name of the plugin, like 'Sample', and `{$pluginclass}`
|
||||
is the name of the main class, like 'SamplePlugin'. Plugins that are part of
|
||||
the main GNU social distribution go in 'plugins' and third-party or local ones
|
||||
go in 'local'.
|
||||
|
||||
Simple plugins can be implemented as a single module. Others are more complex
|
||||
and require additional modules; these should use their own directory, like
|
||||
'local/plugins/{$name}/'. All files related to the plugin, including images,
|
||||
JavaScript, CSS, external libraries or PHP modules should go in the plugin
|
||||
directory.
|
||||
|
||||
Plugin Configuration
|
||||
------------------
|
||||
|
||||
Plugins are configured using public instance attributes. To set their values,
|
||||
site administrators use this syntax:
|
||||
|
||||
```php
|
||||
addPlugin('Sample', ('attr1' => 'foo', 'attr2' => 'bar'));
|
||||
```
|
||||
|
||||
The same plugin class can be initialized multiple times with different arguments:
|
||||
|
||||
```php
|
||||
addPlugin('EmailNotify', array('sendTo' => 'evan@status.net'));
|
||||
addPlugin('EmailNotify', array('sendTo' => 'brionv@status.net'));
|
||||
```
|
||||
|
||||
```php
|
||||
class SamplePlugin extends Plugin
|
||||
{
|
||||
public $attr1 = null;
|
||||
public $attr2 = null;
|
||||
}
|
||||
```
|
||||
|
||||
Initialization
|
||||
------------------
|
||||
|
||||
Plugins overload this method to do any initialization they need, like connecting
|
||||
to remote servers or creating paths or so on. @return boolean hook value; true
|
||||
means continue processing, false means stop.
|
||||
|
||||
```php
|
||||
function initialize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Clean Up
|
||||
------------------
|
||||
|
||||
Plugins overload this method to do any cleanup they need, like disconnecting from
|
||||
remote servers or deleting temp files or so on.
|
||||
|
||||
```php
|
||||
function cleanup()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Database schema setup
|
||||
------------------
|
||||
|
||||
Plugins can add their own tables to the GNU social database. Plugins should use
|
||||
GNU social's schema interface to add or delete tables. The ensureTable() method
|
||||
provides an easy way to ensure a table's structure and availability.
|
||||
|
||||
By default, the schema is checked every time GNU social is run (say, when a Web
|
||||
page is hit). Admins can configure their systems to only check the schema when
|
||||
the checkschema.php script is run, greatly improving performance. However, they
|
||||
need to remember to run that script after installing or upgrading a plugin!
|
||||
|
||||
```php
|
||||
function onCheckSchema()
|
||||
{
|
||||
$schema = Schema::get();
|
||||
|
||||
// '''For storing user-submitted flags on profiles'''
|
||||
|
||||
$schema->ensureTable('user_greeting_count',
|
||||
array(new ColumnDef('user_id', 'integer', null,
|
||||
true, 'PRI'),
|
||||
new ColumnDef('greeting_count', 'integer')));
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Load related modules when needed
|
||||
------------------
|
||||
|
||||
Most non-trivial plugins will require extra modules to do their work. Typically
|
||||
these include data classes, action classes, widget classes, or external libraries.
|
||||
|
||||
This method receives a class name and loads the PHP file related to that class.
|
||||
By tradition, action classes typically have files named for the action, all
|
||||
lower-case. Data classes are in files with the data class name, initial letter
|
||||
capitalized.
|
||||
|
||||
Note that this method will be called for *all* overloaded classes, not just ones
|
||||
in this plugin! So, make sure to return true by default to let other plugins,
|
||||
and the core code, get a chance.
|
||||
|
||||
```php
|
||||
function onAutoload($cls)
|
||||
{
|
||||
$dir = dirname(__FILE__);
|
||||
|
||||
switch ($cls)
|
||||
{
|
||||
case 'HelloAction':
|
||||
include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
|
||||
return false;
|
||||
case 'User_greeting_count':
|
||||
include_once $dir . '/'.$cls.'.php';
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Map URLs to actions
|
||||
------------------
|
||||
|
||||
This event handler lets the plugin map URLs on the site to actions (and thus an
|
||||
action handler class). Note that the action handler class for an action will be
|
||||
named 'FoobarAction', where action = 'foobar'. The class must be loaded in the
|
||||
onAutoload() method.
|
||||
|
||||
```php
|
||||
function onRouterInitialized($m)
|
||||
{
|
||||
$m->connect('main/hello',
|
||||
array('action' => 'hello'));
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Modify the default menu to link to our custom action
|
||||
------------------
|
||||
|
||||
Using event handlers, it's possible to modify the default UI for pages almost
|
||||
without limit. In this method, we add a menu item to the default primary menu
|
||||
for the interface to link to our action.
|
||||
|
||||
Action Class
|
||||
------------------
|
||||
|
||||
The Action class provides a rich set of events to hook, as well as output methods.
|
||||
|
||||
```php
|
||||
function onEndPrimaryNav($action)
|
||||
{
|
||||
// '''common_local_url()''' gets the correct URL for the action name we provide
|
||||
|
||||
$action->menuItem(common_local_url('hello'),
|
||||
_m('Hello'), _m('A warm greeting'), false, 'nav_hello');
|
||||
return true;
|
||||
}
|
||||
|
||||
function onPluginVersion(&$versions)
|
||||
{
|
||||
$versions[] = array('name' => 'Sample',
|
||||
'version' => STATUSNET_VERSION,
|
||||
'author' => 'Brion Vibber, Evan Prodromou',
|
||||
'homepage' => 'http://example.org/plugin',
|
||||
'rawdescription' =>
|
||||
_m('A sample plugin to show basics of development for new hackers.'));
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
hello.php
|
||||
------------------
|
||||
|
||||
This section is taken directly from the 'hello.php'. ( plugins/Sample/hello.php )
|
||||
|
||||
Give a warm greeting to our friendly user.
|
||||
|
||||
This sample action shows some basic ways of doing output in an action class.
|
||||
|
||||
Action classes have several output methods that they override from the parent class.
|
||||
|
||||
```php
|
||||
class HelloAction extends Action
|
||||
{
|
||||
var $user = null;
|
||||
var $gc = null;
|
||||
}
|
||||
```
|
||||
|
||||
Take arguments for running
|
||||
------------------
|
||||
|
||||
This method is called first, and it lets the action class get all its arguments
|
||||
and validate them. It's also the time to fetch any relevant data from the database.
|
||||
|
||||
Action classes should run parent::prepare(array $args = []) as the first line
|
||||
of this method to make sure the default argument-processing happens.
|
||||
|
||||
```php
|
||||
function prepare(array $args = [])
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
$this->user = common_current_user();
|
||||
|
||||
if (!empty($this->user)) {
|
||||
$this->gc = User_greeting_count::inc($this->user->id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
Handle request
|
||||
------------------
|
||||
|
||||
This is the main method for handling a request. Note that most preparation
|
||||
should be done in the prepare() method; by the time handle() is called the
|
||||
action should be more or less ready to go.
|
||||
|
||||
```php
|
||||
function handle()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
$this->showPage();
|
||||
}
|
||||
```
|
||||
|
||||
Title of this page
|
||||
------------------
|
||||
|
||||
Override this method to show a custom title.
|
||||
|
||||
```php
|
||||
function title()
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
return _m('Hello');
|
||||
} else {
|
||||
return sprintf(_m('Hello, %s'), $this->user->nickname);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Show content in the content area
|
||||
------------------
|
||||
|
||||
The default GNU social page has a lot of decorations: menus, logos, tabs, all
|
||||
that jazz. This method is used to show content in the content area of the
|
||||
page; it's the main thing you want to overload. This method also demonstrates
|
||||
use of a plural localized string.
|
||||
|
||||
```php
|
||||
function showContent()
|
||||
{
|
||||
if (empty($this->user)) {
|
||||
$this->element('p', array('class' => 'greeting'),
|
||||
_m('Hello, stranger!'));
|
||||
} else {
|
||||
$this->element('p', array('class' => 'greeting'),
|
||||
sprintf(_m('Hello, %s'), $this->user->nickname));
|
||||
$this->element('p', array('class' => 'greeting_count'),
|
||||
sprintf(_m('I have greeted you %d time.',
|
||||
'I have greeted you %d times.',
|
||||
$this->gc->greeting_count),
|
||||
$this->gc->greeting_count));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Return true if read only.
|
||||
------------------
|
||||
|
||||
Some actions only read from the database; others read and write. The simple
|
||||
database load-balancer built into GNU social will direct read-only actions to
|
||||
database mirrors (if they are configured) and read-write actions to the master database.
|
||||
|
||||
This defaults to false to avoid data integrity issues, but you should make sure
|
||||
to overload it for performance gains.
|
||||
|
||||
```php
|
||||
function isReadOnly($args)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
```
|
||||
|
112
DOCUMENTATION/DEVELOPERS/README.md
Normal file
112
DOCUMENTATION/DEVELOPERS/README.md
Normal file
@ -0,0 +1,112 @@
|
||||
# Contributing to GNU social
|
||||
|
||||
First of all, if you're reading this intending to contribute to GNU social,
|
||||
thanks! Free software development only happens when people like you take an
|
||||
interest in giving back to the software they themselves use, and their
|
||||
community.
|
||||
|
||||
When contributing to this repository, please first discuss the change you wish to
|
||||
make via issue, email, or any other method with the owners of this repository before
|
||||
making a change.
|
||||
|
||||
There's a few files you should read before going forward with a merge request
|
||||
or a patch submission. They detail what this file touches on in brief. They
|
||||
are:
|
||||
|
||||
* `DOCUMENTATION/DEVELOPERS/CONTRIBUTING/coding_standards.md`: How your code should be structured and formatted to be
|
||||
accepted into the GNU social codebase.
|
||||
* `/DOCUMENTATION/DEVELOPERS/CONTRIBUTING/merge_request_checklist.md`: A quick checklist to review before submission.
|
||||
|
||||
|
||||
## Merge Request Process
|
||||
|
||||
1. Run php-cs-fixer. We also recommend you to use tools like phpstan, phpactor, phpcs and phpmd
|
||||
2. Run our unit tests (`./vendor/bin/phpunit --testsuite Core`)
|
||||
3. Respect our [VERSIONING](https://notabug.org/diogo/gnu-social/src/nightly/DOCUMENTATION/VERSIONING.md) system
|
||||
4. You may merge the Merge Request in once you have the sign-off of two other developers, or if you
|
||||
do not have permission to do that, you may request the second reviewer to merge it for you
|
||||
|
||||
|
||||
## Coding Standards
|
||||
|
||||
Since we will be expected to maintain your code once it's submitted, we ask you
|
||||
to adhere to certain coding standards that make it easier for us to do so. If
|
||||
code doesn't follow them, it will be rejected, so please read up on these.
|
||||
|
||||
|
||||
## Bug Reports
|
||||
|
||||
Please report bugs to the issue tracker at
|
||||
<https://notabug.org/diogo/gnu-social/issues> Avoid assigning the labels
|
||||
yourself, as these are for the development team to assign priority and area of
|
||||
coverage to a subject. Please only submit something here if you are certain it
|
||||
is a bug or represents a feature enhancement that we do not presently have. If
|
||||
you are uncertain whether it's a bug, please feel free to ask
|
||||
at #social IRC channel on freenode.net https://www.freenode.net/.
|
||||
|
||||
When reporting a bug, please try to include as much information as possible,
|
||||
including the environment being run on (if it's a common LAMP stack just give
|
||||
us version numbers of the main stack components, that's fine), and the specific
|
||||
error you get. If you do not get a client-facing error, please check the PHP
|
||||
error_log and ensure there isn't something silently reported there, as well as
|
||||
the GNU social log. Try to include steps to reproduce the error as well, as if
|
||||
we cannot reproduce the error, we can't fix it!
|
||||
|
||||
It is perfectly acceptable to reference the archive page of a discussion on the
|
||||
mailing list for the bug report, by the way, as long as it includes all the
|
||||
information we need for a bug report.
|
||||
|
||||
|
||||
## Submitting Feature Requests / Enhancement Requests
|
||||
|
||||
Social media is constantly evolving, and we welcome ideas about how we can
|
||||
change and evolve GNU social to keep it the excellent piece of software that it
|
||||
is. However, there are a few things we ask you do when submitting feature
|
||||
requests:
|
||||
|
||||
1. Understand that since we have a limited amount of developers and these people
|
||||
contribute in their free time, we may prioritize things differently than you
|
||||
value them. Oftentimes this is because certain requests involve less changes
|
||||
to the existing codebase than others, and therefore this makes them easier
|
||||
to add.
|
||||
2. Please search the existing feature requests and enhancements to see if a
|
||||
similar request exists. If one does but you have different ideas about how
|
||||
to do it or what it should entail, please add a comment to the existing idea
|
||||
rather than create a new one for your "version" of it. Duplicate submissions
|
||||
mean we spend more time maintaining the tracker and less time actually
|
||||
working on the codebase!
|
||||
3. When outlining the way that you see something working, don't be afraid to be
|
||||
as detailed as possible! We may not implement it exactly as you describe for
|
||||
any variety of reasons, but the more concrete and fleshed out an idea is, the
|
||||
easier it is for us to know what you want and be able to implement it in a
|
||||
sane and secure fashion.
|
||||
4. When describing a possible new idea and its mechanisms of operation, the key
|
||||
words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
|
||||
"SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the issue submission
|
||||
are to be interpreted as described in RFC 2119.
|
||||
<https://tools.ietf.org/html/rfc2119>
|
||||
|
||||
Finally, and just as a call back to the first point, realize just because we
|
||||
might not rush to implement something, doesn't mean that we don't want to
|
||||
implement it! We would rather take the time to do something right the first
|
||||
time, then hurriedly apply a new idea, or a fix, only to have to patch it later.
|
||||
|
||||
|
||||
## Branch of Code Submissions
|
||||
|
||||
Unless you've been specifically directed otherwise, all submissions of code
|
||||
should be against the `nightly` branch, so make sure any modifications are based
|
||||
on Nightly.
|
||||
|
||||
|
||||
## Copyright / Licensing
|
||||
|
||||
You acknowledge that by submitting code to GNU social, you are licensing it under
|
||||
the GNU AGPLv3 unless there is an extenuating circumstance where it would be
|
||||
licensed differently (such as modifications to an external library we include
|
||||
such as Stomp).
|
||||
|
||||
You also acknowledge that unless you assign a copyright explicitly, it will be
|
||||
assumed to be assigned to GNU social.
|
||||
|
||||
Thanks for considering submission, and happy hacking!
|
@ -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
|
@ -1 +0,0 @@
|
||||
../../INSTALL.md
|
文件差異過大導致無法顯示
Load Diff
44
DOCUMENTATION/SYSTEM_ADMINISTRATORS/PLUGINS.md
Normal file
44
DOCUMENTATION/SYSTEM_ADMINISTRATORS/PLUGINS.md
Normal 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 AddMyWebsiteLink($action)
|
||||
{
|
||||
$action->menuItem('http://mywebsite.net/', _('My web site'), _('Example web link'));
|
||||
return true;
|
||||
}
|
||||
|
||||
Event::addHandler('EndPrimaryNav', 'AddMyWebsiteLink');
|
||||
|
||||
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.
|
@ -1 +0,0 @@
|
||||
../../INSTALL.md
|
@ -0,0 +1,15 @@
|
||||
#Simple way to Webfinger enable your domain -- needs PHP
|
||||
|
||||
##Step 1
|
||||
|
||||
Put the 'dot-well-known' on your website, so it loads at:
|
||||
|
||||
https://another_cool.org/.well-known/
|
||||
|
||||
(Remember the . at the beginning of this one, which is common practice
|
||||
for "hidden" files and why we have renamed it "dot-")
|
||||
|
||||
## Step 2
|
||||
|
||||
Edit the .well-known/webfinger/index.php file and replace "https://www.example.org/gnusocial/index.php" with the domain name
|
||||
you're hosting the .well-known handler on.
|
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
// FIXME: REPLACE \/ here
|
||||
define('MY_GNUSOCIAL', 'https://www.example.org/gnusocial/index.php');
|
||||
|
||||
/**
|
||||
* This is a general solution for when you can't have your GNU social instance in the domain root and for when you want to
|
||||
* socialfy from another domain.
|
||||
*/
|
||||
|
||||
// From https://www.php.net/manual/en/function.getallheaders.php#84262 (joyview at gmail dot com)
|
||||
if (!function_exists('getallheaders')) {
|
||||
function getallheaders()
|
||||
{
|
||||
$headers = [];
|
||||
foreach ($_SERVER as $name => $value) {
|
||||
if (substr($name, 0, 5) == 'HTTP_') {
|
||||
$headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, getallheaders());
|
||||
curl_setopt($ch, CURLOPT_URL, MY_GNUSOCIAL . str_replace('webfinger/', 'webfinger', $_SERVER['REQUEST_URI']));
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
$response = curl_exec($ch);
|
||||
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||
$body = substr($response, $header_size);
|
||||
// From https://stackoverflow.com/a/10590242 (c.hill)
|
||||
function get_headers_from_curl_response($response)
|
||||
{
|
||||
$headers = array();
|
||||
|
||||
$header_text = substr($response, 0, strpos($response, "\r\n\r\n"));
|
||||
|
||||
foreach (explode("\r\n", $header_text) as $i => $line) {
|
||||
if ($i === 0) {
|
||||
$headers['http_code'] = $line;
|
||||
} else {
|
||||
list($key, $value) = explode(': ', $line);
|
||||
|
||||
$headers[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
$headers = get_headers_from_curl_response($response);
|
||||
foreach ($headers as $name => $value) {
|
||||
header("{$name}: $value");
|
||||
}
|
||||
echo $body;
|
||||
curl_close($ch);
|
@ -0,0 +1,100 @@
|
||||
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).
|
||||
|
||||
MAKE SURE YOU ARE THE SAME USER THAT RUNS THE PHP FILES WHILE PERFORMING
|
||||
THE COMMANDS BELOW (I usually prepend the commands with 'sudo -u social')
|
||||
|
||||
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
|
@ -0,0 +1,60 @@
|
||||
### GNU social "fancy URL" setup
|
||||
#
|
||||
# Change the "RewriteBase" in the new .htaccess file to be the URL path
|
||||
# to your GNU Social installation on your server. Typically this will
|
||||
# be the path to your GNU Social directory relative to your Web root.
|
||||
# If you are installing it in the root directory, leave it as '/'.
|
||||
#
|
||||
# If it doesn't work, double-check that AllowOverride for the GNU Social
|
||||
# directory is 'All' in your Apache configuration file. This can be
|
||||
# * /etc/apache2/apache2.conf (generic)
|
||||
# * /etc/apache2/sites-available/default(on Debian and Ubuntu)
|
||||
# * ...many other variations depending on distribution...
|
||||
#
|
||||
# See the Apache documentation for .htaccess files for more details:
|
||||
# https://httpd.apache.org/docs/2.4/howto/htaccess.html
|
||||
#
|
||||
# Also, check that mod_rewrite is installed and enabled:
|
||||
# https://httpd.apache.org/docs/2.4/mod/mod_rewrite.html
|
||||
|
||||
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine On
|
||||
|
||||
# NOTE: change this to your actual GNU social base URL path,
|
||||
# minus the domain part:
|
||||
#
|
||||
# https://social.example.com/ => /
|
||||
# https://example.com/social/ => /social/
|
||||
#
|
||||
RewriteBase /
|
||||
#RewriteBase /mublog/
|
||||
|
||||
## Uncomment these if having trouble with API authentication
|
||||
## when PHP is running in CGI or FastCGI mode.
|
||||
#
|
||||
#RewriteCond %{HTTP:Authorization} ^(.*)
|
||||
#RewriteRule ^(.*) - [E=HTTP_AUTHORIZATION:%1]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule (.*) index.php?p=$1 [L,QSA]
|
||||
|
||||
## You can also use PATHINFO by using this RewriteRule instead:
|
||||
# RewriteRule (.*) index.php/$1 [L,QSA]
|
||||
</IfModule>
|
||||
|
||||
# Enable X-Sendfile if available. Still needs to be enabled in the config
|
||||
<IfModule mod_xsendfile.c>
|
||||
XSendFile On
|
||||
</IfModule>
|
||||
|
||||
<FilesMatch "\.(ini)">
|
||||
<IfVersion < 2.3>
|
||||
Order allow,deny
|
||||
Deny from all
|
||||
</IfVersion>
|
||||
<IfVersion >= 2.3>
|
||||
Require all denied
|
||||
</IfVersion>
|
||||
</FilesMatch>
|
@ -0,0 +1,28 @@
|
||||
# This is completely optional, but if you're running a site with
|
||||
# ssl="always" configured, then you might as well redirect any of
|
||||
# your stray HTTP visitors to HTTPS.
|
||||
#
|
||||
#$HTTP["scheme"] == "http" {
|
||||
# $HTTP["host"] =~ "^(social\.example\.com)$" {
|
||||
# url.redirect = ( "^\/?(.*)" => "https://%1/$1" )
|
||||
# server.name = "%1"
|
||||
# }
|
||||
#}
|
||||
|
||||
# If you're using vhosts, you should have per-vhost server.document-root
|
||||
# settings too! Read how in the respective alternative vhost modules.
|
||||
#$HTTP["host"] =~ "^social\.example\.com$" {
|
||||
|
||||
# NOTE: configure fastcgi/cgi/fpm here if you're using per-user cgi/fpm
|
||||
# fastcgi.server += ( ".php" =>
|
||||
# ( "localhost" => (
|
||||
# "host" => "127.0.0.1",
|
||||
# "port" => "9000"
|
||||
# ))
|
||||
# )
|
||||
|
||||
dir-listing.activate = "disable"
|
||||
|
||||
# Make sure "mod_rewrite" is enabled in server.modules
|
||||
url.rewrite-if-not-file = ( "^/(.*)$" => "/index.php/$1" )
|
||||
#}
|
@ -0,0 +1,97 @@
|
||||
server {
|
||||
listen [::]:80;
|
||||
listen 80;
|
||||
|
||||
# FIXME: Change domain name here (and also make sure you do the same in the next 'server' section)
|
||||
server_name social.example.org;
|
||||
|
||||
# redirect all traffic to HTTPS
|
||||
rewrite ^ https://$host$request_uri? permanent;
|
||||
}
|
||||
|
||||
server {
|
||||
# HTTPS is mandatory on GNU social unless you are using Tor network. Seriously.
|
||||
# Set it up with a cert (any cert) before you run the install.
|
||||
listen [::]:443 ssl http2;
|
||||
listen 443 ssl http2;
|
||||
|
||||
# Root
|
||||
# FIXME: Change the path below to where you installed GNU social (GNU social's root + /public)
|
||||
root /var/www/gnusocial/public;
|
||||
|
||||
# Server name
|
||||
# FIXME: Change "social.example.org" to your site's domain name
|
||||
server_name social.example.org;
|
||||
|
||||
# SSL
|
||||
# FIXME: Change the paths to setup your SSL key/cert. See https://cipherli.st/ for more information
|
||||
ssl_certificate ssl/certs/social.example.org.crt;
|
||||
ssl_certificate_key ssl/private/social.example.org.key;
|
||||
|
||||
# Index
|
||||
index index.php;
|
||||
|
||||
# X-Accel/X-Sendfile. Still needs to be enabled in the config
|
||||
location /file {
|
||||
internal;
|
||||
# FIXME: Change "/path/to/gnusocial/root/" to the folder where
|
||||
# attachments are stored (normally the same as the site root)
|
||||
root /path/to/gnusocial/root/;
|
||||
}
|
||||
|
||||
# PHP
|
||||
location ~ ^/(index|install)\.php(/.*)?$ {
|
||||
#location ^~ /index.php {
|
||||
include fastcgi_params;
|
||||
include snippets/fastcgi-php.conf;
|
||||
|
||||
fastcgi_pass unix:/run/php/php-fpm.sock;
|
||||
fastcgi_param SCRIPT_FILENAME $request_filename;
|
||||
}
|
||||
|
||||
# Don't allow any PHP file other than index.php to be executed
|
||||
# This will ensure that nor config.php nor plugin files with eventual hardcoded security information are downloadable
|
||||
# And this is better than allowing php files to be executed in case of forgotten `if (!defined('GNUSOCIAL')) { exit(1); }`
|
||||
location ~ \.php$ {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Location
|
||||
location / {
|
||||
try_files $uri $uri/ @index_handler;
|
||||
}
|
||||
|
||||
# Fancy URLs
|
||||
error_page 404 @index_handler;
|
||||
location @index_handler {
|
||||
rewrite ^(.*)$ /index.php?p=$1 last;
|
||||
}
|
||||
|
||||
# Restrict access that is unnecessary anyway
|
||||
location ~ /\.(ht|git) {
|
||||
deny all;
|
||||
}
|
||||
|
||||
#
|
||||
# Hardening (optional)
|
||||
#
|
||||
# add_header Strict-Transport-Security "max-age=15768000; preload;";
|
||||
# add_header X-Content-Type-Options nosniff;
|
||||
# add_header Referrer-Policy strict-origin-when-cross-origin;
|
||||
# add_header Content-Security-Policy "default-src 'self' 'unsafe-inline'; frame-ancestors 'self'; form-action 'self'; style-src 'self' 'unsafe-inline'; img-src * blob: data:;";
|
||||
# add_header X-Permitted-Cross-Domain-Policies none;
|
||||
# add_header X-Robots-Tag all; # Not really hardening, just here for strictness purposes
|
||||
#
|
||||
# client_max_body_size 15M;
|
||||
# client_body_buffer_size 128k;
|
||||
# gzip_vary on;
|
||||
#
|
||||
# location ~* \.(?:css|js|woff|svg|gif|png|webp|ttf|ico|jpe?g)$ {
|
||||
# gzip on;
|
||||
# gzip_comp_level 4;
|
||||
# add_header Cache-Control "public";
|
||||
# expires 30d;
|
||||
# access_log off;
|
||||
# log_not_found off;
|
||||
# }
|
||||
}
|
17
DOCUMENTATION/VERSIONING.md
Normal file
17
DOCUMENTATION/VERSIONING.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Versioning
|
||||
|
||||
## Release cycle
|
||||
We follow Semantic Versioning as defined on [https://semver.org/](https://semver.org).
|
||||
|
||||
The GNU social platform has a monthly release cycle. That is, we only increment the version number on day 1 of each month.
|
||||
|
||||
For GNU social plugins, we increment version number by commit. Plugins update have a propagation of the same type in the
|
||||
GNU social's monthly version bump.
|
||||
|
||||
If we have various patch type updates in a month, we only bump it by one in the end of the month. If in the same month
|
||||
we have a minor too, we will only bump the minor value.
|
||||
|
||||
# Lifecycle
|
||||
|
||||
GNU social has 'dev', 'alpha', 'beta', 'rc' and 'release' cycles. We usually are in `dev` during summer. When we finish a `dev` cycle we release an `alpha` (`dev`s aren't tagged as releases). From `alpha` to `release` it depends on how perfect GNU social is after the end of the `dev` cycle. But we use the number after `alpha` instead of `patch` number for small bug fixes when in this cycle. When we finally go from a `r`elease `c`andidate to a `release`, we party... and we use semver for all the small changes until we start working in another `dev` cycle (usually on another summer).
|
||||
|
466
INSTALL.md
466
INSTALL.md
@ -1,5 +1,463 @@
|
||||
GNU social
|
||||
=====
|
||||
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
|
||||
* Additional configuration
|
||||
|
||||
GNU social is a federated social network. For documentation, visit
|
||||
https://docs.gnusocial.rocks/ or view the files under docs/
|
||||
Prerequisites
|
||||
=============
|
||||
|
||||
PHP modules
|
||||
-----------
|
||||
|
||||
The following software packages are *required* for this software to
|
||||
run correctly.
|
||||
|
||||
- PHP 7+ PHP7.x is also supported.
|
||||
- MariaDB 5+ MariaDB 10.x is also supported.
|
||||
- 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)
|
||||
- php-curl Fetching files by HTTP.
|
||||
- php-exif Exchangeable image information.
|
||||
- php-gd Image manipulation (scaling).
|
||||
- php-intl Internationalization support (transliteration et al).
|
||||
- php-json For WebFinger lookups and more.
|
||||
- php-mbstring String manipulation
|
||||
- php-mysql The native driver for MariaDB connections.
|
||||
- php-gmp For Salmon signatures (part of OStatus)
|
||||
- php-bcmath Arbitrary Precision Mathematics
|
||||
- php-opcache Improved PHP performance by precompilation
|
||||
- php-readline For interactive scripts
|
||||
- php-xml XML parser
|
||||
- php-ds Faster data structures
|
||||
|
||||
NOTE: Some distros require manual enabling in the relevant php.ini for some modules.
|
||||
|
||||
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.
|
||||
- php-ds For faster data structures; will be emulated if not present.
|
||||
|
||||
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. 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.)
|
||||
|
||||
5. 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.
|
||||
|
||||
6. 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.
|
||||
|
||||
7. 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.
|
||||
|
||||
Additional configuration
|
||||
------------------------
|
||||
|
||||
Please refer to DOCUMENTATION/SYSTEM_ADMINISTRATORS/CONFIGURE for information.
|
||||
|
127
Makefile
127
Makefile
@ -1,122 +1,21 @@
|
||||
# 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
|
||||
upgrade :
|
||||
php scripts/upgrade.php
|
||||
|
||||
up: .PHONY
|
||||
docker-compose up -d
|
||||
clean :
|
||||
rm -f $(core_mo) $(plugin_mo)
|
||||
|
||||
down: .PHONY
|
||||
docker-compose down
|
||||
updatepo :
|
||||
php scripts/update_po_templates.php --all
|
||||
|
||||
redis-shell:
|
||||
docker exec -it $(call translate-container-name,$(strip $(DIR))_redis_1) sh -c 'redis-cli'
|
||||
%.mo : %.po
|
||||
msgfmt -o $@ $<
|
||||
|
||||
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: .PHONY
|
||||
@cd docker/tooling && docker-compose up -d --build > /dev/null 2>&1
|
||||
|
||||
stop-tooling: .PHONY
|
||||
cd docker/tooling && docker-compose down
|
||||
|
||||
tooling-php-shell: tooling-docker
|
||||
docker exec -it $(call translate-container-name,tooling_php_1) sh
|
||||
|
||||
test-accesibility: tooling-docker
|
||||
cd docker/tooling && docker-compose run pa11y /accessibility.sh
|
||||
|
||||
test: tooling-docker
|
||||
docker exec $(call translate-container-name,tooling_php_1) /var/tooling/coverage.sh $(call args,'')
|
||||
|
||||
cs-fixer: tooling-docker
|
||||
@bin/php-cs-fixer $${CS_FIXER_FILE}
|
||||
|
||||
doc-check: tooling-docker
|
||||
bin/php-doc-check
|
||||
|
||||
phpstan: tooling-docker
|
||||
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
|
||||
|
157
README.md
Normal file
157
README.md
Normal file
@ -0,0 +1,157 @@
|
||||
# GNU social 2.0.x
|
||||
(c) 2010-2019 Free Software Foundation, 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.
|
||||
|
||||
The file INSTALL.md has useful instructions on how to
|
||||
install this software.
|
||||
|
||||
System administrators may find the `DOCUMENTATION/SYSTEM_ADMINISTRATORS`
|
||||
directory useful, namely:
|
||||
|
||||
- upgrade_from: upgrading from different software
|
||||
- CONFIGURE.md: configuration options in gruesome detail.
|
||||
- PLUGINS.md: how to install and configure plugins.
|
||||
|
||||
Developers may find the `DOCUMENTATION/DEVELOPERS` directory useful.
|
||||
|
||||
## 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 proprietary social network sites,
|
||||
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 open standards (such as 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.
|
||||
|
||||
Refer to COPYING.md for full text of the software license..
|
||||
|
||||
### 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@notabug.org:diogo/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.20.x "oldstable", few updates, well tested coded
|
||||
* master "stable", usually working well
|
||||
* nightly "testing", most updates, not always working as expected
|
||||
|
||||
To keep it up-to-date, use `git pull`. Watch for conflicts!
|
||||
|
||||
As in any upgrade, do __not__ forget to run `/scripts/upgrade.php`.
|
||||
|
||||
## 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/>
|
||||
|
||||
* GNU social has a bug tracker for any defects you may find, or ideas for
|
||||
making things better. <https://notabug.org/diogo/gnu-social/issues>
|
||||
* Patches are welcome, preferrably to our repository on notabug.org. <https://notabug.org/diogo/gnu-social>
|
||||
|
||||
## Credits
|
||||
|
||||
An incomplete list of developers who've worked on GNU social,
|
||||
or its predecessors StatusNet and Free Social has been made available
|
||||
in `CREDITS.md`.
|
||||
|
||||
### Current team
|
||||
|
||||
* Matt Lee
|
||||
* Mikael Nordfeldth
|
||||
* Diogo Cordeiro
|
||||
* Bruno Casteleiro
|
||||
* Miguel Dantas
|
||||
* Alexei Sorokin
|
214
actions/accessadminpanel.php
Normal file
214
actions/accessadminpanel.php
Normal 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
161
actions/addpeopletag.php
Normal 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(array $args = array())
|
||||
{
|
||||
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()
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
182
actions/all.php
Normal file
182
actions/all.php
Normal file
@ -0,0 +1,182 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ThreadingInboxNoticeStream extends ThreadingNoticeStream
|
||||
{
|
||||
function __construct(Profile $target, Profile $scoped=null)
|
||||
{
|
||||
parent::__construct(new InboxNoticeStream($target, $scoped));
|
||||
}
|
||||
}
|
73
actions/allrss.php
Normal file
73
actions/allrss.php
Normal 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, $this->scoped);
|
||||
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;
|
||||
}
|
||||
}
|
121
actions/apiaccountratelimitstatus.php
Normal file
121
actions/apiaccountratelimitstatus.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?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
|
||||
{
|
||||
/**
|
||||
* Return true if read only.
|
||||
*
|
||||
* MAY override
|
||||
*
|
||||
* @param array $args other arguments
|
||||
*
|
||||
* @return boolean is read only action?
|
||||
*/
|
||||
public function isReadOnly($args)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the request
|
||||
*
|
||||
* Return some Twitter-ish data about API limits
|
||||
*
|
||||
* @return void
|
||||
* @throws ClientException
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
if (!in_array($this->format, ['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', ['type' => 'integer'], "150");
|
||||
$this->element('hourly-limit', ['type' => 'integer'], "150");
|
||||
$this->element(
|
||||
'reset-time',
|
||||
['type' => 'datetime'],
|
||||
common_date_iso8601($reset->format('r'))
|
||||
);
|
||||
$this->element(
|
||||
'reset_time_in_seconds',
|
||||
['type' => 'integer'],
|
||||
strtotime('+1 hour')
|
||||
);
|
||||
$this->elementEnd('hash');
|
||||
} elseif ($this->format == 'json') {
|
||||
$out = [
|
||||
'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);
|
||||
}
|
||||
}
|
194
actions/apiaccountregister.php
Normal file
194
actions/apiaccountregister.php
Normal 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);
|
||||
}
|
||||
|
||||
}
|
90
actions/apiaccountupdatebackgroundcolor.php
Normal file
90
actions/apiaccountupdatebackgroundcolor.php
Normal 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');
|
||||
}
|
||||
|
||||
|
||||
}
|
147
actions/apiaccountupdatedeliverydevice.php
Normal file
147
actions/apiaccountupdatedeliverydevice.php
Normal 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(array $args = array())
|
||||
{
|
||||
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()
|
||||
{
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
// 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');
|
||||
}
|
||||
}
|
||||
}
|
89
actions/apiaccountupdatelinkcolor.php
Normal file
89
actions/apiaccountupdatelinkcolor.php
Normal 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');
|
||||
}
|
||||
}
|
141
actions/apiaccountupdateprofile.php
Normal file
141
actions/apiaccountupdateprofile.php
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
112
actions/apiaccountupdateprofileimage.php
Normal file
112
actions/apiaccountupdateprofileimage.php
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
85
actions/apiaccountverifycredentials.php
Normal file
85
actions/apiaccountverifycredentials.php
Normal 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
136
actions/apiatomservice.php
Normal 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(array $args = array())
|
||||
{
|
||||
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()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
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
106
actions/apiattachment.php
Normal 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
113
actions/apiblockcreate.php
Normal 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
102
actions/apiblockdestroy.php
Normal 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
115
actions/apicheckhub.php
Normal 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;
|
||||
}
|
||||
}
|
69
actions/apichecknickname.php
Normal file
69
actions/apichecknickname.php
Normal 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
227
actions/apiconversation.php
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
99
actions/apiexternalprofileshow.php
Normal file
99
actions/apiexternalprofileshow.php
Normal 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;
|
||||
}
|
||||
}
|
111
actions/apifriendshipscreate.php
Normal file
111
actions/apifriendshipscreate.php
Normal 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);
|
||||
}
|
||||
}
|
114
actions/apifriendshipsdestroy.php
Normal file
114
actions/apifriendshipsdestroy.php
Normal 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);
|
||||
}
|
||||
}
|
118
actions/apifriendshipsexists.php
Normal file
118
actions/apifriendshipsexists.php
Normal 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;
|
||||
}
|
||||
}
|
169
actions/apifriendshipsshow.php
Normal file
169
actions/apifriendshipsshow.php
Normal 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;
|
||||
}
|
||||
}
|
148
actions/apignusocialconfig.php
Normal file
148
actions/apignusocialconfig.php
Normal 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;
|
||||
}
|
||||
}
|
82
actions/apignusocialversion.php
Normal file
82
actions/apignusocialversion.php
Normal 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
190
actions/apigroupadmins.php
Normal 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
181
actions/apigroupcreate.php
Normal 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;
|
||||
}
|
||||
}
|
125
actions/apigroupismember.php
Normal file
125
actions/apigroupismember.php
Normal 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
124
actions/apigroupjoin.php
Normal 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
123
actions/apigroupleave.php
Normal 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
211
actions/apigrouplist.php
Normal 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
208
actions/apigrouplistall.php
Normal 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(array $args = array())
|
||||
{
|
||||
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()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
181
actions/apigroupmembership.php
Normal file
181
actions/apigroupmembership.php
Normal 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;
|
||||
}
|
||||
}
|
247
actions/apigroupprofileupdate.php
Normal file
247
actions/apigroupprofileupdate.php
Normal 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
163
actions/apigroupshow.php
Normal 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;
|
||||
}
|
||||
}
|
76
actions/apihelptest.php
Normal file
76
actions/apihelptest.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?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('GNUSOCIAL')) { 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
|
||||
{
|
||||
protected function handle()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
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 {
|
||||
// TRANS: Client error displayed when coming across a non-supported API method.
|
||||
throw new ClientException(_('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;
|
||||
}
|
||||
}
|
240
actions/apilist.php
Normal file
240
actions/apilist.php
Normal 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
112
actions/apilistmember.php
Normal 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
131
actions/apilistmembers.php
Normal 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);
|
||||
}
|
||||
}
|
124
actions/apilistmemberships.php
Normal file
124
actions/apilistmemberships.php
Normal 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
232
actions/apilists.php
Normal 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;
|
||||
}
|
||||
}
|
87
actions/apilistsubscriber.php
Normal file
87
actions/apilistsubscriber.php
Normal 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(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 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()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
102
actions/apilistsubscribers.php
Normal file
102
actions/apilistsubscribers.php
Normal 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);
|
||||
}
|
||||
}
|
110
actions/apilistsubscriptions.php
Normal file
110
actions/apilistsubscriptions.php
Normal 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
195
actions/apimediaupload.php
Normal 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;
|
||||
}
|
||||
}
|
122
actions/apioauthaccesstoken.php
Normal file
122
actions/apioauthaccesstoken.php
Normal 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()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
707
actions/apioauthauthorize.php
Normal file
707
actions/apioauthauthorize.php
Normal 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(array $args = array())
|
||||
{
|
||||
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()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
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
172
actions/apioauthpin.php
Normal 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);
|
||||
}
|
||||
}
|
152
actions/apioauthrequesttoken.php
Normal file
152
actions/apioauthrequesttoken.php
Normal 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(array $args = array())
|
||||
{
|
||||
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()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
$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
392
actions/apisearchatom.php
Normal 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(array $args = array())
|
||||
{
|
||||
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()
|
||||
{
|
||||
parent::handle();
|
||||
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;
|
||||
$source_link = null;
|
||||
|
||||
$ns = $notice->getSource();
|
||||
if ($ns instanceof Notice_source) {
|
||||
$source = $ns->code;
|
||||
if (!empty($ns->url)) {
|
||||
$source_link = $ns->url;
|
||||
if (!empty($ns->name)) {
|
||||
$source = $ns->name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->element("twitter:source", null, $source);
|
||||
$this->element("twitter:source_link", null, $source_link);
|
||||
|
||||
$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
136
actions/apisearchjson.php
Normal 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(array $args = array())
|
||||
{
|
||||
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()
|
||||
{
|
||||
parent::handle();
|
||||
$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;
|
||||
}
|
||||
}
|
113
actions/apistatusesdestroy.php
Normal file
113
actions/apistatusesdestroy.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?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('GNUSOCIAL')) { 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
|
||||
{
|
||||
protected function prepare(array $args=array())
|
||||
{
|
||||
parent::prepare($args);
|
||||
|
||||
if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
|
||||
// TRANS: Client error displayed trying to delete a status not using POST or DELETE.
|
||||
// TRANS: POST and DELETE should not be translated.
|
||||
throw new ClientException(_('This method requires a POST or DELETE.'));
|
||||
}
|
||||
|
||||
// FIXME: Return with a Not Acceptable status code?
|
||||
if (!in_array($this->format, array('xml', 'json'))) {
|
||||
// TRANS: Client error displayed when coming across a non-supported API method.
|
||||
throw new ClientException(_('API method not found.'), 404);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->notice = Notice::getByID($this->trimmed('id'));
|
||||
} catch (NoResultException $e) {
|
||||
// TRANS: Client error displayed trying to delete a status with an invalid ID.
|
||||
throw new ClientException(_('No status found with that ID.'), 404);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function handle()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
if (!$this->scoped->sameAs($this->notice->getProfile()) && !$this->scoped->hasRight(Right::DELETEOTHERSNOTICE)) {
|
||||
// TRANS: Client error displayed trying to delete a status of another user.
|
||||
throw new AuthorizationException(_('You may not delete another user\'s status.'));
|
||||
}
|
||||
|
||||
if (Event::handle('StartDeleteOwnNotice', array($this->scoped->getUser(), $this->notice))) {
|
||||
$this->notice->deleteAs($this->scoped);
|
||||
Event::handle('EndDeleteOwnNotice', array($this->scoped->getUser(), $this->notice));
|
||||
}
|
||||
$this->showNotice();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
224
actions/apistatusesshow.php
Normal 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");
|
||||
}
|
||||
}
|
368
actions/apistatusesupdate.php
Normal file
368
actions/apistatusesupdate.php
Normal file
@ -0,0 +1,368 @@
|
||||
<?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, atom
|
||||
|
||||
@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 (EmptyPkeyValueException $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);
|
||||
} elseif ($this->format == 'atom') {
|
||||
$this->showSingleAtomStatus($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;
|
||||
}
|
||||
}
|
254
actions/apisubscriptions.php
Normal file
254
actions/apisubscriptions.php
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
344
actions/apitimelinefriends.php
Normal file
344
actions/apitimelinefriends.php
Normal 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><a href="http://somesourcecode.net/microblog/">mbpidgin</a></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;
|
||||
}
|
||||
}
|
221
actions/apitimelinegroup.php
Normal file
221
actions/apitimelinegroup.php
Normal 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
249
actions/apitimelinehome.php
Normal 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
248
actions/apitimelinelist.php
Normal 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;
|
||||
}
|
||||
}
|
254
actions/apitimelinementions.php
Normal file
254
actions/apitimelinementions.php
Normal 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;
|
||||
}
|
||||
}
|
19
actions/apitimelinenetworkpublic.php
Normal file
19
actions/apitimelinenetworkpublic.php
Normal 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);
|
||||
}
|
||||
}
|
335
actions/apitimelinepublic.php
Normal file
335
actions/apitimelinepublic.php
Normal 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
228
actions/apitimelinetag.php
Normal 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;
|
||||
}
|
||||
}
|
409
actions/apitimelineuser.php
Normal file
409
actions/apitimelineuser.php
Normal file
@ -0,0 +1,409 @@
|
||||
<?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
|
||||
{
|
||||
public $notices = null;
|
||||
|
||||
public $next_id = null;
|
||||
|
||||
/**
|
||||
* We expose AtomPub here, so non-GET/HEAD reqs must be read/write.
|
||||
*
|
||||
* @param array $args other arguments
|
||||
*
|
||||
* @return boolean true
|
||||
*/
|
||||
|
||||
public 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
|
||||
*/
|
||||
public 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
|
||||
*/
|
||||
public 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take arguments for running
|
||||
*
|
||||
* @param array $args $_REQUEST args
|
||||
*
|
||||
* @return boolean success flag
|
||||
* @throws AuthorizationException
|
||||
* @throws ClientException
|
||||
*/
|
||||
protected function prepare(array $args = [])
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get notices
|
||||
*
|
||||
* @return array notices
|
||||
*/
|
||||
public function getNotices()
|
||||
{
|
||||
$notices = [];
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the request
|
||||
*
|
||||
* Just show the notices
|
||||
*
|
||||
* @return void
|
||||
* @throws ClientException
|
||||
* @throws ServerException
|
||||
*/
|
||||
protected function handle()
|
||||
{
|
||||
parent::handle();
|
||||
|
||||
if ($this->isPost()) {
|
||||
$this->handlePost();
|
||||
} else {
|
||||
$this->showTimeline();
|
||||
}
|
||||
}
|
||||
|
||||
public 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the timeline of notices
|
||||
*
|
||||
* @return void
|
||||
* @throws ClientException
|
||||
* @throws ServerException
|
||||
* @throws UserNoProfileException
|
||||
*/
|
||||
public 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 = [];
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
85
actions/apitrends.php
Normal file
85
actions/apitrends.php
Normal 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(array $args = array())
|
||||
{
|
||||
parent::prepare($args);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a request
|
||||
*
|
||||
* @param array $args Arguments from $_REQUEST
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function handle()
|
||||
{
|
||||
parent::handle();
|
||||
$this->showTrends();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the trends
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function showTrends()
|
||||
{
|
||||
// TRANS: Server error for unfinished API method showTrends.
|
||||
$this->serverError(_('API method under construction.'), 501);
|
||||
}
|
||||
}
|
80
actions/apiuserfollowers.php
Normal file
80
actions/apiuserfollowers.php
Normal 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;
|
||||
}
|
||||
}
|
80
actions/apiuserfriends.php
Normal file
80
actions/apiuserfriends.php
Normal 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;
|
||||
}
|
||||
}
|
117
actions/apiuserprofileimage.php
Normal file
117
actions/apiuserprofileimage.php
Normal 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;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user