code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-05 16:12+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Tagalog (http://www.transifex.com/gnu-social/gnu-social/language/tl/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: tl\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Button text for adding the favourite status to a notice.
#: forms/favor.php:122
msgctxt "BUTTON"
msgid "Favor"
msgstr ""

#. TRANS: Button text for removing the favourite status for a favourite
#. notice.
#: forms/disfavor.php:123
msgctxt "BUTTON"
msgid "Disfavor favorite"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav <nickname>".
#: FavoriteModule.php:415
msgctxt "COMMANDHELP"
msgid "add user's last notice as a 'fave'"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav #<notice_id>".
#: FavoriteModule.php:417
msgctxt "COMMANDHELP"
msgid "add notice with the given id as a 'fave'"
msgstr ""

#. TRANS: Menu item in personal group navigation menu.
#: FavoriteModule.php:469
msgctxt "MENU"
msgid "Favorites"
msgstr ""

#. TRANS: Menu item in search group navigation panel.
#: FavoriteModule.php:482
msgctxt "MENU"
msgid "Popular"
msgstr "Tanyag"

#. TRANS: Module description.
#: FavoriteModule.php:508
msgid "Favorites (likes) using ActivityStreams."
msgstr ""

#: actions/favor.php:57
msgid "No such notice."
msgstr "Walang ganyang pabatid."

#. TRANS: Client error displayed when trying to reply to a notice a the target
#. has no access to.
#. TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
#: actions/favor.php:62
#, php-format
msgid "%1$s has no right to reply to notice %2$d."
msgstr ""

#: classes/Fave.php:86
#, php-format
msgid "Could not store new object of type %s"
msgstr ""

#. TRANS: List message for notice favoured by logged in user.
#: lib/threadednoticelistfavesitem.php:51
msgctxt "FAVELIST"
msgid "You like this."
msgstr ""

#. TRANS: List message for when more than 4 people like something.
#. TRANS: %%s is a list of users liking a notice, %d is the number over 4 that
#. like the notice.
#. TRANS: Plural is decided on the total number of users liking the notice
#. (count of %%s + %d).
#: lib/threadednoticelistfavesitem.php:56
#, php-format
msgid "%%s and %d others like this."
msgid_plural "%%s and %d others like this."
msgstr[0] ""
msgstr[1] ""

#. TRANS: List message for favoured notices.
#. TRANS: %%s is a list of users liking a notice.
#. TRANS: Plural is based on the number of of users that have favoured a
#. notice.
#: lib/threadednoticelistfavesitem.php:64
#, php-format
msgid "%%s likes this."
msgid_plural "%%s like this."
msgstr[0] ""
msgstr[1] ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-05 16:12+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Turkish (http://www.transifex.com/gnu-social/gnu-social/language/tr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: tr\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"

#. TRANS: Button text for adding the favourite status to a notice.
#: forms/favor.php:122
msgctxt "BUTTON"
msgid "Favor"
msgstr ""

#. TRANS: Button text for removing the favourite status for a favourite
#. notice.
#: forms/disfavor.php:123
msgctxt "BUTTON"
msgid "Disfavor favorite"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav <nickname>".
#: FavoriteModule.php:415
msgctxt "COMMANDHELP"
msgid "add user's last notice as a 'fave'"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav #<notice_id>".
#: FavoriteModule.php:417
msgctxt "COMMANDHELP"
msgid "add notice with the given id as a 'fave'"
msgstr ""

#. TRANS: Menu item in personal group navigation menu.
#: FavoriteModule.php:469
msgctxt "MENU"
msgid "Favorites"
msgstr ""

#. TRANS: Menu item in search group navigation panel.
#: FavoriteModule.php:482
msgctxt "MENU"
msgid "Popular"
msgstr "Popüler"

#. TRANS: Module description.
#: FavoriteModule.php:508
msgid "Favorites (likes) using ActivityStreams."
msgstr ""

#: actions/favor.php:57
msgid "No such notice."
msgstr "Böyle bir durum mesajı yok."

#. TRANS: Client error displayed when trying to reply to a notice a the target
#. has no access to.
#. TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
#: actions/favor.php:62
#, php-format
msgid "%1$s has no right to reply to notice %2$d."
msgstr ""

#: classes/Fave.php:86
#, php-format
msgid "Could not store new object of type %s"
msgstr ""

#. TRANS: List message for notice favoured by logged in user.
#: lib/threadednoticelistfavesitem.php:51
msgctxt "FAVELIST"
msgid "You like this."
msgstr ""

#. TRANS: List message for when more than 4 people like something.
#. TRANS: %%s is a list of users liking a notice, %d is the number over 4 that
#. like the notice.
#. TRANS: Plural is decided on the total number of users liking the notice
#. (count of %%s + %d).
#: lib/threadednoticelistfavesitem.php:56
#, php-format
msgid "%%s and %d others like this."
msgid_plural "%%s and %d others like this."
msgstr[0] ""
msgstr[1] ""

#. TRANS: List message for favoured notices.
#. TRANS: %%s is a list of users liking a notice.
#. TRANS: Plural is based on the number of of users that have favoured a
#. notice.
#: lib/threadednoticelistfavesitem.php:64
#, php-format
msgid "%%s likes this."
msgid_plural "%%s like this."
msgstr[0] ""
msgstr[1] ""

repo_name: spookie/gnu

license: mit


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
# Петро Романчук <ixer@ixer.org.ua>, 2015
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-03-31 12:11+0000\n"
"Last-Translator: Петро Романчук <ixer@ixer.org.ua>\n"
"Language-Team: Ukrainian (http://www.transifex.com/gnu-social/gnu-social/language/uk/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: uk\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"

#. TRANS: Button text for adding the favourite status to a notice.
#: forms/favor.php:122
msgctxt "BUTTON"
msgid "Favor"
msgstr ""

#. TRANS: Button text for removing the favourite status for a favourite
#. notice.
#: forms/disfavor.php:123
msgctxt "BUTTON"
msgid "Disfavor favorite"
msgstr "Видалити з обраного"

#. TRANS: Help message for IM/SMS command "fav <nickname>".
#: FavoriteModule.php:415
msgctxt "COMMANDHELP"
msgid "add user's last notice as a 'fave'"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav #<notice_id>".
#: FavoriteModule.php:417
msgctxt "COMMANDHELP"
msgid "add notice with the given id as a 'fave'"
msgstr ""

#. TRANS: Menu item in personal group navigation menu.
#: FavoriteModule.php:469
msgctxt "MENU"
msgid "Favorites"
msgstr "Обрані"

#. TRANS: Menu item in search group navigation panel.
#: FavoriteModule.php:482
msgctxt "MENU"
msgid "Popular"
msgstr "Популярні"

#. TRANS: Module description.
#: FavoriteModule.php:508
msgid "Favorites (likes) using ActivityStreams."
msgstr "Вибране (уподобання) з використанням ActivityStreams."

#: actions/favor.php:57
msgid "No such notice."
msgstr "Такого допису немає."

#. TRANS: Client error displayed when trying to reply to a notice a the target
#. has no access to.
#. TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
#: actions/favor.php:62
#, php-format
msgid "%1$s has no right to reply to notice %2$d."
msgstr "%1$s немає достатньо прав щоб відповісти на повідомлення %2$d."

#: classes/Fave.php:86
#, php-format
msgid "Could not store new object of type %s"
msgstr "Неможливо зберегти новий об'єкт типу %s"

#. TRANS: List message for notice favoured by logged in user.
#: lib/threadednoticelistfavesitem.php:51
msgctxt "FAVELIST"
msgid "You like this."
msgstr ""

#. TRANS: List message for when more than 4 people like something.
#. TRANS: %%s is a list of users liking a notice, %d is the number over 4 that
#. like the notice.
#. TRANS: Plural is decided on the total number of users liking the notice
#. (count of %%s + %d).
#: lib/threadednoticelistfavesitem.php:56
#, php-format
msgid "%%s and %d others like this."
msgid_plural "%%s and %d others like this."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""

#. TRANS: List message for favoured notices.
#. TRANS: %%s is a list of users liking a notice.
#. TRANS: Plural is based on the number of of users that have favoured a
#. notice.
#: lib/threadednoticelistfavesitem.php:64
#, php-format
msgid "%%s likes this."
msgid_plural "%%s like this."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-05 16:12+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Urdu (Pakistan) (http://www.transifex.com/gnu-social/gnu-social/language/ur_PK/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: ur_PK\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

#. TRANS: Button text for adding the favourite status to a notice.
#: forms/favor.php:122
msgctxt "BUTTON"
msgid "Favor"
msgstr ""

#. TRANS: Button text for removing the favourite status for a favourite
#. notice.
#: forms/disfavor.php:123
msgctxt "BUTTON"
msgid "Disfavor favorite"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav <nickname>".
#: FavoriteModule.php:415
msgctxt "COMMANDHELP"
msgid "add user's last notice as a 'fave'"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav #<notice_id>".
#: FavoriteModule.php:417
msgctxt "COMMANDHELP"
msgid "add notice with the given id as a 'fave'"
msgstr ""

#. TRANS: Menu item in personal group navigation menu.
#: FavoriteModule.php:469
msgctxt "MENU"
msgid "Favorites"
msgstr ""

#. TRANS: Menu item in search group navigation panel.
#: FavoriteModule.php:482
msgctxt "MENU"
msgid "Popular"
msgstr ""

#. TRANS: Module description.
#: FavoriteModule.php:508
msgid "Favorites (likes) using ActivityStreams."
msgstr ""

#: actions/favor.php:57
msgid "No such notice."
msgstr ""

#. TRANS: Client error displayed when trying to reply to a notice a the target
#. has no access to.
#. TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
#: actions/favor.php:62
#, php-format
msgid "%1$s has no right to reply to notice %2$d."
msgstr ""

#: classes/Fave.php:86
#, php-format
msgid "Could not store new object of type %s"
msgstr ""

#. TRANS: List message for notice favoured by logged in user.
#: lib/threadednoticelistfavesitem.php:51
msgctxt "FAVELIST"
msgid "You like this."
msgstr ""

#. TRANS: List message for when more than 4 people like something.
#. TRANS: %%s is a list of users liking a notice, %d is the number over 4 that
#. like the notice.
#. TRANS: Plural is decided on the total number of users liking the notice
#. (count of %%s + %d).
#: lib/threadednoticelistfavesitem.php:56
#, php-format
msgid "%%s and %d others like this."
msgid_plural "%%s and %d others like this."
msgstr[0] ""
msgstr[1] ""

#. TRANS: List message for favoured notices.
#. TRANS: %%s is a list of users liking a notice.
#. TRANS: Plural is based on the number of of users that have favoured a
#. notice.
#: lib/threadednoticelistfavesitem.php:64
#, php-format
msgid "%%s likes this."
msgid_plural "%%s like this."
msgstr[0] ""
msgstr[1] ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-05 16:12+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Vietnamese (http://www.transifex.com/gnu-social/gnu-social/language/vi/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: vi\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Button text for adding the favourite status to a notice.
#: forms/favor.php:122
msgctxt "BUTTON"
msgid "Favor"
msgstr ""

#. TRANS: Button text for removing the favourite status for a favourite
#. notice.
#: forms/disfavor.php:123
msgctxt "BUTTON"
msgid "Disfavor favorite"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav <nickname>".
#: FavoriteModule.php:415
msgctxt "COMMANDHELP"
msgid "add user's last notice as a 'fave'"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav #<notice_id>".
#: FavoriteModule.php:417
msgctxt "COMMANDHELP"
msgid "add notice with the given id as a 'fave'"
msgstr ""

#. TRANS: Menu item in personal group navigation menu.
#: FavoriteModule.php:469
msgctxt "MENU"
msgid "Favorites"
msgstr ""

#. TRANS: Menu item in search group navigation panel.
#: FavoriteModule.php:482
msgctxt "MENU"
msgid "Popular"
msgstr ""

#. TRANS: Module description.
#: FavoriteModule.php:508
msgid "Favorites (likes) using ActivityStreams."
msgstr ""

#: actions/favor.php:57
msgid "No such notice."
msgstr "Không có tin nhắn nào."

#. TRANS: Client error displayed when trying to reply to a notice a the target
#. has no access to.
#. TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
#: actions/favor.php:62
#, php-format
msgid "%1$s has no right to reply to notice %2$d."
msgstr ""

#: classes/Fave.php:86
#, php-format
msgid "Could not store new object of type %s"
msgstr ""

#. TRANS: List message for notice favoured by logged in user.
#: lib/threadednoticelistfavesitem.php:51
msgctxt "FAVELIST"
msgid "You like this."
msgstr ""

#. TRANS: List message for when more than 4 people like something.
#. TRANS: %%s is a list of users liking a notice, %d is the number over 4 that
#. like the notice.
#. TRANS: Plural is decided on the total number of users liking the notice
#. (count of %%s + %d).
#: lib/threadednoticelistfavesitem.php:56
#, php-format
msgid "%%s and %d others like this."
msgid_plural "%%s and %d others like this."
msgstr[0] ""

#. TRANS: List message for favoured notices.
#. TRANS: %%s is a list of users liking a notice.
#. TRANS: Plural is based on the number of of users that have favoured a
#. notice.
#: lib/threadednoticelistfavesitem.php:64
#, php-format
msgid "%%s likes this."
msgid_plural "%%s like this."
msgstr[0] ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-05 16:12+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (http://www.transifex.com/gnu-social/gnu-social/language/zh/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Button text for adding the favourite status to a notice.
#: forms/favor.php:122
msgctxt "BUTTON"
msgid "Favor"
msgstr ""

#. TRANS: Button text for removing the favourite status for a favourite
#. notice.
#: forms/disfavor.php:123
msgctxt "BUTTON"
msgid "Disfavor favorite"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav <nickname>".
#: FavoriteModule.php:415
msgctxt "COMMANDHELP"
msgid "add user's last notice as a 'fave'"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav #<notice_id>".
#: FavoriteModule.php:417
msgctxt "COMMANDHELP"
msgid "add notice with the given id as a 'fave'"
msgstr ""

#. TRANS: Menu item in personal group navigation menu.
#: FavoriteModule.php:469
msgctxt "MENU"
msgid "Favorites"
msgstr ""

#. TRANS: Menu item in search group navigation panel.
#: FavoriteModule.php:482
msgctxt "MENU"
msgid "Popular"
msgstr ""

#. TRANS: Module description.
#: FavoriteModule.php:508
msgid "Favorites (likes) using ActivityStreams."
msgstr ""

#: actions/favor.php:57
msgid "No such notice."
msgstr ""

#. TRANS: Client error displayed when trying to reply to a notice a the target
#. has no access to.
#. TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
#: actions/favor.php:62
#, php-format
msgid "%1$s has no right to reply to notice %2$d."
msgstr ""

#: classes/Fave.php:86
#, php-format
msgid "Could not store new object of type %s"
msgstr ""

#. TRANS: List message for notice favoured by logged in user.
#: lib/threadednoticelistfavesitem.php:51
msgctxt "FAVELIST"
msgid "You like this."
msgstr ""

#. TRANS: List message for when more than 4 people like something.
#. TRANS: %%s is a list of users liking a notice, %d is the number over 4 that
#. like the notice.
#. TRANS: Plural is decided on the total number of users liking the notice
#. (count of %%s + %d).
#: lib/threadednoticelistfavesitem.php:56
#, php-format
msgid "%%s and %d others like this."
msgid_plural "%%s and %d others like this."
msgstr[0] ""

#. TRANS: List message for favoured notices.
#. TRANS: %%s is a list of users liking a notice.
#. TRANS: Plural is based on the number of of users that have favoured a
#. notice.
#: lib/threadednoticelistfavesitem.php:64
#, php-format
msgid "%%s likes this."
msgid_plural "%%s like this."
msgstr[0] ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-02-05 16:12+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Chinese (China) (http://www.transifex.com/gnu-social/gnu-social/language/zh_CN/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_CN\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Button text for adding the favourite status to a notice.
#: forms/favor.php:122
msgctxt "BUTTON"
msgid "Favor"
msgstr ""

#. TRANS: Button text for removing the favourite status for a favourite
#. notice.
#: forms/disfavor.php:123
msgctxt "BUTTON"
msgid "Disfavor favorite"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav <nickname>".
#: FavoriteModule.php:415
msgctxt "COMMANDHELP"
msgid "add user's last notice as a 'fave'"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav #<notice_id>".
#: FavoriteModule.php:417
msgctxt "COMMANDHELP"
msgid "add notice with the given id as a 'fave'"
msgstr ""

#. TRANS: Menu item in personal group navigation menu.
#: FavoriteModule.php:469
msgctxt "MENU"
msgid "Favorites"
msgstr ""

#. TRANS: Menu item in search group navigation panel.
#: FavoriteModule.php:482
msgctxt "MENU"
msgid "Popular"
msgstr ""

#. TRANS: Module description.
#: FavoriteModule.php:508
msgid "Favorites (likes) using ActivityStreams."
msgstr ""

#: actions/favor.php:57
msgid "No such notice."
msgstr "没有这条消息。"

#. TRANS: Client error displayed when trying to reply to a notice a the target
#. has no access to.
#. TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
#: actions/favor.php:62
#, php-format
msgid "%1$s has no right to reply to notice %2$d."
msgstr ""

#: classes/Fave.php:86
#, php-format
msgid "Could not store new object of type %s"
msgstr ""

#. TRANS: List message for notice favoured by logged in user.
#: lib/threadednoticelistfavesitem.php:51
msgctxt "FAVELIST"
msgid "You like this."
msgstr ""

#. TRANS: List message for when more than 4 people like something.
#. TRANS: %%s is a list of users liking a notice, %d is the number over 4 that
#. like the notice.
#. TRANS: Plural is decided on the total number of users liking the notice
#. (count of %%s + %d).
#: lib/threadednoticelistfavesitem.php:56
#, php-format
msgid "%%s and %d others like this."
msgid_plural "%%s and %d others like this."
msgstr[0] ""

#. TRANS: List message for favoured notices.
#. TRANS: %%s is a list of users liking a notice.
#. TRANS: Plural is based on the number of of users that have favoured a
#. notice.
#: lib/threadednoticelistfavesitem.php:64
#, php-format
msgid "%%s likes this."
msgid_plural "%%s like this."
msgstr[0] ""


code:
# Translation file for GNU social - the free software social networking platform
# Copyright (C) 2015 - 2019 Free Software Foundation, Inc http://www.fsf.org
# This file is under https://www.gnu.org/licenses/agpl v3 or later
#
# Translators:
# Po-Chun Huang <aphroteus@gmail.com>, 2015
msgid ""
msgstr ""
"Project-Id-Version: GNU social\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2015-02-02 17:47+0100\n"
"PO-Revision-Date: 2015-03-26 09:54+0000\n"
"Last-Translator: Po-Chun Huang <aphroteus@gmail.com>\n"
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/gnu-social/gnu-social/language/zh_TW/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: zh_TW\n"
"Plural-Forms: nplurals=1; plural=0;\n"

#. TRANS: Button text for adding the favourite status to a notice.
#: forms/favor.php:122
msgctxt "BUTTON"
msgid "Favor"
msgstr ""

#. TRANS: Button text for removing the favourite status for a favourite
#. notice.
#: forms/disfavor.php:123
msgctxt "BUTTON"
msgid "Disfavor favorite"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav <nickname>".
#: FavoriteModule.php:415
msgctxt "COMMANDHELP"
msgid "add user's last notice as a 'fave'"
msgstr ""

#. TRANS: Help message for IM/SMS command "fav #<notice_id>".
#: FavoriteModule.php:417
msgctxt "COMMANDHELP"
msgid "add notice with the given id as a 'fave'"
msgstr ""

#. TRANS: Menu item in personal group navigation menu.
#: FavoriteModule.php:469
msgctxt "MENU"
msgid "Favorites"
msgstr ""

#. TRANS: Menu item in search group navigation panel.
#: FavoriteModule.php:482
msgctxt "MENU"
msgid "Popular"
msgstr ""

#. TRANS: Module description.
#: FavoriteModule.php:508
msgid "Favorites (likes) using ActivityStreams."
msgstr ""

#: actions/favor.php:57
msgid "No such notice."
msgstr "無此通知。"

#. TRANS: Client error displayed when trying to reply to a notice a the target
#. has no access to.
#. TRANS: %1$s is a user nickname, %2$d is a notice ID (number).
#: actions/favor.php:62
#, php-format
msgid "%1$s has no right to reply to notice %2$d."
msgstr ""

#: classes/Fave.php:86
#, php-format
msgid "Could not store new object of type %s"
msgstr ""

#. TRANS: List message for notice favoured by logged in user.
#: lib/threadednoticelistfavesitem.php:51
msgctxt "FAVELIST"
msgid "You like this."
msgstr ""

#. TRANS: List message for when more than 4 people like something.
#. TRANS: %%s is a list of users liking a notice, %d is the number over 4 that
#. like the notice.
#. TRANS: Plural is decided on the total number of users liking the notice
#. (count of %%s + %d).
#: lib/threadednoticelistfavesitem.php:56
#, php-format
msgid "%%s and %d others like this."
msgid_plural "%%s and %d others like this."
msgstr[0] ""

#. TRANS: List message for favoured notices.
#. TRANS: %%s is a list of users liking a notice.
#. TRANS: Plural is based on the number of of users that have favoured a
#. notice.
#: lib/threadednoticelistfavesitem.php:64
#, php-format
msgid "%%s likes this."
msgid_plural "%%s like this."
msgstr[0] ""


code:
<?php
/*
 * GNU Social - a federating social network
 * Copyright (C) 2014, Free Software Foundation, 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); }

/**
 * @package     Activity
 * @maintainer  Mikael Nordfeldth <mmn@hethane.se>
 */
class HTMLPurifierSchemesModule extends Module
{
    const MODULE_VERSION = '2.0.0';

    public function onModuleVersion(array &$versions): bool
    {
        $versions[] = array('name' => 'HTMLPurifier Schemes',
                            'version' => self::MODULE_VERSION,
                            'author' => 'Mikael Nordfeldth',
                            'homepage' => GNUSOCIAL_ENGINE_URL,
                            'rawdescription' =>
                            // TRANS: Module description.
                            _m('Additional URI schemes for HTMLPurifier.'));

        return true;
    }
}


code:
<?php

/**
 * Validates bitcoin (Bitcoin addresses)
 * @todo validate address length and stuff
 */

class HTMLPurifier_URIScheme_bitcoin extends HTMLPurifier_URIScheme
{
    /**
     * @type bool
     */
    public $browsable = false;

    /**
     * @type bool
     */
    public $may_omit_host = true;

    /**
     * @param HTMLPurifier_URI $uri
     * @param HTMLPurifier_Config $config
     * @param HTMLPurifier_Context $context
     * @return bool
     */
    public function doValidate(&$uri, $config, $context)
    {
        $uri->userinfo = null;
        $uri->host     = null;
        $uri->port     = null;
        return true;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Validates geo (geographical coordinates) according to RFC 5870
 * @todo Filter allowed query parameters and their order
 */

class HTMLPurifier_URIScheme_geo extends HTMLPurifier_URIScheme
{
    /**
     * @type bool
     */
    public $browsable = false;

    /**
     * @type bool
     */
    public $may_omit_host = true;

    /**
     * @param HTMLPurifier_URI $uri
     * @param HTMLPurifier_Config $config
     * @param HTMLPurifier_Context $context
     * @return bool
     */
    public function doValidate(&$uri, $config, $context)
    {
        $uri->userinfo = null;
        $uri->host     = null;
        $uri->port     = null;
        return true;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Validates gopher
 */
class HTMLPurifier_URIScheme_gopher extends HTMLPurifier_URIScheme
{
    /**
     * @type int
     */
    public $default_port = 70;

    /**
     * @type bool
     */
    public $browsable = true;

    /**
     * @type bool
     */
    public $hierarchical = true;

    /**
     * @param HTMLPurifier_URI $uri
     * @param HTMLPurifier_Config $config
     * @param HTMLPurifier_Context $context
     * @return bool
     */
    public function doValidate(&$uri, $config, $context)
    {
        $uri->userinfo = null;
        return true;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Validates irc
 */
class HTMLPurifier_URIScheme_irc extends HTMLPurifier_URIScheme
{
    /**
     * @type int
     */
//    public $default_port = 6667;

    /**
     * @type bool
     */
    public $browsable = false;

    /**
     * @type bool
     */
    public $hierarchical = true;

    /**
     * @param HTMLPurifier_URI $uri
     * @param HTMLPurifier_Config $config
     * @param HTMLPurifier_Context $context
     * @return bool
     */
    public function doValidate(&$uri, $config, $context)
    {
        $uri->userinfo = null;
        return true;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Validates irc
 */
class HTMLPurifier_URIScheme_ircs extends HTMLPurifier_URIScheme
{
    /**
     * @type int
     */
//    public $default_port = 6697;

    /**
     * @type bool
     */
    public $secure = true;

    /**
     * @type bool
     */
    public $browsable = false;

    /**
     * @type bool
     */
    public $hierarchical = true;

    /**
     * @param HTMLPurifier_URI $uri
     * @param HTMLPurifier_Config $config
     * @param HTMLPurifier_Context $context
     * @return bool
     */
    public function doValidate(&$uri, $config, $context)
    {
        $uri->userinfo = null;
        return true;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Validates magnet (BitTorrent hash links)
 * @todo require xt= GET parameter
 */

class HTMLPurifier_URIScheme_magnet extends HTMLPurifier_URIScheme
{
    /**
     * @type bool
     */
    public $browsable = false;

    /**
     * @type bool
     */
    public $may_omit_host = true;

    /**
     * @param HTMLPurifier_URI $uri
     * @param HTMLPurifier_Config $config
     * @param HTMLPurifier_Context $context
     * @return bool
     */
    public function doValidate(&$uri, $config, $context)
    {
        $uri->userinfo = null;
        $uri->host     = null;
        $uri->port     = null;
        return true;
    }
}

// vim: et sw=4 sts=4


code:
<?php

/**
 * Validates xmpp (for XMPP, so called JIDs)
 * @todo Validate the xmpp address
 */

class HTMLPurifier_URIScheme_xmpp extends HTMLPurifier_URIScheme
{
    /**
     * @type bool
     */
    public $browsable = false;

    /**
     * @type bool
     */
    public $may_omit_host = true;

    /**
     * @param HTMLPurifier_URI $uri
     * @param HTMLPurifier_Config $config
     * @param HTMLPurifier_Context $context
     * @return bool
     */
    public function doValidate(&$uri, $config, $context)
    {
        $uri->userinfo = null;
        $uri->host     = null;
        $uri->port     = null;
        return true;
    }
}

// vim: et sw=4 sts=4


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-06-08 18:20+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#. TRANS: Module description.
#: HTMLPurifierSchemesModule.php:41
msgid "Additional URI schemes for HTMLPurifier."
msgstr ""


code:
GNU social modules are like plugins but cannot be disabled. Having "mandatory plugins" allows to aggregate together, more
logically, some of the core functionality, which makes it easier to find where a particular component was defined.


code:
The Share plugin implements "Shares" (repeats) type of notices

Installation
============
This plugin is enabled by default

Settings
========
none

path: modules/Share/README


code:
<?php
/*
 * GNU Social - a federating social network
 * Copyright (C) 2014, Free Software Foundation, 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); }

/**
 * @package     Activity
 * @maintainer  Mikael Nordfeldth <mmn@hethane.se>
 */
class ShareModule extends ActivityVerbHandlerModule
{
    const MODULE_VERSION = '2.0.0';

    public function tag()
    {
        return 'share';
    }

    public function types()
    {
        return array();
    }

    public function verbs()
    {
        return array(ActivityVerb::SHARE);
    }

    // Share is a bit special and $act->objects[0] should be an Activity
    // instead of ActivityObject! Therefore also $act->objects[0]->type is not set.
    public function isMyActivity(Activity $act) {
        return (count($act->objects) == 1
            && ($act->objects[0] instanceof Activity)
            && $this->isMyVerb($act->verb));
    }

    public function onRouterInitialized(URLMapper $m)
    {
        // Web UI actions
        $m->connect('main/repeat', ['action' => 'repeat']);

        // Share for Twitter API ("Retweet")
        $m->connect('api/statuses/retweeted_by_me.:format',
                    ['action' => 'ApiTimelineRetweetedByMe'],
                    ['format' => '(xml|json|atom|as)']);

        $m->connect('api/statuses/retweeted_to_me.:format',
                    ['action' => 'ApiTimelineRetweetedToMe'],
                    ['format' => '(xml|json|atom|as)']);

        $m->connect('api/statuses/retweets_of_me.:format',
                    ['action' => 'ApiTimelineRetweetsOfMe'],
                    ['format' => '(xml|json|atom|as)']);

        $m->connect('api/statuses/retweet/:id.:format',
                    ['action' => 'ApiStatusesRetweet'],
                    ['id'     => '[0-9]+',
                     'format' => '(xml|json)']);

        $m->connect('api/statuses/retweets/:id.:format',
                    ['action' => 'ApiStatusesRetweets'],
                    ['id'     => '[0-9]+',
                     'format' => '(xml|json)']);
    }

    // FIXME: Set this to abstract public in lib/modules/ActivityHandlerPlugin.php when all plugins have migrated!
    protected function saveObjectFromActivity(Activity $act, Notice $stored, array $options=array())
    {
        assert($this->isMyActivity($act));

        // The below algorithm is mainly copied from the previous Ostatus_profile->processShare()

        if (count($act->objects) !== 1) {
            // TRANS: Client exception thrown when trying to share multiple activities at once.
            throw new ClientException(_m('Can only handle share activities with exactly one object.'));
        }

        $shared = $act->objects[0];
        if (!$shared instanceof Activity) {
            // TRANS: Client exception thrown when trying to share a non-activity object.
            throw new ClientException(_m('Can only handle shared activities.'));
        }

        $sharedUri = $shared->id;
        if (!empty($shared->objects[0]->id)) {
            // Because StatusNet since commit 8cc4660 sets $shared->id to a TagURI which
            // fucks up federation, because the URI is no longer recognised by the origin.
            // So we set it to the object ID if it exists, otherwise we trust $shared->id
            $sharedUri = $shared->objects[0]->id;
        }
        if (empty($sharedUri)) {
            throw new ClientException(_m('Shared activity does not have an id'));
        }

        try {
            // First check if we have the shared activity. This has to be done first, because
            // we can't use these functions to "ensureActivityObjectProfile" of a local user,
            // who might be the creator of the shared activity in question.
            $sharedNotice = Notice::getByUri($sharedUri);
        } catch (NoResultException $e) {
            // If no locally stored notice is found, process it!
            // TODO: Remember to check Deleted_notice!
            // TODO: If a post is shared that we can't retrieve - what to do?
            $other = Ostatus_profile::ensureActivityObjectProfile($shared->actor);
            $sharedNotice = Notice::saveActivity($shared, $other->localProfile(), array('source'=>'share'));
        } catch (FeedSubException $e) {
            // Remote feed could not be found or verified, should we
            // transform this into an "RT @user Blah, blah, blah..."?
            common_log(LOG_INFO, __METHOD__ . ' got a ' . get_class($e) . ': ' . $e->getMessage());
            return false;
        }

        // Setting this here because when the algorithm gets back to
        // Notice::saveActivity it will update the Notice object.
        $stored->repeat_of = $sharedNotice->getID();
        $stored->conversation = $sharedNotice->conversation;

        // We don't have to save a repeat in a separate table, we can
        // find repeats by just looking at the notice.repeat_of field.

        // By returning true here instead of something that evaluates
        // to false, we show that we have processed everything properly.
        return true;
    }

    // FIXME: Put this in lib/modules/ActivityHandlerPlugin.php when we're ready
    //          with the other microapps/activityhandlers as well.
    //          Also it should be StartNoticeAsActivity (with a prepped Activity, including ->context etc.)
    public function onEndNoticeAsActivity(Notice $stored, Activity $act, Profile $scoped=null)
    {
        if (!$this->isMyNotice($stored)) {
            return true;
        }

        $this->extendActivity($stored, $act, $scoped);
        return false;
    }

    public function extendActivity(Notice $stored, Activity $act, Profile $scoped=null)
    {
        // TODO: How to handle repeats of deleted notices?
        $target = Notice::getByID($stored->repeat_of);
        // TRANS: A repeat activity's title. %1$s is repeater's nickname
        //        and %2$s is the repeated user's nickname.
        $act->title = sprintf(_('%1$s repeated a notice by %2$s'),
                              $stored->getProfile()->getNickname(),
                              $target->getProfile()->getNickname());
        $act->objects[] = $target->asActivity($scoped);
    }

    public function activityObjectFromNotice(Notice $stored)
    {
        // Repeat is a little bit special. As it's an activity, our
        // ActivityObject is instead turned into an Activity
        $object          = new Activity();
        $object->actor   = $stored->getProfile()->asActivityObject();
        $object->verb    = ActivityVerb::SHARE;
        $object->content = $stored->getRendered();
        $this->extendActivity($stored, $object);

        return $object;
    }

    public function deleteRelated(Notice $notice)
    {
        // No action needed as we don't have a separate table for share objects.
        return true;
    }

    // Layout stuff

    /**
     * show a link to the author of repeat
     *
     * FIXME: Some repeat stuff still in lib/noticelistitem.php! ($nli->repeat etc.)
     */
    public function onEndShowNoticeInfo(NoticeListItem $nli)
    {
        if (!empty($nli->repeat)) {
            $repeater = $nli->repeat->getProfile();

            $attrs = array('href' => $repeater->getUrl(),
                           'class' => 'h-card p-author',
                           'title' => $repeater->getFancyName());

            $nli->out->elementStart('span', 'repeat');

            // TRANS: Addition in notice list item if notice was repeated. Followed by a span with a nickname.
            $nli->out->raw(_('Repeated by').' ');

            $nli->out->element('a', $attrs, $repeater->getNickname());

            $nli->out->elementEnd('span');
        }
    }

    public function onEndShowThreadedNoticeTailItems(NoticeListItem $nli, Notice $notice, &$threadActive)
    {
        if ($nli instanceof ThreadedNoticeListSubItem) {
            // The sub-items are replies to a conversation, thus we use different HTML elements etc.
            $item = new ThreadedNoticeListInlineRepeatsItem($notice, $nli->out);
        } else {
            $item = new ThreadedNoticeListRepeatsItem($notice, $nli->out);
        }
        $threadActive = $item->show() || $threadActive;
        return true;
    }

    /**
     * show the "repeat" form in the notice options element
     * FIXME: Don't let a NoticeListItemAdapter slip in here (or extend that from NoticeListItem)
     *
     * @return void
     */
    public function onEndShowNoticeOptionItems($nli)
    {
        $notice = $nli->notice;

        // We shouldn't be restricting Shares for received unlisted notices,
        // but without subscription_policy working we treat both this type
        // and followers-only notices the same, so we also restrict both.
        if (!$notice->isPublic()) {
            return;
        }

        // FIXME: Use bitmasks (but be aware that PUBLIC_SCOPE is 0!)
        // Also: AHHH, $scope and $scoped are scarily similar looking.
        $scope = $notice->getScope();
        if ($scope === Notice::PUBLIC_SCOPE || $scope === Notice::SITE_SCOPE) {
            $scoped = Profile::current();
            if ($scoped instanceof Profile &&
                    $scoped->getID() !== $notice->getProfile()->getID()) {

                if ($scoped->hasRepeated($notice)) {
                    $nli->out->element('span', array('class' => 'repeated',
                                                      // TRANS: Title for repeat form status in notice list when a notice has been repeated.
                                                      'title' => _('Notice repeated.')),
                                        // TRANS: Repeat form status in notice list when a notice has been repeated.
                                        _('Repeated'));
                } else {
                    $repeat = new RepeatForm($nli->out, $notice);
                    $repeat->show();
                }
            }
        }
    }

    protected function showNoticeListItem(NoticeListItem $nli)
    {
        // pass
    }
    public function openNoticeListItemElement(NoticeListItem $nli)
    {
        // pass
    }
    public function closeNoticeListItemElement(NoticeListItem $nli)
    {
        // pass
    }

    // API stuff

    /**
     * Typically just used to fill out Twitter-compatible API status data.
     *
     * FIXME: Make all the calls before this end up with a Notice instead of ArrayWrapper please...
     */
    public function onNoticeSimpleStatusArray($notice, array &$status, Profile $scoped=null, array $args=array())
    {
        $status['repeated'] = $scoped instanceof Profile
                            ? $scoped->hasRepeated($notice)
                            : false;

        if ($status['repeated'] === true) {
            // Qvitter API wants the "repeated_id" value set too.
            $repeated = Notice::pkeyGet(array('profile_id' => $scoped->getID(),
                                              'repeat_of' => $notice->getID(),
                                              'verb' => ActivityVerb::SHARE));
            $status['repeated_id'] = $repeated->getID();
        }
    }

    public function onTwitterUserArray(Profile $profile, array &$userdata, Profile $scoped=null, array $args=array())
    {
        $userdata['favourites_count'] = Fave::countByProfile($profile);
    }

    // Command stuff

    /**
     * EndInterpretCommand for RepeatPlugin will handle the 'repeat' command
     * using the class RepeatCommand.
     *
     * @param string  $cmd     Command being run
     * @param string  $arg     Rest of the message (including address)
     * @param User    $user    User sending the message
     * @param Command &$result The resulting command object to be run.
     *
     * @return boolean hook value
     */
    public function onStartInterpretCommand($cmd, $arg, $user, &$result)
    {
        if ($result === false && in_array($cmd, array('repeat', 'rp', 'rt', 'rd'))) {
            if (empty($arg)) {
                $result = null;
            } else {
                list($other, $extra) = CommandInterpreter::split_arg($arg);
                if (!empty($extra)) {
                    $result = null;
                } else {
                    $result = new RepeatCommand($user, $other);
                }
            }
            return false;
        }
        return true;
    }

    public function onHelpCommandMessages(HelpCommand $help, array &$commands)
    {
        // TRANS: Help message for IM/SMS command "repeat #<notice_id>".
        $commands['repeat #<notice_id>'] = _m('COMMANDHELP', "repeat a notice with a given id");
        // TRANS: Help message for IM/SMS command "repeat <nickname>".
        $commands['repeat <nickname>']   = _m('COMMANDHELP', "repeat the last notice from user");
    }

    /**
     * Are we allowed to perform a certain command over the API?
     */
    public function onCommandSupportedAPI(Command $cmd, &$supported)
    {
        $supported = $supported || $cmd instanceof RepeatCommand;
    }

    protected function getActionTitle(ManagedAction $action, $verb, Notice $target, Profile $scoped)
    {
        // return page title
    }

    protected function doActionPreparation(ManagedAction $action, $verb, Notice $target, Profile $scoped)
    {
        // prepare Action?
    }

    protected function doActionPost(ManagedAction $action, $verb, Notice $target, Profile $scoped)
    {
        // handle repeat POST
    }

    protected function getActivityForm(ManagedAction $action, $verb, Notice $target, Profile $scoped)
    {
        return new RepeatForm($action, $target);
    }

    public function onModuleVersion(array &$versions): bool
    {
        $versions[] = array('name' => 'Share verb',
                            'version' => self::MODULE_VERSION,
                            'author' => 'Mikael Nordfeldth',
                            'homepage' => GNUSOCIAL_ENGINE_URL,
                            'rawdescription' =>
                            // TRANS: Plugin description.
                            _m('Shares (repeats) using ActivityStreams.'));

        return true;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Repeat 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    Evan Prodromou <evan@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); }

/**
 * Repeat a notice through the API
 *
 * @category API
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class ApiStatusesRetweetAction extends ApiAuthAction
{
    protected $needPost = true;

    var $original = null;

    /**
     * Take arguments for running
     *
     * @param array $args $_REQUEST args
     *
     * @return boolean success flag
     */
    protected function prepare(array $args=array())
    {
        parent::prepare($args);

        $id = $this->trimmed('id');

        $this->original = Notice::getKV('id', $id);

        if (!$this->original instanceof Notice) {
            // TRANS: Client error displayed trying to repeat a non-existing notice through the API.
            $this->clientError(_('No such notice.'), 400, $this->format);
        }

        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();

        $repeat = $this->original->repeat($this->scoped, $this->source);

        $this->showNotice($repeat);
    }

    /**
     * Show the resulting notice
     *
     * @return void
     */
    function showNotice($notice)
    {
        if (!empty($notice)) {
            if ($this->format == 'xml') {
                $this->showSingleXmlStatus($notice);
            } elseif ($this->format == 'json') {
                $this->show_single_json_status($notice);
            }
        }
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Show up to 100 repeats of a notice
 *
 * 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 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);
}

/**
 * Show up to 100 repeats of a notice
 *
 * @category API
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class ApiStatusesRetweetsAction 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
     */
    function prepare(array $args = array())
    {
        parent::prepare($args);

        $id = $this->trimmed('id');

        $this->original = Notice::getKV('id', $id);

        if (empty($this->original)) {
            // TRANS: Client error displayed trying to display redents of a non-exiting notice.
            $this->clientError(_('No such notice.'),
                               400, $this->format);
            return false;
        }

        $cnt = $this->trimmed('count');

        if (empty($cnt) || !is_integer($cnt)) {
            $cnt = 100;
        } else {
            $this->cnt = min((int)$cnt, self::MAXCOUNT);
        }

        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
     */
    function handle()
    {
        parent::handle();

        $strm = $this->original->repeatStream($this->cnt);

        switch ($this->format) {
        case 'xml':
            $this->showXmlTimeline($strm);
            break;
        case 'json':
            $this->showJsonTimeline($strm);
            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)
    {
        return true;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Show authenticating user's most recent repeats
 *
 * 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 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);
}

/**
 * Show authenticating user's most recent repeats
 *
 * @category API
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class ApiTimelineRetweetedByMeAction extends ApiAuthAction
{
    const DEFAULTCOUNT = 20;
    const MAXCOUNT     = 200;
    const MAXNOTICES   = 3200;

    var $repeats  = null;
    var $cnt      = self::DEFAULTCOUNT;
    var $page     = 1;
    var $since_id = null;
    var $max_id   = null;

    /**
     * Take arguments for running
     *
     * @param array $args $_REQUEST args
     *
     * @return boolean success flag
     *
     */
    function prepare(array $args = array())
    {
        parent::prepare($args);

        // TRANS: Server error displayed calling unimplemented API method for 'retweeted by me'.
        $this->serverError(_('Unimplemented.'), 503);

        return false;
    }

    /**
     * Return true if read only.
     *
     * @param array $args other arguments
     *
     * @return boolean is read only action?
     */
    function isReadOnly($args)
    {
        return true;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Show most recent notices that are repeats in user's inbox
 *
 * 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 2009 StatusNet, Inc.
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link      http://status.net/
 */

if (!defined('GNUSOCIAL')) { exit(1); }

/**
 * Show most recent notices that are repeats in user's inbox
 *
 * @category API
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class ApiTimelineRetweetedToMeAction extends ApiAuthAction
{
    const DEFAULTCOUNT = 20;
    const MAXCOUNT     = 200;
    const MAXNOTICES   = 3200;

    var $repeats  = null;
    var $cnt      = self::DEFAULTCOUNT;
    var $page     = 1;
    var $since_id = null;
    var $max_id   = null;

    /**
     * Take arguments for running
     *
     * @param array $args $_REQUEST args
     *
     * @return boolean success flag
     */
    protected function prepare(array $args=array())
    {
        parent::prepare($args);

        $cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1);

        $page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt));

        $since_id = $this->int('since_id');

        $max_id = $this->int('max_id');

        return true;
    }

    /**
     * Handle the request
     *
     * show a timeline of the user's repeated notices
     *
     * @return void
     */
    protected function handle()
    {
        parent::handle();

        $offset = ($this->page-1) * $this->cnt;
        $limit  = $this->cnt;

        // TRANS: Title for Atom feed "repeated to me". %s is the user nickname.
        $title      = sprintf(_("Repeated to %s"), $this->scoped->getNickname());
        $subtitle   = sprintf(
            // @todo FIXME: $profile is not defined.
            // TRANS: Subtitle for API action that shows most recent notices that are repeats in user's inbox.
            // TRANS: %1$s is the sitename, %2$s is a user nickname, %3$s is a user profile name.
            _('%1$s notices that were to repeated to %2$s / %3$s.'),
            $sitename, $this->scoped->getNickname(), $profile->getBestName()
        );
        $taguribase = TagURI::base();
        $id         = "tag:$taguribase:RepeatedToMe:" . $this->scoped->id;

        $link = common_local_url(
            'all',
             array('nickname' => $this->scoped->getNickname())
        );

        $strm = $this->scoped->repeatedToMe($offset, $limit, $this->since_id, $this->max_id);

        switch ($this->format) {
        case 'xml':
            $this->showXmlTimeline($strm);
            break;
        case 'json':
            $this->showJsonTimeline($strm);
            break;
        case 'atom':
            header('Content-Type: application/atom+xml; charset=utf-8');

            $atom = new AtomNoticeFeed($this->scoped->getUser());

            $atom->setId($id);
            $atom->setTitle($title);
            $atom->setSubtitle($subtitle);
            $atom->setUpdated('now');
            $atom->addLink($link);

            $id = $this->arg('id');

            $atom->setSelfLink($self);
            $atom->addEntryFromNotices($strm);

            $this->raw($atom->getString());

            break;
        case 'as':
            header('Content-Type: ' . ActivityStreamJSONDocument::CONTENT_TYPE);
            $doc = new ActivityStreamJSONDocument($this->scoped->getUser());
            $doc->setTitle($title);
            $doc->addLink($link, 'alternate', 'text/html');
            $doc->addItemsFromNotices($strm);
            $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)
    {
        return true;
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Show authenticating user's most recent notices that have been repeated
 *
 * 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 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);
}

/**
 * Show authenticating user's most recent notices that have been repeated
 *
 * @category API
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class ApiTimelineRetweetsOfMeAction extends ApiAuthAction
{
    const DEFAULTCOUNT = 20;
    const MAXCOUNT     = 200;
    const MAXNOTICES   = 3200;

    var $repeats  = null;
    var $cnt      = self::DEFAULTCOUNT;
    var $page     = 1;
    var $since_id = null;
    var $max_id   = null;

    /**
     * Take arguments for running
     *
     * @param array $args $_REQUEST args
     *
     * @return boolean success flag
     */
    function prepare(array $args = array())
    {
        parent::prepare($args);

        $cnt = $this->int('count', self::DEFAULTCOUNT, self::MAXCOUNT, 1);

        $page = $this->int('page', 1, (self::MAXNOTICES/$this->cnt));

        $since_id = $this->int('since_id');

        $max_id = $this->int('max_id');

        return true;
    }

    /**
     * Handle the request
     *
     * show a timeline of the user's repeated notices
     *
     * @param array $args $_REQUEST data (unused)
     *
     * @return void
     */
    function handle()
    {
        parent::handle();

        $offset = ($this->page-1) * $this->cnt;
        $limit  = $this->cnt;

        // TRANS: Title of list of repeated notices of the logged in user.
        // TRANS: %s is the nickname of the logged in user.
        $title      = sprintf(_("Repeats of %s"), $this->auth_user->nickname);
        $sitename   = common_config('site', 'name');

        $profile = $this->auth_user->getProfile();

        $subtitle   = sprintf(
            // TRANS: Subtitle of API time with retweets of me.
            // TRANS: %1$s is the StatusNet sitename, %2$s is the user nickname, %3$s is the user profile name.
            _('%1$s notices that %2$s / %3$s has repeated.'),
            $sitename, $this->auth_user->nickname, $profile->getBestName()
        );

        $taguribase = TagURI::base();
        $id         = "tag:$taguribase:RepeatsOfMe:" . $this->auth_user->id;

        $link = common_local_url(
            'all',
             array('nickname' => $this->auth_user->nickname)
        );

        // This is a really bad query for some reason

        if (!common_config('performance', 'high')) {
            $strm = $this->auth_user->repeatsOfMe($offset, $limit, $this->since_id, $this->max_id);
        } else {
            $strm = new Notice();
            $strm->whereAdd('0 = 1');
            $strm->find();
        }

        switch ($this->format) {
        case 'xml':
            $this->showXmlTimeline($strm);
            break;
        case 'json':
            $this->showJsonTimeline($strm);
            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->setUpdated('now');
            $atom->addLink($link);
            $atom->setSelfLink($this->getSelfUri());
            $atom->addEntryFromNotices($strm);
            $this->raw($atom->getString());
            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($strm);
            $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);
            break;
        }
    }

    /**
     * Return true if read only.
     *
     * MAY override
     *
     * @param array $args other arguments
     *
     * @return boolean is read only action?
     */
    function isReadOnly($args)
    {
        return true;
    }
}


code:
<?php
/**
 * Repeat action.
 *
 * PHP version 5
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@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); }

/**
 * Repeat action
 *
 * @category Action
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
 * @link     http://status.net/
 */
class RepeatAction extends FormAction
{
    protected $notice = null;   // Notice that is being repeated.
    protected $repeat = null;   // The resulting repeat object/notice.

    function title()
    {
        return _m('TITLE', 'Repeat notice');
    }

    protected function doPreparation()
    {
        $id = $this->trimmed('notice');

        if (empty($id)) {
            // TRANS: Client error displayed when trying to repeat a notice while not providing a notice ID.
            $this->clientError(_('No notice specified.'));
        }

        $this->notice = Notice::getKV('id', $id);

        if (!$this->notice instanceof Notice) {
            // TRANS: Client error displayed when trying to repeat a non-existing notice.
            $this->clientError(_('Notice not found.'));
        }

        $this->repeat = $this->notice->repeat($this->scoped, 'web');
        if (!$this->repeat instanceof Notice) {
            // TRANS: Error when unable to repeat a notice for unknown reason.
            $this->clientError(_('Could not repeat notice for unknown reason. Please contact the webmaster!'));
        }

        return true;
    }

    /**
     * Class handler.
     *
     * @param array $args query arguments
     *
     * @return void
     */
    protected function showContent()
    {
        $this->element('p', array('id' => 'repeat_response',
                                  'class' => 'repeated'),
                            // TRANS: Confirmation text after repeating a notice.
                            _('Repeated!'));
    }
}


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Form for repeating a notice
 *
 * 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  Form
 * @package   StatusNet
 * @author    Evan Prodromou <evan@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);
}

/**
 * Form for repeating a notice
 *
 * @category Form
 * @package  StatusNet
 * @author   Evan Prodromou <evan@status.net>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class RepeatForm extends Form
{
    /**
     * Notice to repeat
     */
    var $notice = null;

    /**
     * Constructor
     *
     * @param HTMLOutputter $out    output channel
     * @param Notice        $notice notice to repeat
     */
    function __construct($out=null, $notice=null)
    {
        parent::__construct($out);

        $this->notice = $notice;
    }

    /**
     * ID of the form
     *
     * @return int ID of the form
     */
    function id()
    {
        return 'repeat-' . $this->notice->id;
    }

    /**
     * Action of the form
     *
     * @return string URL of the action
     */
    function action()
    {
        return common_local_url('repeat');
    }

    /**
     * Legend of the Form
     *
     * @return void
     */
    function formLegend()
    {
        // TRANS: For legend for notice repeat form.
        $this->out->element('legend', null, _('Repeat this notice?'));
    }

    /**
     * Data elements
     *
     * @return void
     */
    function formData()
    {
        $this->out->hidden('notice-n'.$this->notice->id,
                           $this->notice->id,
                           'notice');
    }

    /**
     * Action elements
     *
     * @return void
     */
    function formActions()
    {
        $this->out->submit('repeat-submit-' . $this->notice->id,
                           // TRANS: Button text to repeat a notice on notice repeat form.
                           _m('BUTTON','Yes'), 'submit', null,
                           // TRANS: Button title to repeat a notice on notice repeat form.
                           _('Repeat this notice.'));
    }

    /**
     * Class of the form.
     *
     * @return string the form's class
     */
    function formClass()
    {
        return 'form_repeat';
    }
}


code:
<?php

if (!defined('GNUSOCIAL')) { exit(1); }

class RepeatCommand extends Command
{
    var $other = null;
    function __construct($user, $other)
    {
        parent::__construct($user);
        $this->other = $other;
    }

    function handle($channel)
    {
        $notice = $this->getNotice($this->other);

        try {
            $repeat = $notice->repeat($this->scoped->id, $channel->source());
            $recipient = $notice->getProfile();

            // TRANS: Message given having repeated a notice from another user.
            // TRANS: %s is the name of the user for which the notice was repeated.
            $channel->output($this->user, sprintf(_('Notice from %s repeated.'), $recipient->nickname));
        } catch (Exception $e) {
            $channel->error($this->user, $e->getMessage());
        }
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Stream of notices repeated by me
 *
 * 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  Stream
 * @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('GNUSOCIAL')) { exit(1); }

/**
 * Stream of notices repeated by me
 *
 * @category  General
 * @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 RepeatedByMeNoticeStream extends ScopingNoticeStream
{
    function __construct(Profile $target, Profile $scoped=null)
    {
        parent::__construct(new CachingNoticeStream(new RawRepeatedByMeNoticeStream($target),
                                                    'user:repeated_by_me:'.$target->getID()),
                            $scoped);
    }
}

/**
 * Raw stream of notices repeated by me
 *
 * @category  General
 * @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 RawRepeatedByMeNoticeStream extends NoticeStream
{
    protected $target;

    function __construct(Profile $target)
    {
        $this->target = $target;
    }

    function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        $notice = new Notice();

        $notice->selectAdd(); // clears it
        $notice->selectAdd('id');

        $notice->profile_id = $this->target->getID();
        $notice->whereAdd('repeat_of IS NOT NULL');

        $notice->orderBy('created DESC, id DESC');

        if (!is_null($offset)) {
            $notice->limit($offset, $limit);
        }

        Notice::addWhereSinceId($notice, $since_id);
        Notice::addWhereMaxId($notice, $max_id);

        $ids = array();

        if ($notice->find()) {
            while ($notice->fetch()) {
                $ids[] = $notice->id;
            }
        }

        $notice->free();
        $notice = NULL;

        return $ids;
    }
}


code:
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
 * Copyright (C) 2011, StatusNet, Inc.
 *
 * Stream of notices that are repeats of mine
 *
 * 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  Stream
 * @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('GNUSOCIAL')) { exit(1); }

/**
 * Stream of notices that are repeats of mine
 *
 * @category  Stream
 * @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 RepeatsOfMeNoticeStream extends ScopingNoticeStream
{
    function __construct(Profile $target, Profile $scoped=null)
    {
        parent::__construct(new CachingNoticeStream(new RawRepeatsOfMeNoticeStream($target),
                                                    'user:repeats_of_me:'.$target->getID()),
                            $scoped);
    }
}

/**
 * Raw stream of notices that are repeats of mine
 *
 * @category  Stream
 * @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 RawRepeatsOfMeNoticeStream extends NoticeStream
{
    protected $target;

    function __construct(Profile $target)
    {
        $this->target = $target;
    }

    function getNoticeIds($offset, $limit, $since_id, $max_id)
    {
        $qry =
          'SELECT DISTINCT original.id AS id ' .
          'FROM notice original JOIN notice rept ON original.id = rept.repeat_of ' .
          'WHERE original.profile_id = ' . $this->target->getID() . ' ';

        $since = Notice::whereSinceId($since_id, 'original.id', 'original.created');
        if ($since) {
            $qry .= "AND ($since) ";
        }

        $max = Notice::whereMaxId($max_id, 'original.id', 'original.created');
        if ($max) {
            $qry .= "AND ($max) ";
        }

        $qry .= 'ORDER BY original.created, original.id DESC ';

        if (!is_null($offset)) {
            $qry .= "LIMIT $limit OFFSET $offset";
        }

        $ids = array();

        $notice = new Notice();

        $notice->query($qry);

        while ($notice->fetch()) {
            $ids[] = $notice->id;
        }

        $notice->free();
        $notice = NULL;

        return $ids;
    }
}


code:
<?php

if (!defined('GNUSOCIAL')) { exit(1); }

class ThreadedNoticeListInlineRepeatsItem extends ThreadedNoticeListRepeatsItem
{
    function showStart()
    {
        $this->out->elementStart('div', array('class' => 'notice-repeats'));
    }

    function showEnd()
    {
        $this->out->elementEnd('div');
    }
}


code:
<?php

if (!defined('GNUSOCIAL')) { exit(1); }

/**
 * Placeholder for showing repeats...
 */
class ThreadedNoticeListRepeatsItem extends NoticeListActorsItem
{
    function getProfiles()
    {
        $repeats = Notice::listGet('repeat_of', array($this->notice->getID()));

        $profiles = array();
        foreach ($repeats[$this->notice->getID()] as $rep) {
            $profiles[] = $rep->profile_id;
        }

        return $profiles;
    }

    function magicList($items)
    {
        if (count($items) > 4) {
            return parent::magicList(array_slice($items, 0, 3));
        } else {
            return parent::magicList($items);
        }
    }

    function getListMessage($count, $you)
    {
        if ($count == 1 && $you) {
            // darn first person being different from third person!
            // TRANS: List message for notice repeated by logged in user.
            return _m('REPEATLIST', 'You repeated this.');
        } else if ($count > 4) {
            // TRANS: List message for when more than 4 people repeat something.
            // TRANS: %%s is a list of users liking a notice, %d is the number over 4 that like the notice.
            // TRANS: Plural is decided on the total number of users liking the notice (count of %%s + %d).
            return sprintf(_m('%%s and %d other repeated this.',
                              '%%s and %d others repeated this.',
                              $count - 3),
                           $count - 3);
        } else {
            // TRANS: List message for repeated notices.
            // TRANS: %%s is a list of users who have repeated a notice.
            // TRANS: Plural is based on the number of of users that have repeated a notice.
            return sprintf(_m('%%s repeated this.',
                              '%%s repeated this.',
                              $count),
                           $count);
        }
    }

    function showStart()
    {
        $this->out->elementStart('li', array('class' => 'notice-data notice-repeats'));
    }

    function showEnd()
    {
        $this->out->elementEnd('li');
    }
}


code:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-06-08 18:20+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"

#. TRANS: Client exception thrown when trying to share multiple activities at once.
#: ShareModule.php:91
msgid "Can only handle share activities with exactly one object."
msgstr ""

#. TRANS: Client exception thrown when trying to share a non-activity object.
#: ShareModule.php:97
msgid "Can only handle shared activities."
msgstr ""

#: ShareModule.php:108
msgid "Shared activity does not have an id"
msgstr ""

#. TRANS: Help message for IM/SMS command "repeat #<notice_id>".
#: ShareModule.php:329
msgctxt "COMMANDHELP"
msgid "repeat a notice with a given id"
msgstr ""

#. TRANS: Help message for IM/SMS command "repeat <nickname>".
#: ShareModule.php:331
msgctxt "COMMANDHELP"
msgid "repeat the last notice from user"
msgstr ""

#. TRANS: Module description.
#: ShareModule.php:370
msgid "Shares (repeats) using ActivityStreams."
msgstr ""

#. TRANS: List message for notice repeated by logged in user.
#: lib/threadednoticelistrepeatsitem.php:36
msgctxt "REPEATLIST"
msgid "You repeated this."
msgstr ""

#. TRANS: List message for when more than 4 people repeat something.
#. TRANS: %%s is a list of users liking a notice, %d is the number over 4 that like the notice.
#. TRANS: Plural is decided on the total number of users liking the notice (count of %%s + %d).
#: lib/threadednoticelistrepeatsitem.php:41
#, php-format
msgid "%%s and %d other repeated this."
msgid_plural "%%s and %d others repeated this."
msgstr[0] ""
msgstr[1] ""

#. TRANS: List message for repeated notices.
#. TRANS: %%s is a list of users who have repeated a notice.
#. TRANS: Plural is based on the number of of users that have repeated a notice.
#: lib/threadednoticelistrepeatsitem.php:49
#, php-format
msgid "%%s repeated this."
msgid_plural "%%s repeated this."
msgstr[0] ""
msgstr[1] ""

#: actions/repeat.php:48
msgctxt "TITLE"
msgid "Repeat notice"
msgstr ""

#. TRANS: Button text to repeat a notice on notice repeat form.
#: forms/repeat.php:115
msgctxt "BUTTON"
msgid "Yes"
msgstr ""


code:
StartTFNLookup: tries to locate a duplicated remote profile by URI; federation plugins must trigger this event before profile insertion
@param string $uri URI of the remote profile to be inserted
@param string $class profile class of the federation protocol that triggered the event
@param int|null &$profile_id profile ID associated with the duplicated remote profile found

EndTFNLookup: deletes a previous duplicated remote profile found; federation plugins must trigget this event after successfully inserting a new profile
@param string $class profile class of the federation protocol that triggered the event
@param int &$profile_id profile ID associated with the duplicated remote profile


code:
<?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/>.

/**
 * TheFreeNetwork, "automagic" migration of internal remote profiles
 * between different federation protocols.
 *
 * @package   GNUsocial
 * @author    Bruno Casteleiro <brunoccast@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
 */

defined('GNUSOCIAL') || die();

/**
 * Class TheFreeNetworkModule
 * This module ensures that multiple protocols serving the same purpose won't result in duplicated data.
 * This class is not to be extended but a developer implementing a new protocol should be aware of it and notify the
 * StartTFNCensus event.
 *
 * @category  Module
 * @package   GNUsocial
 * @author    Bruno Casteleiro <brunoccast@fc.up.pt>
 * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
 */
class TheFreeNetworkModule extends Module
{
    const MODULE_VERSION = '0.1.0alpha0';

    public $protocols = null; // protocols TFN should handle

    private $lrdd = false; // whether LRDD plugin is active or not

    /**
     * Initialize TFN
     *
     * @return bool hook value
     */
    public function onInitializePlugin(): bool
    {
        // some protocol plugins can be unactivated,
        // require needed classes
        $plugin_dir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'plugins';

        foreach ($this->protocols as $protocol => $class) {
            require_once $plugin_dir . DIRECTORY_SEPARATOR . $protocol . DIRECTORY_SEPARATOR . 'classes' . DIRECTORY_SEPARATOR . $class . '.php';
        }

        // $lrdd flag
        $this->lrdd = PluginList::isPluginActive("LRDD");

        return true;
    }

    /**
     * A new remote profile is being added, check if we
     * already have someone with the same URI.
     *
     * @param string $uri
     * @param string $class profile class that triggered this event
     * @param int|null &$profile_id Profile:id associated with the remote entity found
     * @return bool hook flag
     */
    public function onStartTFNLookup(string $uri, string $class, int &$profile_id = null): bool
    {
        $profile_id = $this->lookup($uri, $class);

        $perf = common_config('performance', 'high');

        if (is_null($profile_id) && !$perf && $this->lrdd) {
            // Force lookup with online resources
            $profile_id = $this->lookup($uri, $class, true);
        }

        return is_null($profile_id);
    }

    /**
     * A new remote profile was sucessfully added, delete
     * other remotes associated with the same Profile entity.
     *
     * @param string $class profile class that triggered this event
     * @param int $profile_id Profile:id associated with the new remote profile
     * @return bool hook flag
     */
    public function onEndTFNLookup(string $class, int $profile_id): bool
    {
        foreach ($this->protocols as $p => $cls) {
            if ($cls != $class) {
                $profile = $cls::getKV('profile_id', $profile_id);
                if ($profile instanceof $cls) {
                    $this->log(LOG_INFO, 'Deleting remote ' . $cls . ' associated with Profile:' . $profile_id);
                    $i = new $cls();
                    $i->profile_id = $profile_id;
                    $i->delete();
                    break;
                }
            }
        }

        return false;
    }

    /**
     * Plugin version information
     *
     * @param array $versions
     * @return bool hook true
     */
    public function onPluginVersion(array &$versions): bool
    {
        $versions[] = [
            'name' => 'TheFreeNetwork',
            'version' => self::MODULE_VERSION,
            'author' => 'Bruno Casteleiro',
            'homepage' => 'https://notabug.org/diogo/gnu-social/src/nightly/plugins/TheFreeNetwork',
            // TRANS: Module description.
            'rawdescription' => '"Automagically" migrate internal remote profiles between different federation protocols'
        ];
        return true;
    }

    /**
     * Search remote profile tables to find someone by URI.
     * When set to search online, it will grab the remote
     * entity's aliases and search with each one.
     * The table associated with the class that triggered
     * this lookup process will be discarded in the search.
     *
     * @param string $uri
     * @param string $class
     * @param bool $online
     * @return int|null Profile:id associated with the remote entity found
     */
    private function lookup(string $uri, string $class, bool $online = false): ?int
    {
        if ($online) {
            $this->log(LOG_INFO, 'Searching with online resources for a remote profile with URI: ' . $uri);
            $all_ids = LRDDPlugin::grab_profile_aliases($uri);
        } else {
            $this->log(LOG_INFO, 'Searching for a remote profile with URI: ' . $uri);
            $all_ids = [$uri];
        }

        if ($all_ids == null) {
            $this->log(LOG_INFO, 'Unable to find a remote profile with URI: ' . $uri);
            return null;
        }

        foreach ($this->protocols as $p => $cls) {
            if ($cls != $class) {
                foreach ($all_ids as $alias) {
                    $profile = $cls::getKV('uri', $alias);
                    if ($profile instanceof $cls) {
                        $this->log(LOG_INFO, 'Found a remote ' . $cls . ' associated with Profile:' . $profile->getID());
                        return $profile->getID();
                    }
                }
            }
        }

        $this->log(LOG_INFO, 'Unable to find a remote profile with URI: ' . $uri);
        return null;
    }
}


code:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="vendor/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Core">
            <directory suffix="Test.php">./tests/Core</directory>
        </testsuite>
        <testsuite name="Media">
            <directory suffix="Test.php">./tests/Media</directory>
        </testsuite>
    </testsuites>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
        <env name="MAIL_DRIVER" value="array"/>
    </php>
</phpunit>


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Plugin to implement the Account Manager specification
 *
 * 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  Plugin
 * @package   StatusNet
 * @author    Craig Andrews <candrews@integralblue.com>
 * @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') && !defined('LACONICA')) {
    exit(1);
}

class AccountManagerPlugin extends Plugin
{
    const PLUGIN_VERSION = '2.0.0';

    const AM_REL = 'acct-mgmt';

    function __construct()
    {
        parent::__construct();
    }

    /**
     * Hook for RouterInitialized event.
     *
     * @param URLMapper $m path-to-action mapper
     * @return boolean hook return
     */
    public function onRouterInitialized(URLMapper $m)
    {
        // Discovery actions
        $m->connect('main/amcd.json',
                    ['action' => 'AccountManagementControlDocument']);
        $m->connect('main/amsessionstatus',
                    ['action' => 'AccountManagementSessionStatus']);
        return true;
    }

    function onStartHostMetaLinks(&$links) {
        $links[] = new XML_XRD_Element_Link(AccountManagerPlugin::AM_REL,
                        common_local_url('AccountManagementControlDocument'));
    }

    function onStartShowHTML($action)
    {
        //Account management discovery link
        header('Link: <'.common_local_url('AccountManagementControlDocument').'>; rel="'. AccountManagerPlugin::AM_REL.'"; type="application/json"');

        //Account management login status
        $cur = common_current_user();
        if(empty($cur)) {
            header('X-Account-Management-Status: none');
        } else {
            //TODO it seems " should be escaped in the name and id, but the spec doesn't seem to indicate how to do that
            header('X-Account-Management-Status: active; name="' . $cur->nickname . '"; id="' . $cur->nickname . '"');
        }
    }

    function onLoginAction($action, &$login) {
        switch ($action)
        {
         case 'AccountManagementControlDocument':
            $login = true;
            return false;
         default:
            return true;
        }
    }

    public function onPluginVersion(array &$versions): bool
    {
        $versions[] = array('name' => 'AccountManager',
                            'version' => self::PLUGIN_VERSION,
                            'author' => 'Craig Andrews',
                            'homepage' => GNUSOCIAL_ENGINE_REPO_URL . 'tree/master/plugins/AccountManager',
                            'rawdescription' =>
                            // TRANS: Plugin description.
                            _m('The Account Manager plugin implements the Account Manager specification.'));
        return true;
    }
}


code:
The Account Manager plugin implements the Account Manager specification to "allow a server to describe an account-based user identification process in terms that a user-agent can understand."

See https://wiki.mozilla.org/Labs/Weave/Identity/Account_Manager/Spec/Latest

Installation
============
add "addPlugin('accountManager');"
to the bottom of your config.php

Settings
========
none

Example
=======
addPlugin('accountManager');

path: plugins/AccountManager/README


code:
<?php
/**
 * StatusNet, the distributed open-source microblogging tool
 *
 * Implements the JSON Account Management endpoint
 *
 * 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  AccountManager
 * @package   StatusNet
 * @author    Craig Andrews <candrews@integralblue.com>
 * @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);
}

/**
 * Implements the JSON Account Management endpoint
 *
 * @category AccountManager
 * @package  StatusNet
 * @author   ECraig Andrews <candrews@integralblue.com>
 * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
 * @link     http://status.net/
 */
class AccountManagementControlDocumentAction extends Action
{
    /**
     * handle the action
     *
     * @param array $args unused.
     *
     * @return void
     */
    function handle()
    {
        parent::handle();

        header('Content-Type: application/json; charset=utf-8');

        $amcd = array();

        if(Event::handle('StartAccountManagementControlDocument', array(&$amcd))) {

            $amcd['version'] = 1;
            $amcd['sessionstatus'] = array(
                'method' => 'GET',
                'path' => common_local_url('AccountManagementSessionStatus')
            );
            $amcd['auth-methods'] = array(
                'username-password-form' => array(
                    'connect' => array(
                        'method' => 'POST',
                        'path' => common_local_url('login'),
                        'params' => array(
                            'username' => 'nickname',
                            'password' => 'password'
                        )
                    ),
                    'disconnect' => array(
                        'method' => 'GET',
                        'path' => common_local_url('logout')
                    )
                )
            );

            Event::handle('EndAccountManagementControlDocument', array(&$amcd));
        }

        print json_encode($amcd);

        return true;
    }

    function isReadOnly()
    {
        return true;
    }
}