[#923] Merge remote-tracking branch 'remotes/upstream/develop' into twitter_oauth
authorIvan Tashkinov <ivant.business@gmail.com>
Thu, 4 Apr 2019 20:43:08 +0000 (23:43 +0300)
committerIvan Tashkinov <ivant.business@gmail.com>
Thu, 4 Apr 2019 20:43:08 +0000 (23:43 +0300)
# Conflicts:
# mix.exs

42 files changed:
AGPL-3 [moved from LICENSE with 100% similarity]
CC-BY-NC-ND-4.0 [new file with mode: 0644]
CC-BY-SA-4.0 [new file with mode: 0644]
COPYING [new file with mode: 0644]
config/config.exs
docs/api/differences_in_mastoapi_responses.md
docs/config.md
lib/mix/tasks/pleroma/instance.ex
lib/mix/tasks/pleroma/robots_txt.eex [new file with mode: 0644]
lib/mix/tasks/pleroma/user.ex
lib/pleroma/html.ex
lib/pleroma/object.ex
lib/pleroma/plugs/user_fetcher_plug.ex
lib/pleroma/user.ex
lib/pleroma/web/activity_pub/activity_pub.ex
lib/pleroma/web/activity_pub/utils.ex
lib/pleroma/web/common_api/common_api.ex
lib/pleroma/web/common_api/utils.ex
lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
lib/pleroma/web/twitter_api/controllers/util_controller.ex
lib/pleroma/web/twitter_api/twitter_api.ex
mix.exs
mix.lock
priv/repo/migrations/20190403131720_add_oauth_token_indexes.exs [new file with mode: 0644]
priv/static/images/pleroma-fox-tan-smol.png [new file with mode: 0644]
priv/static/images/pleroma-fox-tan.png [new file with mode: 0644]
priv/static/images/pleroma-tan.png [new file with mode: 0644]
test/fixtures/httpoison_mock/emelie.atom [new file with mode: 0644]
test/fixtures/httpoison_mock/status.emelie.json [new file with mode: 0644]
test/fixtures/httpoison_mock/webfinger_emelie.json [new file with mode: 0644]
test/support/http_request_mock.ex
test/tasks/user_test.exs
test/user_test.exs
test/web/activity_pub/activity_pub_test.exs
test/web/activity_pub/utils_test.exs
test/web/common_api/common_api_utils_test.exs
test/web/mastodon_api/mastodon_api_controller_test.exs
test/web/mastodon_api/status_view_test.exs
test/web/salmon/salmon_test.exs
test/web/twitter_api/twitter_api_controller_test.exs
test/web/twitter_api/twitter_api_test.exs
test/web/twitter_api/util_controller_test.exs

diff --git a/LICENSE b/AGPL-3
similarity index 100%
rename from LICENSE
rename to AGPL-3
diff --git a/CC-BY-NC-ND-4.0 b/CC-BY-NC-ND-4.0
new file mode 100644 (file)
index 0000000..4865442
--- /dev/null
@@ -0,0 +1,403 @@
+Attribution-NonCommercial-NoDerivatives 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+     Considerations for licensors: Our public licenses are
+     intended for use by those authorized to give the public
+     permission to use material in ways otherwise restricted by
+     copyright and certain other rights. Our licenses are
+     irrevocable. Licensors should read and understand the terms
+     and conditions of the license they choose before applying it.
+     Licensors should also secure all rights necessary before
+     applying our licenses so that the public can reuse the
+     material as expected. Licensors should clearly mark any
+     material not subject to the license. This includes other CC-
+     licensed material, or material used under an exception or
+     limitation to copyright. More considerations for licensors:
+       wiki.creativecommons.org/Considerations_for_licensors
+
+     Considerations for the public: By using one of our public
+     licenses, a licensor grants the public permission to use the
+     licensed material under specified terms and conditions. If
+     the licensor's permission is not necessary for any reason--for
+     example, because of any applicable exception or limitation to
+     copyright--then that use is not regulated by the license. Our
+     licenses grant only permissions under copyright and certain
+     other rights that a licensor has authority to grant. Use of
+     the licensed material may still be restricted for other
+     reasons, including because others have copyright or other
+     rights in the material. A licensor may make special requests,
+     such as asking that all changes be marked or described.
+     Although not required by our licenses, you are encouraged to
+     respect those requests where reasonable. More considerations
+     for the public: 
+       wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
+International Public License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-NonCommercial-NoDerivatives 4.0 International Public
+License ("Public License"). To the extent this Public License may be
+interpreted as a contract, You are granted the Licensed Rights in
+consideration of Your acceptance of these terms and conditions, and the
+Licensor grants You such rights in consideration of benefits the
+Licensor receives from making the Licensed Material available under
+these terms and conditions.
+
+
+Section 1 -- Definitions.
+
+  a. Adapted Material means material subject to Copyright and Similar
+     Rights that is derived from or based upon the Licensed Material
+     and in which the Licensed Material is translated, altered,
+     arranged, transformed, or otherwise modified in a manner requiring
+     permission under the Copyright and Similar Rights held by the
+     Licensor. For purposes of this Public License, where the Licensed
+     Material is a musical work, performance, or sound recording,
+     Adapted Material is always produced where the Licensed Material is
+     synched in timed relation with a moving image.
+
+  b. Copyright and Similar Rights means copyright and/or similar rights
+     closely related to copyright including, without limitation,
+     performance, broadcast, sound recording, and Sui Generis Database
+     Rights, without regard to how the rights are labeled or
+     categorized. For purposes of this Public License, the rights
+     specified in Section 2(b)(1)-(2) are not Copyright and Similar
+     Rights.
+
+  c. Effective Technological Measures means those measures that, in the
+     absence of proper authority, may not be circumvented under laws
+     fulfilling obligations under Article 11 of the WIPO Copyright
+     Treaty adopted on December 20, 1996, and/or similar international
+     agreements.
+
+  d. Exceptions and Limitations means fair use, fair dealing, and/or
+     any other exception or limitation to Copyright and Similar Rights
+     that applies to Your use of the Licensed Material.
+
+  e. Licensed Material means the artistic or literary work, database,
+     or other material to which the Licensor applied this Public
+     License.
+
+  f. Licensed Rights means the rights granted to You subject to the
+     terms and conditions of this Public License, which are limited to
+     all Copyright and Similar Rights that apply to Your use of the
+     Licensed Material and that the Licensor has authority to license.
+
+  g. Licensor means the individual(s) or entity(ies) granting rights
+     under this Public License.
+
+  h. NonCommercial means not primarily intended for or directed towards
+     commercial advantage or monetary compensation. For purposes of
+     this Public License, the exchange of the Licensed Material for
+     other material subject to Copyright and Similar Rights by digital
+     file-sharing or similar means is NonCommercial provided there is
+     no payment of monetary compensation in connection with the
+     exchange.
+
+  i. Share means to provide material to the public by any means or
+     process that requires permission under the Licensed Rights, such
+     as reproduction, public display, public performance, distribution,
+     dissemination, communication, or importation, and to make material
+     available to the public including in ways that members of the
+     public may access the material from a place and at a time
+     individually chosen by them.
+
+  j. Sui Generis Database Rights means rights other than copyright
+     resulting from Directive 96/9/EC of the European Parliament and of
+     the Council of 11 March 1996 on the legal protection of databases,
+     as amended and/or succeeded, as well as other essentially
+     equivalent rights anywhere in the world.
+
+  k. You means the individual or entity exercising the Licensed Rights
+     under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+  a. License grant.
+
+       1. Subject to the terms and conditions of this Public License,
+          the Licensor hereby grants You a worldwide, royalty-free,
+          non-sublicensable, non-exclusive, irrevocable license to
+          exercise the Licensed Rights in the Licensed Material to:
+
+            a. reproduce and Share the Licensed Material, in whole or
+               in part, for NonCommercial purposes only; and
+
+            b. produce and reproduce, but not Share, Adapted Material
+               for NonCommercial purposes only.
+
+       2. Exceptions and Limitations. For the avoidance of doubt, where
+          Exceptions and Limitations apply to Your use, this Public
+          License does not apply, and You do not need to comply with
+          its terms and conditions.
+
+       3. Term. The term of this Public License is specified in Section
+          6(a).
+
+       4. Media and formats; technical modifications allowed. The
+          Licensor authorizes You to exercise the Licensed Rights in
+          all media and formats whether now known or hereafter created,
+          and to make technical modifications necessary to do so. The
+          Licensor waives and/or agrees not to assert any right or
+          authority to forbid You from making technical modifications
+          necessary to exercise the Licensed Rights, including
+          technical modifications necessary to circumvent Effective
+          Technological Measures. For purposes of this Public License,
+          simply making modifications authorized by this Section 2(a)
+          (4) never produces Adapted Material.
+
+       5. Downstream recipients.
+
+            a. Offer from the Licensor -- Licensed Material. Every
+               recipient of the Licensed Material automatically
+               receives an offer from the Licensor to exercise the
+               Licensed Rights under the terms and conditions of this
+               Public License.
+
+            b. No downstream restrictions. You may not offer or impose
+               any additional or different terms or conditions on, or
+               apply any Effective Technological Measures to, the
+               Licensed Material if doing so restricts exercise of the
+               Licensed Rights by any recipient of the Licensed
+               Material.
+
+       6. No endorsement. Nothing in this Public License constitutes or
+          may be construed as permission to assert or imply that You
+          are, or that Your use of the Licensed Material is, connected
+          with, or sponsored, endorsed, or granted official status by,
+          the Licensor or others designated to receive attribution as
+          provided in Section 3(a)(1)(A)(i).
+
+  b. Other rights.
+
+       1. Moral rights, such as the right of integrity, are not
+          licensed under this Public License, nor are publicity,
+          privacy, and/or other similar personality rights; however, to
+          the extent possible, the Licensor waives and/or agrees not to
+          assert any such rights held by the Licensor to the limited
+          extent necessary to allow You to exercise the Licensed
+          Rights, but not otherwise.
+
+       2. Patent and trademark rights are not licensed under this
+          Public License.
+
+       3. To the extent possible, the Licensor waives any right to
+          collect royalties from You for the exercise of the Licensed
+          Rights, whether directly or through a collecting society
+          under any voluntary or waivable statutory or compulsory
+          licensing scheme. In all other cases the Licensor expressly
+          reserves any right to collect such royalties, including when
+          the Licensed Material is used other than for NonCommercial
+          purposes.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+  a. Attribution.
+
+       1. If You Share the Licensed Material, You must:
+
+            a. retain the following if it is supplied by the Licensor
+               with the Licensed Material:
+
+                 i. identification of the creator(s) of the Licensed
+                    Material and any others designated to receive
+                    attribution, in any reasonable manner requested by
+                    the Licensor (including by pseudonym if
+                    designated);
+
+                ii. a copyright notice;
+
+               iii. a notice that refers to this Public License;
+
+                iv. a notice that refers to the disclaimer of
+                    warranties;
+
+                 v. a URI or hyperlink to the Licensed Material to the
+                    extent reasonably practicable;
+
+            b. indicate if You modified the Licensed Material and
+               retain an indication of any previous modifications; and
+
+            c. indicate the Licensed Material is licensed under this
+               Public License, and include the text of, or the URI or
+               hyperlink to, this Public License.
+
+          For the avoidance of doubt, You do not have permission under
+          this Public License to Share Adapted Material.
+
+       2. You may satisfy the conditions in Section 3(a)(1) in any
+          reasonable manner based on the medium, means, and context in
+          which You Share the Licensed Material. For example, it may be
+          reasonable to satisfy the conditions by providing a URI or
+          hyperlink to a resource that includes the required
+          information.
+
+       3. If requested by the Licensor, You must remove any of the
+          information required by Section 3(a)(1)(A) to the extent
+          reasonably practicable.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+     to extract, reuse, reproduce, and Share all or a substantial
+     portion of the contents of the database for NonCommercial purposes
+     only and provided You do not Share Adapted Material;
+
+  b. if You include all or a substantial portion of the database
+     contents in a database in which You have Sui Generis Database
+     Rights, then the database in which You have Sui Generis Database
+     Rights (but not its individual contents) is Adapted Material; and
+
+  c. You must comply with the conditions in Section 3(a) if You Share
+     all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+  c. The disclaimer of warranties and limitation of liability provided
+     above shall be interpreted in a manner that, to the extent
+     possible, most closely approximates an absolute disclaimer and
+     waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+  a. This Public License applies for the term of the Copyright and
+     Similar Rights licensed here. However, if You fail to comply with
+     this Public License, then Your rights under this Public License
+     terminate automatically.
+
+  b. Where Your right to use the Licensed Material has terminated under
+     Section 6(a), it reinstates:
+
+       1. automatically as of the date the violation is cured, provided
+          it is cured within 30 days of Your discovery of the
+          violation; or
+
+       2. upon express reinstatement by the Licensor.
+
+     For the avoidance of doubt, this Section 6(b) does not affect any
+     right the Licensor may have to seek remedies for Your violations
+     of this Public License.
+
+  c. For the avoidance of doubt, the Licensor may also offer the
+     Licensed Material under separate terms or conditions or stop
+     distributing the Licensed Material at any time; however, doing so
+     will not terminate this Public License.
+
+  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+     License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+  a. The Licensor shall not be bound by any additional or different
+     terms or conditions communicated by You unless expressly agreed.
+
+  b. Any arrangements, understandings, or agreements regarding the
+     Licensed Material not stated herein are separate from and
+     independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+  a. For the avoidance of doubt, this Public License does not, and
+     shall not be interpreted to, reduce, limit, restrict, or impose
+     conditions on any use of the Licensed Material that could lawfully
+     be made without permission under this Public License.
+
+  b. To the extent possible, if any provision of this Public License is
+     deemed unenforceable, it shall be automatically reformed to the
+     minimum extent necessary to make it enforceable. If the provision
+     cannot be reformed, it shall be severed from this Public License
+     without affecting the enforceability of the remaining terms and
+     conditions.
+
+  c. No term or condition of this Public License will be waived and no
+     failure to comply consented to unless expressly agreed to by the
+     Licensor.
+
+  d. Nothing in this Public License constitutes or may be interpreted
+     as a limitation upon, or waiver of, any privileges and immunities
+     that apply to the Licensor or You, including from the legal
+     processes of any jurisdiction or authority.
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
+
diff --git a/CC-BY-SA-4.0 b/CC-BY-SA-4.0
new file mode 100644 (file)
index 0000000..4681ab8
--- /dev/null
@@ -0,0 +1,427 @@
+Attribution-ShareAlike 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+     Considerations for licensors: Our public licenses are
+     intended for use by those authorized to give the public
+     permission to use material in ways otherwise restricted by
+     copyright and certain other rights. Our licenses are
+     irrevocable. Licensors should read and understand the terms
+     and conditions of the license they choose before applying it.
+     Licensors should also secure all rights necessary before
+     applying our licenses so that the public can reuse the
+     material as expected. Licensors should clearly mark any
+     material not subject to the license. This includes other CC-
+     licensed material, or material used under an exception or
+     limitation to copyright. More considerations for licensors:
+       wiki.creativecommons.org/Considerations_for_licensors
+
+     Considerations for the public: By using one of our public
+     licenses, a licensor grants the public permission to use the
+     licensed material under specified terms and conditions. If
+     the licensor's permission is not necessary for any reason--for
+     example, because of any applicable exception or limitation to
+     copyright--then that use is not regulated by the license. Our
+     licenses grant only permissions under copyright and certain
+     other rights that a licensor has authority to grant. Use of
+     the licensed material may still be restricted for other
+     reasons, including because others have copyright or other
+     rights in the material. A licensor may make special requests,
+     such as asking that all changes be marked or described.
+     Although not required by our licenses, you are encouraged to
+     respect those requests where reasonable. More considerations
+     for the public:
+       wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-ShareAlike 4.0 International Public
+License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-ShareAlike 4.0 International Public License ("Public
+License"). To the extent this Public License may be interpreted as a
+contract, You are granted the Licensed Rights in consideration of Your
+acceptance of these terms and conditions, and the Licensor grants You
+such rights in consideration of benefits the Licensor receives from
+making the Licensed Material available under these terms and
+conditions.
+
+
+Section 1 -- Definitions.
+
+  a. Adapted Material means material subject to Copyright and Similar
+     Rights that is derived from or based upon the Licensed Material
+     and in which the Licensed Material is translated, altered,
+     arranged, transformed, or otherwise modified in a manner requiring
+     permission under the Copyright and Similar Rights held by the
+     Licensor. For purposes of this Public License, where the Licensed
+     Material is a musical work, performance, or sound recording,
+     Adapted Material is always produced where the Licensed Material is
+     synched in timed relation with a moving image.
+
+  b. Adapter's License means the license You apply to Your Copyright
+     and Similar Rights in Your contributions to Adapted Material in
+     accordance with the terms and conditions of this Public License.
+
+  c. BY-SA Compatible License means a license listed at
+     creativecommons.org/compatiblelicenses, approved by Creative
+     Commons as essentially the equivalent of this Public License.
+
+  d. Copyright and Similar Rights means copyright and/or similar rights
+     closely related to copyright including, without limitation,
+     performance, broadcast, sound recording, and Sui Generis Database
+     Rights, without regard to how the rights are labeled or
+     categorized. For purposes of this Public License, the rights
+     specified in Section 2(b)(1)-(2) are not Copyright and Similar
+     Rights.
+
+  e. Effective Technological Measures means those measures that, in the
+     absence of proper authority, may not be circumvented under laws
+     fulfilling obligations under Article 11 of the WIPO Copyright
+     Treaty adopted on December 20, 1996, and/or similar international
+     agreements.
+
+  f. Exceptions and Limitations means fair use, fair dealing, and/or
+     any other exception or limitation to Copyright and Similar Rights
+     that applies to Your use of the Licensed Material.
+
+  g. License Elements means the license attributes listed in the name
+     of a Creative Commons Public License. The License Elements of this
+     Public License are Attribution and ShareAlike.
+
+  h. Licensed Material means the artistic or literary work, database,
+     or other material to which the Licensor applied this Public
+     License.
+
+  i. Licensed Rights means the rights granted to You subject to the
+     terms and conditions of this Public License, which are limited to
+     all Copyright and Similar Rights that apply to Your use of the
+     Licensed Material and that the Licensor has authority to license.
+
+  j. Licensor means the individual(s) or entity(ies) granting rights
+     under this Public License.
+
+  k. Share means to provide material to the public by any means or
+     process that requires permission under the Licensed Rights, such
+     as reproduction, public display, public performance, distribution,
+     dissemination, communication, or importation, and to make material
+     available to the public including in ways that members of the
+     public may access the material from a place and at a time
+     individually chosen by them.
+
+  l. Sui Generis Database Rights means rights other than copyright
+     resulting from Directive 96/9/EC of the European Parliament and of
+     the Council of 11 March 1996 on the legal protection of databases,
+     as amended and/or succeeded, as well as other essentially
+     equivalent rights anywhere in the world.
+
+  m. You means the individual or entity exercising the Licensed Rights
+     under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+  a. License grant.
+
+       1. Subject to the terms and conditions of this Public License,
+          the Licensor hereby grants You a worldwide, royalty-free,
+          non-sublicensable, non-exclusive, irrevocable license to
+          exercise the Licensed Rights in the Licensed Material to:
+
+            a. reproduce and Share the Licensed Material, in whole or
+               in part; and
+
+            b. produce, reproduce, and Share Adapted Material.
+
+       2. Exceptions and Limitations. For the avoidance of doubt, where
+          Exceptions and Limitations apply to Your use, this Public
+          License does not apply, and You do not need to comply with
+          its terms and conditions.
+
+       3. Term. The term of this Public License is specified in Section
+          6(a).
+
+       4. Media and formats; technical modifications allowed. The
+          Licensor authorizes You to exercise the Licensed Rights in
+          all media and formats whether now known or hereafter created,
+          and to make technical modifications necessary to do so. The
+          Licensor waives and/or agrees not to assert any right or
+          authority to forbid You from making technical modifications
+          necessary to exercise the Licensed Rights, including
+          technical modifications necessary to circumvent Effective
+          Technological Measures. For purposes of this Public License,
+          simply making modifications authorized by this Section 2(a)
+          (4) never produces Adapted Material.
+
+       5. Downstream recipients.
+
+            a. Offer from the Licensor -- Licensed Material. Every
+               recipient of the Licensed Material automatically
+               receives an offer from the Licensor to exercise the
+               Licensed Rights under the terms and conditions of this
+               Public License.
+
+            b. Additional offer from the Licensor -- Adapted Material.
+               Every recipient of Adapted Material from You
+               automatically receives an offer from the Licensor to
+               exercise the Licensed Rights in the Adapted Material
+               under the conditions of the Adapter's License You apply.
+
+            c. No downstream restrictions. You may not offer or impose
+               any additional or different terms or conditions on, or
+               apply any Effective Technological Measures to, the
+               Licensed Material if doing so restricts exercise of the
+               Licensed Rights by any recipient of the Licensed
+               Material.
+
+       6. No endorsement. Nothing in this Public License constitutes or
+          may be construed as permission to assert or imply that You
+          are, or that Your use of the Licensed Material is, connected
+          with, or sponsored, endorsed, or granted official status by,
+          the Licensor or others designated to receive attribution as
+          provided in Section 3(a)(1)(A)(i).
+
+  b. Other rights.
+
+       1. Moral rights, such as the right of integrity, are not
+          licensed under this Public License, nor are publicity,
+          privacy, and/or other similar personality rights; however, to
+          the extent possible, the Licensor waives and/or agrees not to
+          assert any such rights held by the Licensor to the limited
+          extent necessary to allow You to exercise the Licensed
+          Rights, but not otherwise.
+
+       2. Patent and trademark rights are not licensed under this
+          Public License.
+
+       3. To the extent possible, the Licensor waives any right to
+          collect royalties from You for the exercise of the Licensed
+          Rights, whether directly or through a collecting society
+          under any voluntary or waivable statutory or compulsory
+          licensing scheme. In all other cases the Licensor expressly
+          reserves any right to collect such royalties.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+  a. Attribution.
+
+       1. If You Share the Licensed Material (including in modified
+          form), You must:
+
+            a. retain the following if it is supplied by the Licensor
+               with the Licensed Material:
+
+                 i. identification of the creator(s) of the Licensed
+                    Material and any others designated to receive
+                    attribution, in any reasonable manner requested by
+                    the Licensor (including by pseudonym if
+                    designated);
+
+                ii. a copyright notice;
+
+               iii. a notice that refers to this Public License;
+
+                iv. a notice that refers to the disclaimer of
+                    warranties;
+
+                 v. a URI or hyperlink to the Licensed Material to the
+                    extent reasonably practicable;
+
+            b. indicate if You modified the Licensed Material and
+               retain an indication of any previous modifications; and
+
+            c. indicate the Licensed Material is licensed under this
+               Public License, and include the text of, or the URI or
+               hyperlink to, this Public License.
+
+       2. You may satisfy the conditions in Section 3(a)(1) in any
+          reasonable manner based on the medium, means, and context in
+          which You Share the Licensed Material. For example, it may be
+          reasonable to satisfy the conditions by providing a URI or
+          hyperlink to a resource that includes the required
+          information.
+
+       3. If requested by the Licensor, You must remove any of the
+          information required by Section 3(a)(1)(A) to the extent
+          reasonably practicable.
+
+  b. ShareAlike.
+
+     In addition to the conditions in Section 3(a), if You Share
+     Adapted Material You produce, the following conditions also apply.
+
+       1. The Adapter's License You apply must be a Creative Commons
+          license with the same License Elements, this version or
+          later, or a BY-SA Compatible License.
+
+       2. You must include the text of, or the URI or hyperlink to, the
+          Adapter's License You apply. You may satisfy this condition
+          in any reasonable manner based on the medium, means, and
+          context in which You Share Adapted Material.
+
+       3. You may not offer or impose any additional or different terms
+          or conditions on, or apply any Effective Technological
+          Measures to, Adapted Material that restrict exercise of the
+          rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+     to extract, reuse, reproduce, and Share all or a substantial
+     portion of the contents of the database;
+
+  b. if You include all or a substantial portion of the database
+     contents in a database in which You have Sui Generis Database
+     Rights, then the database in which You have Sui Generis Database
+     Rights (but not its individual contents) is Adapted Material,
+     including for purposes of Section 3(b); and
+
+  c. You must comply with the conditions in Section 3(a) if You Share
+     all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+  c. The disclaimer of warranties and limitation of liability provided
+     above shall be interpreted in a manner that, to the extent
+     possible, most closely approximates an absolute disclaimer and
+     waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+  a. This Public License applies for the term of the Copyright and
+     Similar Rights licensed here. However, if You fail to comply with
+     this Public License, then Your rights under this Public License
+     terminate automatically.
+
+  b. Where Your right to use the Licensed Material has terminated under
+     Section 6(a), it reinstates:
+
+       1. automatically as of the date the violation is cured, provided
+          it is cured within 30 days of Your discovery of the
+          violation; or
+
+       2. upon express reinstatement by the Licensor.
+
+     For the avoidance of doubt, this Section 6(b) does not affect any
+     right the Licensor may have to seek remedies for Your violations
+     of this Public License.
+
+  c. For the avoidance of doubt, the Licensor may also offer the
+     Licensed Material under separate terms or conditions or stop
+     distributing the Licensed Material at any time; however, doing so
+     will not terminate this Public License.
+
+  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+     License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+  a. The Licensor shall not be bound by any additional or different
+     terms or conditions communicated by You unless expressly agreed.
+
+  b. Any arrangements, understandings, or agreements regarding the
+     Licensed Material not stated herein are separate from and
+     independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+  a. For the avoidance of doubt, this Public License does not, and
+     shall not be interpreted to, reduce, limit, restrict, or impose
+     conditions on any use of the Licensed Material that could lawfully
+     be made without permission under this Public License.
+
+  b. To the extent possible, if any provision of this Public License is
+     deemed unenforceable, it shall be automatically reformed to the
+     minimum extent necessary to make it enforceable. If the provision
+     cannot be reformed, it shall be severed from this Public License
+     without affecting the enforceability of the remaining terms and
+     conditions.
+
+  c. No term or condition of this Public License will be waived and no
+     failure to comply consented to unless expressly agreed to by the
+     Licensor.
+
+  d. Nothing in this Public License constitutes or may be interpreted
+     as a limitation upon, or waiver of, any privileges and immunities
+     that apply to the Licensor or You, including from the legal
+     processes of any jurisdiction or authority.
+
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..ceec519
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,48 @@
+Unless otherwise stated this repository is copyright © 2017-2019
+Pleroma Authors <https://pleroma.social/>, and is distributed under
+The GNU Affero General Public License Version 3, you should have received a
+copy of the license file as AGPL-3.
+
+---
+
+The following files are copyright © 2019 shitposter.club, and are distributed
+under the Creative Commons Attribution-ShareAlike 4.0 International license,
+you should have received a copy of the license file as CC-BY-SA-4.0.
+
+priv/static/images/pleroma-fox-tan.png
+priv/static/images/pleroma-fox-tan-smol.png
+priv/static/images/pleroma-tan.png
+
+---
+
+The following files are copyright © 2017-2019 Pleroma Authors
+<https://pleroma.social/>, and are distributed under the Creative Commons
+Attribution-ShareAlike 4.0 International license, you should have received
+a copy of the license file as CC-BY-SA-4.0.
+
+priv/static/images/avi.png
+priv/static/images/banner.png
+priv/static/instance/thumbnail.jpeg
+
+---
+
+All photos published on Unsplash can be used for free. You can use them for
+commercial and noncommercial purposes. You do not need to ask permission from
+or provide credit to the photographer or Unsplash, although it is appreciated
+when possible.
+
+More precisely, Unsplash grants you an irrevocable, nonexclusive, worldwide
+copyright license to download, copy, modify, distribute, perform, and use
+photos from Unsplash for free, including for commercial purposes, without
+permission from or attributing the photographer or Unsplash. This license
+does not include the right to compile photos from Unsplash to replicate
+a similar or competing service.
+
+priv/static/images/city.jpg
+
+---
+
+The files present under the priv/static/finmoji directory are copyright
+Finland <https://finland.fi/emoji/>, and are distributed under the Creative
+Commons Attribution-NonCommercial-NoDerivatives 4.0 International license, you
+should have received a copy of the license file as CC-BY-NC-ND-4.0.
index 604290544485a6f349471a94f4c8e98a9b75a819..9bc79f939cf49fd6e5e1a392f654d16efe469ea1 100644 (file)
@@ -118,6 +118,11 @@ config :logger, :ex_syslogger,
   format: "$metadata[$level] $message",
   metadata: [:request_id]
 
+config :quack,
+  level: :warn,
+  meta: [:all],
+  webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
+
 config :mime, :types, %{
   "application/xml" => ["xml"],
   "application/xrd+xml" => ["xrd+xml"],
index d993d13830f508c182792cf245c3cfcb27ab5743..215f43155298abb76e8a2a42aa57749a835c4ab1 100644 (file)
@@ -44,3 +44,9 @@ Has these additional fields under the `pleroma` object:
 Has these additional fields under the `pleroma` object:
 
 - `is_seen`: true if the notification was read by the user
+
+## POST `/api/v1/statuses`
+
+Additional parameters can be added to the JSON body/Form data:
+
+- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
index 97a0e6ffa57087317409d606ddc687d070e2165b..06d6fd757de0bb704a9bec6d06c7c0b566874b43 100644 (file)
@@ -105,7 +105,7 @@ config :pleroma, Pleroma.Mailer,
 * `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
 
 ## :logger
-* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog
+* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog, and `Quack.Logger` to log to Slack
 
 An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed:
 ```
@@ -128,6 +128,24 @@ config :logger, :ex_syslogger,
 
 See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_syslogger’s documentation](https://hexdocs.pm/ex_syslogger/)
 
+An example of logging info to local syslog, but warn to a Slack channel:
+```
+config :logger,
+  backends: [ {ExSyslogger, :ex_syslogger}, Quack.Logger ],
+  level: :info
+
+config :logger, :ex_syslogger,
+  level: :info,
+  ident: "pleroma",
+  format: "$metadata[$level] $message"
+
+config :quack,
+  level: :warn,
+  meta: [:all],
+  webhook_url: "https://hooks.slack.com/services/YOUR-API-KEY-HERE"
+```
+
+See the [Quack Github](https://github.com/azohra/quack) for more details
 
 ## :frontend_configurations
 
index 1ba452275e376396ad0f15d9b6db05e2320c34c3..8f8d86a1120c5b402ea31b72d0f0dfb0e5291bcb 100644 (file)
@@ -81,6 +81,14 @@ defmodule Mix.Tasks.Pleroma.Instance do
 
       email = Common.get_option(options, :admin_email, "What is your admin email address?")
 
+      indexable =
+        Common.get_option(
+          options,
+          :indexable,
+          "Do you want search engines to index your site? (y/n)",
+          "y"
+        ) === "y"
+
       dbhost =
         Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
 
@@ -142,6 +150,8 @@ defmodule Mix.Tasks.Pleroma.Instance do
       Mix.shell().info("Writing #{psql_path}.")
       File.write(psql_path, result_psql)
 
+      write_robots_txt(indexable)
+
       Mix.shell().info(
         "\n" <>
           """
@@ -163,4 +173,28 @@ defmodule Mix.Tasks.Pleroma.Instance do
       )
     end
   end
+
+  defp write_robots_txt(indexable) do
+    robots_txt =
+      EEx.eval_file(
+        Path.expand("robots_txt.eex", __DIR__),
+        indexable: indexable
+      )
+
+    static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")
+
+    unless File.exists?(static_dir) do
+      File.mkdir_p!(static_dir)
+    end
+
+    robots_txt_path = Path.join(static_dir, "robots.txt")
+
+    if File.exists?(robots_txt_path) do
+      File.cp!(robots_txt_path, "#{robots_txt_path}.bak")
+      Mix.shell().info("Backing up existing robots.txt to #{robots_txt_path}.bak")
+    end
+
+    File.write(robots_txt_path, robots_txt)
+    Mix.shell().info("Writing #{robots_txt_path}.")
+  end
 end
diff --git a/lib/mix/tasks/pleroma/robots_txt.eex b/lib/mix/tasks/pleroma/robots_txt.eex
new file mode 100644 (file)
index 0000000..1af3c47
--- /dev/null
@@ -0,0 +1,2 @@
+User-Agent: *
+Disallow: <%= if indexable, do: "", else: "/" %>
index 2487b4ab54d7ec0d648a4edbaed401ee21dfa382..0d0bea8c08a7333aad1edff65ef3a7c118df4a33 100644 (file)
@@ -32,6 +32,10 @@ defmodule Mix.Tasks.Pleroma.User do
 
       mix pleroma.user rm NICKNAME
 
+  ## Delete the user's activities.
+
+      mix pleroma.user delete_activities NICKNAME
+
   ## Deactivate or activate the user's account.
 
       mix pleroma.user toggle_activated NICKNAME
@@ -303,6 +307,18 @@ defmodule Mix.Tasks.Pleroma.User do
     end
   end
 
+  def run(["delete_activities", nickname]) do
+    Common.start_pleroma()
+
+    with %User{local: true} = user <- User.get_by_nickname(nickname) do
+      User.delete_user_activities(user)
+      Mix.shell().info("User #{nickname} statuses deleted.")
+    else
+      _ ->
+        Mix.shell().error("No local user #{nickname}")
+    end
+  end
+
   defp set_moderator(user, value) do
     info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value})
 
index 5b152d926ed57d66b4d4acde5248955f6891d4dc..1e48749a854b3795d2e7c2aba32b6fe790d6dd47 100644 (file)
@@ -28,9 +28,13 @@ defmodule Pleroma.HTML do
   def filter_tags(html), do: filter_tags(html, nil)
   def strip_tags(html), do: Scrubber.scrub(html, Scrubber.StripTags)
 
+  # TODO: rename object to activity because that's what it is really working with
   def get_cached_scrubbed_html_for_object(content, scrubbers, object, module) do
     key = "#{module}#{generate_scrubber_signature(scrubbers)}|#{object.id}"
-    Cachex.fetch!(:scrubber_cache, key, fn _key -> ensure_scrubbed_html(content, scrubbers) end)
+
+    Cachex.fetch!(:scrubber_cache, key, fn _key ->
+      ensure_scrubbed_html(content, scrubbers, object.data["object"]["fake"] || false)
+    end)
   end
 
   def get_cached_stripped_html_for_object(content, object, module) do
@@ -44,11 +48,20 @@ defmodule Pleroma.HTML do
 
   def ensure_scrubbed_html(
         content,
-        scrubbers
+        scrubbers,
+        false = _fake
       ) do
     {:commit, filter_tags(content, scrubbers)}
   end
 
+  def ensure_scrubbed_html(
+        content,
+        scrubbers,
+        true = _fake
+      ) do
+    {:ignore, filter_tags(content, scrubbers)}
+  end
+
   defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
     generate_scrubber_signature([scrubber])
   end
index 8a670645d424aa3b9ef0688935b2101ec92527e8..013d6215710ec42858ab18ed3be69e6775e405ed 100644 (file)
@@ -44,6 +44,11 @@ defmodule Pleroma.Object do
   # Use this whenever possible, especially when walking graphs in an O(N) loop!
   def normalize(%Activity{object: %Object{} = object}), do: object
 
+  # A hack for fake activities
+  def normalize(%Activity{data: %{"object" => %{"fake" => true} = data}}) do
+    %Object{id: "pleroma:fake_object_id", data: data}
+  end
+
   # Catch and log Object.normalize() calls where the Activity's child object is not
   # preloaded.
   def normalize(%Activity{data: %{"object" => %{"id" => ap_id}}}) do
index 5a77f683361321be000612417268d212f99ac0e6..4089aa958865d2b0dea038fb0f175dadeb64c064 100644 (file)
@@ -3,9 +3,7 @@
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Plugs.UserFetcherPlug do
-  alias Pleroma.Repo
   alias Pleroma.User
-
   import Plug.Conn
 
   def init(options) do
@@ -14,26 +12,10 @@ defmodule Pleroma.Plugs.UserFetcherPlug do
 
   def call(conn, _options) do
     with %{auth_credentials: %{username: username}} <- conn.assigns,
-         {:ok, %User{} = user} <- user_fetcher(username) do
-      conn
-      |> assign(:auth_user, user)
+         %User{} = user <- User.get_by_nickname_or_email(username) do
+      assign(conn, :auth_user, user)
     else
       _ -> conn
     end
   end
-
-  defp user_fetcher(username_or_email) do
-    {
-      :ok,
-      cond do
-        # First, try logging in as if it was a name
-        user = Repo.get_by(User, %{nickname: username_or_email}) ->
-          user
-
-        # If we get nil, we try using it as an email
-        user = Repo.get_by(User, %{email: username_or_email}) ->
-          user
-      end
-    }
-  end
 end
index cd1815b85fd1d8bdf96862ca10b49942d75315f7..05f56c01e339677748f6685d6db10e68bab7b9b7 100644 (file)
@@ -1096,28 +1096,27 @@ defmodule Pleroma.User do
     # Remove all relationships
     {:ok, followers} = User.get_followers(user)
 
-    followers
-    |> Enum.each(fn follower -> User.unfollow(follower, user) end)
+    Enum.each(followers, fn follower -> User.unfollow(follower, user) end)
 
     {:ok, friends} = User.get_friends(user)
 
-    friends
-    |> Enum.each(fn followed -> User.unfollow(user, followed) end)
+    Enum.each(friends, fn followed -> User.unfollow(user, followed) end)
 
-    query =
-      from(a in Activity, where: a.actor == ^user.ap_id)
-      |> Activity.with_preloaded_object()
+    delete_user_activities(user)
+  end
 
-    Repo.all(query)
-    |> Enum.each(fn activity ->
-      case activity.data["type"] do
-        "Create" ->
-          ActivityPub.delete(Object.normalize(activity))
+  def delete_user_activities(%User{ap_id: ap_id} = user) do
+    Activity
+    |> where(actor: ^ap_id)
+    |> Activity.with_preloaded_object()
+    |> Repo.all()
+    |> Enum.each(fn
+      %{data: %{"type" => "Create"}} = activity ->
+        activity |> Object.normalize() |> ActivityPub.delete()
 
-        # TODO: Do something with likes, follows, repeats.
-        _ ->
-          "Doing nothing"
-      end
+      # TODO: Do something with likes, follows, repeats.
+      _ ->
+        "Doing nothing"
     end)
 
     {:ok, user}
index 6e1ed7ec9dc51e45c5abdc0bfcf946d6fc6ea342..f217e7bac35c9271f633c3765ddb9477769d1e31 100644 (file)
@@ -113,15 +113,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
 
   def decrease_replies_count_if_reply(_object), do: :noop
 
-  def insert(map, local \\ true) when is_map(map) do
+  def insert(map, local \\ true, fake \\ false) when is_map(map) do
     with nil <- Activity.normalize(map),
-         map <- lazy_put_activity_defaults(map),
+         map <- lazy_put_activity_defaults(map, fake),
          :ok <- check_actor_is_active(map["actor"]),
          {_, true} <- {:remote_limit_error, check_remote_limit(map)},
          {:ok, map} <- MRF.filter(map),
+         {recipients, _, _} = get_recipients(map),
+         {:fake, false, map, recipients} <- {:fake, fake, map, recipients},
          {:ok, object} <- insert_full_object(map) do
-      {recipients, _, _} = get_recipients(map)
-
       {:ok, activity} =
         Repo.insert(%Activity{
           data: map,
@@ -146,8 +146,23 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
       stream_out(activity)
       {:ok, activity}
     else
-      %Activity{} = activity -> {:ok, activity}
-      error -> {:error, error}
+      %Activity{} = activity ->
+        {:ok, activity}
+
+      {:fake, true, map, recipients} ->
+        activity = %Activity{
+          data: map,
+          local: local,
+          actor: map["actor"],
+          recipients: recipients,
+          id: "pleroma:fakeid"
+        }
+
+        Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
+        {:ok, activity}
+
+      error ->
+        {:error, error}
     end
   end
 
@@ -190,7 +205,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
     end
   end
 
-  def create(%{to: to, actor: actor, context: context, object: object} = params) do
+  def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
     additional = params[:additional] || %{}
     # only accept false as false value
     local = !(params[:local] == false)
@@ -201,13 +216,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
              %{to: to, actor: actor, published: published, context: context, object: object},
              additional
            ),
-         {:ok, activity} <- insert(create_data, local),
+         {:ok, activity} <- insert(create_data, local, fake),
+         {:fake, false, activity} <- {:fake, fake, activity},
          _ <- increase_replies_count_if_reply(create_data),
          # Changing note count prior to enqueuing federation task in order to avoid
          # race conditions on updating user.info
          {:ok, _actor} <- increase_note_count_if_public(actor, activity),
          :ok <- maybe_federate(activity) do
       {:ok, activity}
+    else
+      {:fake, true, activity} ->
+        {:ok, activity}
     end
   end
 
index 77841278a3a63489b4ff3ebb17461343895108d3..32545937ee84a91a49abd1e1a32914c0a1b737d3 100644 (file)
@@ -175,18 +175,26 @@ defmodule Pleroma.Web.ActivityPub.Utils do
   Adds an id and a published data if they aren't there,
   also adds it to an included object
   """
-  def lazy_put_activity_defaults(map) do
-    %{data: %{"id" => context}, id: context_id} = create_context(map["context"])
-
+  def lazy_put_activity_defaults(map, fake \\ false) do
     map =
-      map
-      |> Map.put_new_lazy("id", &generate_activity_id/0)
-      |> Map.put_new_lazy("published", &make_date/0)
-      |> Map.put_new("context", context)
-      |> Map.put_new("context_id", context_id)
+      unless fake do
+        %{data: %{"id" => context}, id: context_id} = create_context(map["context"])
+
+        map
+        |> Map.put_new_lazy("id", &generate_activity_id/0)
+        |> Map.put_new_lazy("published", &make_date/0)
+        |> Map.put_new("context", context)
+        |> Map.put_new("context_id", context_id)
+      else
+        map
+        |> Map.put_new("id", "pleroma:fakeid")
+        |> Map.put_new_lazy("published", &make_date/0)
+        |> Map.put_new("context", "pleroma:fakecontext")
+        |> Map.put_new("context_id", -1)
+      end
 
     if is_map(map["object"]) do
-      object = lazy_put_object_defaults(map["object"], map)
+      object = lazy_put_object_defaults(map["object"], map, fake)
       %{map | "object" => object}
     else
       map
@@ -196,7 +204,18 @@ defmodule Pleroma.Web.ActivityPub.Utils do
   @doc """
   Adds an id and published date if they aren't there.
   """
-  def lazy_put_object_defaults(map, activity \\ %{}) do
+  def lazy_put_object_defaults(map, activity \\ %{}, fake)
+
+  def lazy_put_object_defaults(map, activity, true = _fake) do
+    map
+    |> Map.put_new_lazy("published", &make_date/0)
+    |> Map.put_new("id", "pleroma:fake_object_id")
+    |> Map.put_new("context", activity["context"])
+    |> Map.put_new("fake", true)
+    |> Map.put_new("context_id", activity["context_id"])
+  end
+
+  def lazy_put_object_defaults(map, activity, _fake) do
     map
     |> Map.put_new_lazy("id", &generate_object_id/0)
     |> Map.put_new_lazy("published", &make_date/0)
@@ -404,13 +423,15 @@ defmodule Pleroma.Web.ActivityPub.Utils do
             activity.data
           ),
         where: activity.actor == ^follower_id,
+        # this is to use the index
         where:
           fragment(
-            "? @> ?",
+            "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
             activity.data,
-            ^%{object: followed_id}
+            activity.data,
+            ^followed_id
           ),
-        order_by: [desc: :id],
+        order_by: [fragment("? desc nulls last", activity.id)],
         limit: 1
       )
 
@@ -567,13 +588,15 @@ defmodule Pleroma.Web.ActivityPub.Utils do
             activity.data
           ),
         where: activity.actor == ^blocker_id,
+        # this is to use the index
         where:
           fragment(
-            "? @> ?",
+            "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
+            activity.data,
             activity.data,
-            ^%{object: blocked_id}
+            ^blocked_id
           ),
-        order_by: [desc: :id],
+        order_by: [fragment("? desc nulls last", activity.id)],
         limit: 1
       )
 
index 25b99067775770d42fdd5c068180e8a526f4e6c4..745d1839bf2476b40ad27d4a74d4b4eb3a014a6e 100644 (file)
@@ -172,13 +172,16 @@ defmodule Pleroma.Web.CommonAPI do
              end)
            ) do
       res =
-        ActivityPub.create(%{
-          to: to,
-          actor: user,
-          context: context,
-          object: object,
-          additional: %{"cc" => cc, "directMessage" => visibility == "direct"}
-        })
+        ActivityPub.create(
+          %{
+            to: to,
+            actor: user,
+            context: context,
+            object: object,
+            additional: %{"cc" => cc, "directMessage" => visibility == "direct"}
+          },
+          Pleroma.Web.ControllerHelper.truthy_param?(data["preview"]) || false
+        )
 
       res
     end
index 40cea30901199732e994d44bec89f97dba601681..9cd8b3758edd66c74abdfeee4d517e45caa6cdf6 100644 (file)
@@ -15,6 +15,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
   alias Pleroma.Web.Endpoint
   alias Pleroma.Web.MediaProxy
 
+  require Logger
+
   # This is a hack for twidere.
   def get_by_id_or_ap_id(id) do
     activity =
@@ -240,15 +242,21 @@ defmodule Pleroma.Web.CommonAPI.Utils do
     Strftime.strftime!(date, "%a %b %d %H:%M:%S %z %Y")
   end
 
-  def date_to_asctime(date) do
-    with {:ok, date, _offset} <- date |> DateTime.from_iso8601() do
+  def date_to_asctime(date) when is_binary(date) do
+    with {:ok, date, _offset} <- DateTime.from_iso8601(date) do
       format_asctime(date)
     else
       _e ->
+        Logger.warn("Date #{date} in wrong format, must be ISO 8601")
         ""
     end
   end
 
+  def date_to_asctime(date) do
+    Logger.warn("Date #{date} in wrong format, must be ISO 8601")
+    ""
+  end
+
   def to_masto_date(%NaiveDateTime{} = date) do
     date
     |> NaiveDateTime.to_iso8601()
index 0141186d8920db7d8820f4dfed30b8f94cf50ba1..89fd7629a87e45df99352e2c2fe0196482baa110 100644 (file)
@@ -755,7 +755,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
   end
 
   def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do
-    with %User{} = followed <- Repo.get_by(User, nickname: uri),
+    with %User{} = followed <- User.get_by_nickname(uri),
          {:ok, follower, followed, _} <- CommonAPI.follow(follower, followed) do
       conn
       |> put_view(AccountView)
@@ -1121,7 +1121,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
             auto_play_gif: false,
             display_sensitive_media: false,
             reduce_motion: false,
-            max_toot_chars: limit
+            max_toot_chars: limit,
+            mascot: "/images/pleroma-fox-tan-smol.png"
           },
           rights: %{
             delete_others_notice: present?(user.info.is_moderator),
index e817f0d7919445f0d5adfb3ea793ba10a253942b..3cdd7a2f254693af44db3da09864b43cf292a831 100644 (file)
@@ -8,6 +8,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
   require Logger
 
   alias Comeonin.Pbkdf2
+  alias Pleroma.Activity
   alias Pleroma.Emoji
   alias Pleroma.Notification
   alias Pleroma.PasswordResetToken
@@ -73,23 +74,39 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
   end
 
   def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do
-    {err, followee} = OStatus.find_or_make_user(acct)
-    avatar = User.avatar_url(followee)
-    name = followee.nickname
-    id = followee.id
-
-    if !!user do
-      conn
-      |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
+    if is_status?(acct) do
+      {:ok, object} = ActivityPub.fetch_object_from_id(acct)
+      %Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])
+      redirect(conn, to: "/notice/#{activity_id}")
     else
-      conn
-      |> render("follow_login.html", %{
-        error: false,
-        acct: acct,
-        avatar: avatar,
-        name: name,
-        id: id
-      })
+      {err, followee} = OStatus.find_or_make_user(acct)
+      avatar = User.avatar_url(followee)
+      name = followee.nickname
+      id = followee.id
+
+      if !!user do
+        conn
+        |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
+      else
+        conn
+        |> render("follow_login.html", %{
+          error: false,
+          acct: acct,
+          avatar: avatar,
+          name: name,
+          id: id
+        })
+      end
+    end
+  end
+
+  defp is_status?(acct) do
+    case ActivityPub.fetch_and_contain_remote_object_from_id(acct) do
+      {:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] ->
+        true
+
+      _ ->
+        false
     end
   end
 
index d0e58e71b67c83a624dc6264f861645762cc083a..9b081a3167141ce4d2fcdc780df88aa55df21c46 100644 (file)
@@ -227,12 +227,9 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
         end
 
       %{"screen_name" => nickname} ->
-        case target = Repo.get_by(User, nickname: nickname) do
-          nil ->
-            {:error, "No user with such screen_name"}
-
-          _ ->
-            {:ok, target}
+        case User.get_by_nickname(nickname) do
+          nil -> {:error, "No user with such screen_name"}
+          target -> {:ok, target}
         end
 
       _ ->
diff --git a/mix.exs b/mix.exs
index 2b0d25b551ad00b0c30f438ac0defa1cec6a5aec..30931821f13d09404fd07239bb8ce5da381a9b13 100644 (file)
--- a/mix.exs
+++ b/mix.exs
@@ -41,7 +41,7 @@ defmodule Pleroma.Mixfile do
   def application do
     [
       mod: {Pleroma.Application, []},
-      extra_applications: [:logger, :runtime_tools, :comeonin],
+      extra_applications: [:logger, :runtime_tools, :comeonin, :quack],
       included_applications: [:ex_syslogger]
     ]
   end
@@ -101,8 +101,9 @@ defmodule Pleroma.Mixfile do
       {:ueberauth, "~> 0.4"},
       {:auto_linker,
        git: "https://git.pleroma.social/pleroma/auto_linker.git",
-       ref: "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd"},
-      {:pleroma_job_queue, "~> 0.2.0"}
+       ref: "479dd343f4e563ff91215c8275f3b5c67e032850"},
+      {:pleroma_job_queue, "~> 0.2.0"},
+      {:quack, "~> 0.1.1"}
     ] ++ oauth_deps
   end
 
index b0330b580e01777e4127fef102604c48f6f9b9c4..eb1d1221e472ace9487c4c1aa260b645aa8aed66 100644 (file)
--- a/mix.lock
+++ b/mix.lock
@@ -1,5 +1,5 @@
 %{
-  "auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd", [ref: "94193ca5f97c1f9fdf3d1469653e2d46fac34bcd"]},
+  "auto_linker": {:git, "https://git.pleroma.social/pleroma/auto_linker.git", "479dd343f4e563ff91215c8275f3b5c67e032850", [ref: "479dd343f4e563ff91215c8275f3b5c67e032850"]},
   "base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
   "bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
   "cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
@@ -57,6 +57,7 @@
   "poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
   "poolboy": {:hex, :poolboy, "1.5.2", "392b007a1693a64540cead79830443abf5762f5d30cf50bc95cb2c1aaafa006b", [:rebar3], [], "hexpm"},
   "postgrex": {:hex, :postgrex, "0.14.1", "63247d4a5ad6b9de57a0bac5d807e1c32d41e39c04b8a4156a26c63bcd8a2e49", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
+  "quack": {:hex, :quack, "0.1.1", "cca7b4da1a233757fdb44b3334fce80c94785b3ad5a602053b7a002b5a8967bf", [:mix], [{:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2.0", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm"},
   "ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
   "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
   "swoosh": {:hex, :swoosh, "0.20.0", "9a6c13822c9815993c03b6f8fccc370fcffb3c158d9754f67b1fdee6b3a5d928", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.12", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
diff --git a/priv/repo/migrations/20190403131720_add_oauth_token_indexes.exs b/priv/repo/migrations/20190403131720_add_oauth_token_indexes.exs
new file mode 100644 (file)
index 0000000..ebcd293
--- /dev/null
@@ -0,0 +1,9 @@
+defmodule Pleroma.Repo.Migrations.AddOauthTokenIndexes do
+  use Ecto.Migration
+
+  def change do
+    create(unique_index(:oauth_tokens, [:token]))
+    create(index(:oauth_tokens, [:app_id]))
+    create(index(:oauth_tokens, [:user_id]))
+  end
+end
diff --git a/priv/static/images/pleroma-fox-tan-smol.png b/priv/static/images/pleroma-fox-tan-smol.png
new file mode 100644 (file)
index 0000000..e944d0e
Binary files /dev/null and b/priv/static/images/pleroma-fox-tan-smol.png differ
diff --git a/priv/static/images/pleroma-fox-tan.png b/priv/static/images/pleroma-fox-tan.png
new file mode 100644 (file)
index 0000000..da0022f
Binary files /dev/null and b/priv/static/images/pleroma-fox-tan.png differ
diff --git a/priv/static/images/pleroma-tan.png b/priv/static/images/pleroma-tan.png
new file mode 100644 (file)
index 0000000..6c12c8e
Binary files /dev/null and b/priv/static/images/pleroma-tan.png differ
diff --git a/test/fixtures/httpoison_mock/emelie.atom b/test/fixtures/httpoison_mock/emelie.atom
new file mode 100644 (file)
index 0000000..ddaa1c6
--- /dev/null
@@ -0,0 +1,306 @@
+<?xml version="1.0"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0">
+    <id>https://mastodon.social/users/emelie.atom</id>
+    <title>emelie 🎨</title>
+    <subtitle>23 / #Sweden / #Artist / #Equestrian / #GameDev
+
+If I ain't spending time with my pets, I'm probably drawing. 🐴 🐱 🐰</subtitle>
+    <updated>2019-02-04T20:22:19Z</updated>
+    <logo>https://files.mastodon.social/accounts/avatars/000/015/657/original/e7163f98280da1a4.png</logo>
+    <author>
+        <id>https://mastodon.social/users/emelie</id>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/person</activity:object-type>
+        <uri>https://mastodon.social/users/emelie</uri>
+        <name>emelie</name>
+        <email>emelie@mastodon.social</email>
+        <summary type="html">&lt;p&gt;23 / &lt;a href="https://mastodon.social/tags/sweden" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;Sweden&lt;/span&gt;&lt;/a&gt; / &lt;a href="https://mastodon.social/tags/artist" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;Artist&lt;/span&gt;&lt;/a&gt; / &lt;a href="https://mastodon.social/tags/equestrian" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;Equestrian&lt;/span&gt;&lt;/a&gt; / &lt;a href="https://mastodon.social/tags/gamedev" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;GameDev&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;If I ain&amp;apos;t spending time with my pets, I&amp;apos;m probably drawing. 🐴 🐱 🐰&lt;/p&gt;</summary>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie"/>
+        <link rel="avatar" type="image/png" media:width="120" media:height="120" href="https://files.mastodon.social/accounts/avatars/000/015/657/original/e7163f98280da1a4.png"/>
+        <link rel="header" type="image/png" media:width="700" media:height="335" href="https://files.mastodon.social/accounts/headers/000/015/657/original/847f331f3dd9e38b.png"/>
+        <poco:preferredUsername>emelie</poco:preferredUsername>
+        <poco:displayName>emelie 🎨</poco:displayName>
+        <poco:note>23 / #Sweden / #Artist / #Equestrian / #GameDev
+
+If I ain't spending time with my pets, I'm probably drawing. 🐴 🐱 🐰</poco:note>
+        <mastodon:scope>public</mastodon:scope>
+    </author>
+    <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie"/>
+    <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie.atom"/>
+    <link rel="hub" href="https://mastodon.social/api/push"/>
+    <link rel="salmon" href="https://mastodon.social/api/salmon/15657"/>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101850331907006641</id>
+        <published>2019-04-01T09:58:50Z</published>
+        <updated>2019-04-01T09:58:50Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101850331907006641"/>
+        <content type="html" xml:lang="en">&lt;p&gt;Me: I&amp;apos;m going to make this vital change to my world building in the morning, no way I&amp;apos;ll forget this, it&amp;apos;s too big of a deal&lt;br /&gt;Also me: forgets&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101850331907006641"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17854598.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94383214:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101849626603073336</id>
+        <published>2019-04-01T06:59:28Z</published>
+        <updated>2019-04-01T06:59:28Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849626603073336"/>
+        <content type="html" xml:lang="sv">&lt;p&gt;&lt;span class="h-card"&gt;&lt;a href="https://mastodon.social/@Fergant" class="u-url mention"&gt;@&lt;span&gt;Fergant&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; Dom är i stort sett religiös skrift vid det här laget 👏👏&lt;/p&gt;&lt;p&gt;har dock bara läst svenska översättningen, kanske är dags att jag läser dom på engelska&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="https://mastodon.social/users/Fergant"/>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849626603073336"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17852590.atom"/>
+        <thr:in-reply-to ref="https://mastodon.social/users/Fergant/statuses/101849606513357387" href="https://mastodon.social/@Fergant/101849606513357387"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94362529:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101849580030237068</id>
+        <published>2019-04-01T06:47:37Z</published>
+        <updated>2019-04-01T06:47:37Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849580030237068"/>
+        <content type="html" xml:lang="en">&lt;p&gt;What&amp;apos;s you people&amp;apos;s favourite fantasy books? Give me some hot tips 🌞&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849580030237068"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17852464.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94362529:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101849550599949363</id>
+        <published>2019-04-01T06:40:08Z</published>
+        <updated>2019-04-01T06:40:08Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849550599949363"/>
+        <content type="html" xml:lang="en">&lt;p&gt;Stick them legs out 💃 &lt;a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;mastocats&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <category term="mastocats"/>
+        <link rel="enclosure" type="image/jpeg" length="516384" href="https://files.mastodon.social/media_attachments/files/013/051/707/original/125a310abe9a34aa.jpeg"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849550599949363"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17852407.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94361580:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101849191533152720</id>
+        <published>2019-04-01T05:08:49Z</published>
+        <updated>2019-04-01T05:08:49Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849191533152720"/>
+        <content type="html" xml:lang="en">&lt;p&gt;long 🐱 &lt;a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;mastocats&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <category term="mastocats"/>
+        <link rel="enclosure" type="image/jpeg" length="305208" href="https://files.mastodon.social/media_attachments/files/013/049/940/original/f2dbbfe7de3a17d2.jpeg"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849191533152720"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17851663.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94351141:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101849165031453009</id>
+        <published>2019-04-01T05:02:05Z</published>
+        <updated>2019-04-01T05:02:05Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101849165031453009"/>
+        <content type="html" xml:lang="en">&lt;p&gt;You gotta take whatever bellyrubbing opportunity you can get before she changes her mind 🦁 &lt;a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;mastocats&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <category term="mastocats"/>
+        <link rel="enclosure" type="video/mp4" length="9838915" href="https://files.mastodon.social/media_attachments/files/013/049/816/original/e7831178a5e0d6d4.mp4"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101849165031453009"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17851558.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-04-01:objectId=94350309:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101846512530748693</id>
+        <published>2019-03-31T17:47:31Z</published>
+        <updated>2019-03-31T17:47:31Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101846512530748693"/>
+        <content type="html" xml:lang="en">&lt;p&gt;Hello look at this boy having a decent haircut for once &lt;a href="https://mastodon.social/tags/mastohorses" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;mastohorses&lt;/span&gt;&lt;/a&gt; &lt;a href="https://mastodon.social/tags/equestrian" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;equestrian&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <category term="equestrian"/>
+        <category term="mastohorses"/>
+        <link rel="enclosure" type="image/jpeg" length="461632" href="https://files.mastodon.social/media_attachments/files/013/033/387/original/301e8ab668cd61d2.jpeg"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101846512530748693"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17842424.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-31:objectId=94256415:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101846181093805500</id>
+        <published>2019-03-31T16:23:14Z</published>
+        <updated>2019-03-31T16:23:14Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101846181093805500"/>
+        <content type="html" xml:lang="en">&lt;p&gt;Sorry did I disturb the who-is-the-longest-cat competition ?  &lt;a href="https://mastodon.social/tags/mastocats" class="mention hashtag" rel="tag"&gt;#&lt;span&gt;mastocats&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <category term="mastocats"/>
+        <link rel="enclosure" type="image/jpeg" length="211384" href="https://files.mastodon.social/media_attachments/files/013/030/725/original/5b4886730cbbd25c.jpeg"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101846181093805500"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17841108.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-31:objectId=94245239:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101845897513133849</id>
+        <published>2019-03-31T15:11:07Z</published>
+        <updated>2019-03-31T15:11:07Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101845897513133849"/>
+        <summary xml:lang="en">more earthsea ramblings</summary>
+        <content type="html" xml:lang="en">&lt;p&gt;I&amp;apos;m re-watching Tales from Earthsea for the first time since I read the books, and that Therru doesn&amp;apos;t squash Cob like a spider, as Orm Embar did is a wasted opportunity tbh&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101845897513133849"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17840088.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-31:objectId=94232455:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101841219051533307</id>
+        <published>2019-03-30T19:21:19Z</published>
+        <updated>2019-03-30T19:21:19Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101841219051533307"/>
+        <content type="html" xml:lang="en">&lt;p&gt;I gave my cats some mackerel and they ate it all in 0.3 seconds, and now they won&amp;apos;t stop meowing for more, and I&amp;apos;m tired plz shut up&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101841219051533307"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17826587.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-30:objectId=94075000:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101839949762341381</id>
+        <published>2019-03-30T13:58:31Z</published>
+        <updated>2019-03-30T13:58:31Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101839949762341381"/>
+        <content type="html" xml:lang="en">&lt;p&gt;yet I&amp;apos;m  confused about this american dude with a gun, like the heck r ya doin in mah ghibli&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101839949762341381"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17821757.atom"/>
+        <thr:in-reply-to ref="https://mastodon.social/users/emelie/statuses/101839928677863590" href="https://mastodon.social/@emelie/101839928677863590"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-30:objectId=94026360:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101839928677863590</id>
+        <published>2019-03-30T13:53:09Z</published>
+        <updated>2019-03-30T13:53:09Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101839928677863590"/>
+        <content type="html" xml:lang="en">&lt;p&gt;2 hours into Ni no Kuni 2 and I&amp;apos;ve already sold my soul to this game&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101839928677863590"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17821713.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-30:objectId=94026360:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101836329521599438</id>
+        <published>2019-03-29T22:37:51Z</published>
+        <updated>2019-03-29T22:37:51Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101836329521599438"/>
+        <content type="html" xml:lang="en">&lt;p&gt;Pippi Longstocking the original one-punch /man&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101836329521599438"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17811608.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-29:objectId=93907854:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101835905282948341</id>
+        <published>2019-03-29T20:49:57Z</published>
+        <updated>2019-03-29T20:49:57Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101835905282948341"/>
+        <content type="html" xml:lang="en">&lt;p&gt;I&amp;apos;ve had so much wine I thought I had a 3rd brother&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101835905282948341"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17809862.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-29:objectId=93892966:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101835878059204660</id>
+        <published>2019-03-29T20:43:02Z</published>
+        <updated>2019-03-29T20:43:02Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101835878059204660"/>
+        <content type="html" xml:lang="en">&lt;p&gt;ååååhhh booi&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101835878059204660"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17809734.atom"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-29:objectId=93892010:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101835848050598939</id>
+        <published>2019-03-29T20:35:24Z</published>
+        <updated>2019-03-29T20:35:24Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101835848050598939"/>
+        <content type="html" xml:lang="en">&lt;p&gt;&lt;span class="h-card"&gt;&lt;a href="https://thraeryn.net/@thraeryn" class="u-url mention"&gt;@&lt;span&gt;thraeryn&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; if I spent 1 hour and a half watching this monstrosity, I need to&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="https://thraeryn.net/users/thraeryn"/>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101835848050598939"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17809591.atom"/>
+        <thr:in-reply-to ref="https://thraeryn.net/users/thraeryn/statuses/101835839202826007" href="https://thraeryn.net/@thraeryn/101835839202826007"/>
+        <ostatus:conversation ref="tag:mastodon.social,2019-03-29:objectId=93888827:objectType=Conversation"/>
+    </entry>
+    <entry>
+        <id>https://mastodon.social/users/emelie/statuses/101835823138262290</id>
+        <published>2019-03-29T20:29:04Z</published>
+        <updated>2019-03-29T20:29:04Z</updated>
+        <title>New status by emelie</title>
+        <activity:object-type>http://activitystrea.ms/schema/1.0/comment</activity:object-type>
+        <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb>
+        <link rel="alternate" type="application/activity+json" href="https://mastodon.social/users/emelie/statuses/101835823138262290"/>
+        <summary xml:lang="en">medical, fluids mention</summary>
+        <content type="html" xml:lang="en">&lt;p&gt;&lt;span class="h-card"&gt;&lt;a href="https://icosahedron.website/@Trev" class="u-url mention"&gt;@&lt;span&gt;Trev&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; *hugs* ✨&lt;/p&gt;</content>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="https://icosahedron.website/users/Trev"/>
+        <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/collection" href="http://activityschema.org/collection/public"/>
+        <mastodon:scope>public</mastodon:scope>
+        <link rel="alternate" type="text/html" href="https://mastodon.social/@emelie/101835823138262290"/>
+        <link rel="self" type="application/atom+xml" href="https://mastodon.social/users/emelie/updates/17809468.atom"/>
+        <thr:in-reply-to ref="https://icosahedron.website/users/Trev/statuses/101835812250051801" href="https://icosahedron.website/@Trev/101835812250051801"/>
+        <ostatus:conversation ref="tag:icosahedron.website,2019-03-29:objectId=12220882:objectType=Conversation"/>
+    </entry>
+</feed>
diff --git a/test/fixtures/httpoison_mock/status.emelie.json b/test/fixtures/httpoison_mock/status.emelie.json
new file mode 100644 (file)
index 0000000..4aada03
--- /dev/null
@@ -0,0 +1,64 @@
+{
+    "@context": [
+        "https://www.w3.org/ns/activitystreams",
+        {
+            "ostatus": "http://ostatus.org#",
+            "atomUri": "ostatus:atomUri",
+            "inReplyToAtomUri": "ostatus:inReplyToAtomUri",
+            "conversation": "ostatus:conversation",
+            "sensitive": "as:sensitive",
+            "Hashtag": "as:Hashtag",
+            "toot": "http://joinmastodon.org/ns#",
+            "Emoji": "toot:Emoji",
+            "focalPoint": {
+                "@container": "@list",
+                "@id": "toot:focalPoint"
+            }
+        }
+    ],
+    "id": "https://mastodon.social/users/emelie/statuses/101849165031453009",
+    "type": "Note",
+    "summary": null,
+    "inReplyTo": null,
+    "published": "2019-04-01T05:02:05Z",
+    "url": "https://mastodon.social/@emelie/101849165031453009",
+    "attributedTo": "https://mastodon.social/users/emelie",
+    "to": [
+        "https://www.w3.org/ns/activitystreams#Public"
+    ],
+    "cc": [
+        "https://mastodon.social/users/emelie/followers"
+    ],
+    "sensitive": false,
+    "atomUri": "https://mastodon.social/users/emelie/statuses/101849165031453009",
+    "inReplyToAtomUri": null,
+    "conversation": "tag:mastodon.social,2019-04-01:objectId=94350309:objectType=Conversation",
+    "content": "<p>You gotta take whatever bellyrubbing opportunity you can get before she changes her mind 🦁 <a href=\"https://mastodon.social/tags/mastocats\" class=\"mention hashtag\" rel=\"tag\">#<span>mastocats</span></a></p>",
+    "contentMap": {
+        "en": "<p>You gotta take whatever bellyrubbing opportunity you can get before she changes her mind 🦁 <a href=\"https://mastodon.social/tags/mastocats\" class=\"mention hashtag\" rel=\"tag\">#<span>mastocats</span></a></p>"
+    },
+    "attachment": [
+        {
+            "type": "Document",
+            "mediaType": "video/mp4",
+            "url": "https://files.mastodon.social/media_attachments/files/013/049/816/original/e7831178a5e0d6d4.mp4",
+            "name": null
+        }
+    ],
+    "tag": [
+        {
+            "type": "Hashtag",
+            "href": "https://mastodon.social/tags/mastocats",
+            "name": "#mastocats"
+        }
+    ],
+    "replies": {
+        "id": "https://mastodon.social/users/emelie/statuses/101849165031453009/replies",
+        "type": "Collection",
+        "first": {
+            "type": "CollectionPage",
+            "partOf": "https://mastodon.social/users/emelie/statuses/101849165031453009/replies",
+            "items": []
+        }
+    }
+}
diff --git a/test/fixtures/httpoison_mock/webfinger_emelie.json b/test/fixtures/httpoison_mock/webfinger_emelie.json
new file mode 100644 (file)
index 0000000..0b61cb6
--- /dev/null
@@ -0,0 +1,36 @@
+{
+    "aliases": [
+        "https://mastodon.social/@emelie",
+        "https://mastodon.social/users/emelie"
+    ],
+    "links": [
+        {
+            "href": "https://mastodon.social/@emelie",
+            "rel": "http://webfinger.net/rel/profile-page",
+            "type": "text/html"
+        },
+        {
+            "href": "https://mastodon.social/users/emelie.atom",
+            "rel": "http://schemas.google.com/g/2010#updates-from",
+            "type": "application/atom+xml"
+        },
+        {
+            "href": "https://mastodon.social/users/emelie",
+            "rel": "self",
+            "type": "application/activity+json"
+        },
+        {
+            "href": "https://mastodon.social/api/salmon/15657",
+            "rel": "salmon"
+        },
+        {
+            "href": "data:application/magic-public-key,RSA.u3CWs1oAJPE3ZJ9sj6Ut_Mu-mTE7MOijsQc8_6c73XVVuhIEomiozJIH7l8a7S1n5SYL4UuiwcubSOi7u1bbGpYnp5TYhN-Cxvq_P80V4_ncNIPSQzS49it7nSLeG5pA21lGPDA44huquES1un6p9gSmbTwngVX9oe4MYuUeh0Z7vijjU13Llz1cRq_ZgPQPgfz-2NJf-VeXnvyDZDYxZPVBBlrMl3VoGbu0M5L8SjY35559KCZ3woIvqRolcoHXfgvJMdPcJgSZVYxlCw3dA95q9jQcn6s87CPSUs7bmYEQCrDVn5m5NER5TzwBmP4cgJl9AaDVWQtRd4jFZNTxlQ==.AQAB",
+            "rel": "magic-public-key"
+        },
+        {
+            "rel": "http://ostatus.org/schema/1.0/subscribe",
+            "template": "https://mastodon.social/authorize_interaction?uri={uri}"
+        }
+    ],
+    "subject": "acct:emelie@mastodon.social"
+}
index 78e8efc9df77c1128d21bc73630e20b8752b4ec0..d3b547d91c89b5635ce39c84d75f410fdfeba8f1 100644 (file)
@@ -36,6 +36,43 @@ defmodule HttpRequestMock do
      }}
   end
 
+  def get("https://mastodon.social/users/emelie/statuses/101849165031453009", _, _, _) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/httpoison_mock/status.emelie.json")
+     }}
+  end
+
+  def get("https://mastodon.social/users/emelie", _, _, _) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/httpoison_mock/emelie.json")
+     }}
+  end
+
+  def get(
+        "https://mastodon.social/.well-known/webfinger?resource=https://mastodon.social/users/emelie",
+        _,
+        _,
+        _
+      ) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/httpoison_mock/webfinger_emelie.json")
+     }}
+  end
+
+  def get("https://mastodon.social/users/emelie.atom", _, _, _) do
+    {:ok,
+     %Tesla.Env{
+       status: 200,
+       body: File.read!("test/fixtures/httpoison_mock/emelie.atom")
+     }}
+  end
+
   def get(
         "https://osada.macgirvin.com/.well-known/webfinger?resource=acct:mike@osada.macgirvin.com",
         _,
index 7b814d171dad113cd0677d4f6547a34117f0d771..1030bd555ef2bd154914899c1ee6ec8ccb7aa300 100644 (file)
@@ -248,4 +248,14 @@ defmodule Mix.Tasks.Pleroma.UserTest do
       assert message =~ "Generated"
     end
   end
+
+  describe "running delete_activities" do
+    test "activities are deleted" do
+      %{nickname: nickname} = insert(:user)
+
+      assert :ok == Mix.Tasks.Pleroma.User.run(["delete_activities", nickname])
+      assert_received {:mix_shell, :info, [message]}
+      assert message == "User #{nickname} statuses deleted."
+    end
+  end
 end
index bab77fb82c823fee11165693240c3b7046f5163e..38712cebb6aafb1edf7f4639806b4a52a5a87d97 100644 (file)
@@ -799,6 +799,16 @@ defmodule Pleroma.UserTest do
     assert false == user.info.deactivated
   end
 
+  test ".delete_user_activities deletes all create activities" do
+    user = insert(:user)
+
+    {:ok, activity} = CommonAPI.post(user, %{"status" => "2hu"})
+    {:ok, _} = User.delete_user_activities(user)
+
+    # TODO: Remove favorites, repeats, delete activities.
+    refute Activity.get_by_id(activity.id)
+  end
+
   test ".delete deactivates a user, all follow relationships and all create activities" do
     user = insert(:user)
     followed = insert(:user)
index 7969c8035ba0a8a2007b73b1085bf7036967b055..17fec05b102e193183d09a3b30453a2cebd86a0d 100644 (file)
@@ -635,16 +635,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
     end
   end
 
-  describe "fetch the latest Follow" do
-    test "fetches the latest Follow activity" do
-      %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
-      follower = Repo.get_by(User, ap_id: activity.data["actor"])
-      followed = Repo.get_by(User, ap_id: activity.data["object"])
-
-      assert activity == Utils.fetch_latest_follow(follower, followed)
-    end
-  end
-
   describe "fetching an object" do
     test "it fetches an object" do
       {:ok, object} =
index 2bd3ddf932c632ab017128e81a69ff8bd31643e4..6b9961d82d4a130ca39bd80cfd60c8d2f47aa2d5 100644 (file)
@@ -1,10 +1,34 @@
 defmodule Pleroma.Web.ActivityPub.UtilsTest do
   use Pleroma.DataCase
+  alias Pleroma.Activity
+  alias Pleroma.Repo
+  alias Pleroma.User
+  alias Pleroma.Web.ActivityPub.ActivityPub
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.CommonAPI
 
   import Pleroma.Factory
 
+  describe "fetch the latest Follow" do
+    test "fetches the latest Follow activity" do
+      %Activity{data: %{"type" => "Follow"}} = activity = insert(:follow_activity)
+      follower = Repo.get_by(User, ap_id: activity.data["actor"])
+      followed = Repo.get_by(User, ap_id: activity.data["object"])
+
+      assert activity == Utils.fetch_latest_follow(follower, followed)
+    end
+  end
+
+  describe "fetch the latest Block" do
+    test "fetches the latest Block activity" do
+      blocker = insert(:user)
+      blocked = insert(:user)
+      {:ok, activity} = ActivityPub.block(blocker, blocked)
+
+      assert activity == Utils.fetch_latest_block(blocker, blocked)
+    end
+  end
+
   describe "determine_explicit_mentions()" do
     test "works with an object that has mentions" do
       object = %{
index e04b9f9b53e3a51ba0c5ee8abf1352ca90cb6df0..f0c59d5c33dc71aaff97334590ce766ae35e1a89 100644 (file)
@@ -153,4 +153,40 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
       assert conversation_id == object.id
     end
   end
+
+  describe "formats date to asctime" do
+    test "when date is in ISO 8601 format" do
+      date = DateTime.utc_now() |> DateTime.to_iso8601()
+
+      expected =
+        date
+        |> DateTime.from_iso8601()
+        |> elem(1)
+        |> Calendar.Strftime.strftime!("%a %b %d %H:%M:%S %z %Y")
+
+      assert Utils.date_to_asctime(date) == expected
+    end
+
+    test "when date is a binary in wrong format" do
+      date = DateTime.utc_now()
+
+      expected = ""
+
+      assert Utils.date_to_asctime(date) == expected
+    end
+
+    test "when date is a Unix timestamp" do
+      date = DateTime.utc_now() |> DateTime.to_unix()
+
+      expected = ""
+
+      assert Utils.date_to_asctime(date) == expected
+    end
+
+    test "when date is nil" do
+      expected = ""
+
+      assert Utils.date_to_asctime(nil) == expected
+    end
+  end
 end
index 1f3b268800fe843fcd6860c311a22788f0c36249..6060cc97feada708fa7e21dcb57e94739ec46bbb 100644 (file)
@@ -143,6 +143,55 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
     assert Activity.get_by_id(id)
   end
 
+  test "posting a fake status", %{conn: conn} do
+    user = insert(:user)
+
+    real_conn =
+      conn
+      |> assign(:user, user)
+      |> post("/api/v1/statuses", %{
+        "status" =>
+          "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it"
+      })
+
+    real_status = json_response(real_conn, 200)
+
+    assert real_status
+    assert Object.get_by_ap_id(real_status["uri"])
+
+    real_status =
+      real_status
+      |> Map.put("id", nil)
+      |> Map.put("url", nil)
+      |> Map.put("uri", nil)
+      |> Map.put("created_at", nil)
+      |> Kernel.put_in(["pleroma", "conversation_id"], nil)
+
+    fake_conn =
+      conn
+      |> assign(:user, user)
+      |> post("/api/v1/statuses", %{
+        "status" =>
+          "\"Tenshi Eating a Corndog\" is a much discussed concept on /jp/. The significance of it is disputed, so I will focus on one core concept: the symbolism behind it",
+        "preview" => true
+      })
+
+    fake_status = json_response(fake_conn, 200)
+
+    assert fake_status
+    refute Object.get_by_ap_id(fake_status["uri"])
+
+    fake_status =
+      fake_status
+      |> Map.put("id", nil)
+      |> Map.put("url", nil)
+      |> Map.put("uri", nil)
+      |> Map.put("created_at", nil)
+      |> Kernel.put_in(["pleroma", "conversation_id"], nil)
+
+    assert real_status == fake_status
+  end
+
   test "posting a status with OGP link preview", %{conn: conn} do
     Pleroma.Config.put([:rich_media, :enabled], true)
     user = insert(:user)
index e1c9b2c8f61a03897673143f77800204d8b346ae..8db92ac160fd43efe0d8cf138d3115eb81eb7db4 100644 (file)
@@ -175,7 +175,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
 
     status = StatusView.render("status.json", %{activity: activity})
 
-    actor = Repo.get_by(User, ap_id: activity.actor)
+    actor = User.get_by_ap_id(activity.actor)
 
     assert status.mentions ==
              Enum.map([user, actor], fn u -> AccountView.render("mention.json", %{user: u}) end)
index 265e1abbd44588dac1674851f3ece4b0c54ce7de..35503259b933cd2cfe0ba39a5553e4a836cdcffe 100644 (file)
@@ -99,7 +99,7 @@ defmodule Pleroma.Web.Salmon.SalmonTest do
     }
 
     {:ok, activity} = Repo.insert(%Activity{data: activity_data, recipients: activity_data["to"]})
-    user = Repo.get_by(User, ap_id: activity.data["actor"])
+    user = User.get_by_ap_id(activity.data["actor"])
     {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user)
 
     poster = fn url, _data, _headers ->
index dffd401f74e34944f56e5b8420210750d095c1f5..72b7ea85eba8e001cde0a212aa8a73ca9f09d464 100644 (file)
@@ -955,7 +955,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
         |> post(request_path)
 
       activity = Activity.get_by_id(note_activity.id)
-      activity_user = Repo.get_by(User, ap_id: note_activity.data["actor"])
+      activity_user = User.get_by_ap_id(note_activity.data["actor"])
 
       assert json_response(response, 200) ==
                ActivityView.render("activity.json", %{
@@ -993,7 +993,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
         |> post(request_path)
 
       activity = Activity.get_by_id(note_activity.id)
-      activity_user = Repo.get_by(User, ap_id: note_activity.data["actor"])
+      activity_user = User.get_by_ap_id(note_activity.data["actor"])
 
       assert json_response(response, 200) ==
                ActivityView.render("activity.json", %{
@@ -1021,7 +1021,7 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
 
       user = json_response(conn, 200)
 
-      fetched_user = Repo.get_by(User, nickname: "lain")
+      fetched_user = User.get_by_nickname("lain")
       assert user == UserView.render("show.json", %{user: fetched_user})
     end
 
index b823bfd68f6e00a4c0486aa20816d09df6776f39..6c00244deb70ff1a8ffa866c4a76c9ddee2e30a1 100644 (file)
@@ -275,7 +275,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
 
     {:ok, user} = TwitterAPI.register_user(data)
 
-    fetched_user = Repo.get_by(User, nickname: "lain")
+    fetched_user = User.get_by_nickname("lain")
 
     assert UserView.render("show.json", %{user: user}) ==
              UserView.render("show.json", %{user: fetched_user})
@@ -293,7 +293,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
 
     {:ok, user} = TwitterAPI.register_user(data)
 
-    fetched_user = Repo.get_by(User, nickname: "lain")
+    fetched_user = User.get_by_nickname("lain")
 
     assert UserView.render("show.json", %{user: user}) ==
              UserView.render("show.json", %{user: fetched_user})
@@ -369,7 +369,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
 
     {:ok, user} = TwitterAPI.register_user(data)
 
-    fetched_user = Repo.get_by(User, nickname: "vinny")
+    fetched_user = User.get_by_nickname("vinny")
     token = Repo.get_by(UserInviteToken, token: token.token)
 
     assert token.used == true
@@ -393,7 +393,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     {:error, msg} = TwitterAPI.register_user(data)
 
     assert msg == "Invalid token"
-    refute Repo.get_by(User, nickname: "GrimReaper")
+    refute User.get_by_nickname("GrimReaper")
   end
 
   @moduletag skip: "needs 'registrations_open: false' in config"
@@ -414,7 +414,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     {:error, msg} = TwitterAPI.register_user(data)
 
     assert msg == "Expired token"
-    refute Repo.get_by(User, nickname: "GrimReaper")
+    refute User.get_by_nickname("GrimReaper")
   end
 
   test "it returns the error on registration problems" do
@@ -429,7 +429,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
     {:error, error_object} = TwitterAPI.register_user(data)
 
     assert is_binary(error_object[:error])
-    refute Repo.get_by(User, nickname: "lain")
+    refute User.get_by_nickname("lain")
   end
 
   test "it assigns an integer conversation_id" do
index 832fdc09692b118fe71a1e3d94a6e682ae254efb..e4dd97d46915c738c16c067188cf6a54bf653c69 100644 (file)
@@ -6,6 +6,11 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
   alias Pleroma.Web.CommonAPI
   import Pleroma.Factory
 
+  setup do
+    Tesla.Mock.mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
+    :ok
+  end
+
   describe "POST /api/pleroma/follow_import" do
     test "it returns HTTP 200", %{conn: conn} do
       user1 = insert(:user)
@@ -164,4 +169,26 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do
       assert response == Jason.encode!(config |> Enum.into(%{})) |> Jason.decode!()
     end
   end
+
+  describe "GET /ostatus_subscribe?acct=...." do
+    test "adds status to pleroma instance if the `acct` is a status", %{conn: conn} do
+      conn =
+        get(
+          conn,
+          "/ostatus_subscribe?acct=https://mastodon.social/users/emelie/statuses/101849165031453009"
+        )
+
+      assert redirected_to(conn) =~ "/notice/"
+    end
+
+    test "show follow account page if the `acct` is a account link", %{conn: conn} do
+      response =
+        get(
+          conn,
+          "/ostatus_subscribe?acct=https://mastodon.social/users/emelie"
+        )
+
+      assert html_response(response, 200) =~ "Log in to follow"
+    end
+  end
 end