[db-host] [wp-version] [skip-database-creation]"
5 | exit 1
6 | fi
7 |
8 | DB_NAME=$1
9 | DB_USER=$2
10 | DB_PASS=$3
11 | DB_HOST=${4-localhost}
12 | WP_VERSION=${5-latest}
13 | SKIP_DB_CREATE=${6-false}
14 |
15 | TMPDIR=${TMPDIR-/tmp}
16 | TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
17 | WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
18 | WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
19 |
20 | download() {
21 | if [ `which curl` ]; then
22 | curl -s "$1" > "$2";
23 | elif [ `which wget` ]; then
24 | wget -nv -O "$2" "$1"
25 | fi
26 | }
27 |
28 | if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
29 | WP_TESTS_TAG="branches/$WP_VERSION"
30 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
31 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
32 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
33 | WP_TESTS_TAG="tags/${WP_VERSION%??}"
34 | else
35 | WP_TESTS_TAG="tags/$WP_VERSION"
36 | fi
37 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
38 | WP_TESTS_TAG="trunk"
39 | else
40 | # http serves a single offer, whereas https serves multiple. we only want one
41 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
42 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
43 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
44 | if [[ -z "$LATEST_VERSION" ]]; then
45 | echo "Latest WordPress version could not be found"
46 | exit 1
47 | fi
48 | WP_TESTS_TAG="tags/$LATEST_VERSION"
49 | fi
50 |
51 | set -ex
52 |
53 | install_wp() {
54 |
55 | if [ -d $WP_CORE_DIR ]; then
56 | return;
57 | fi
58 |
59 | mkdir -p $WP_CORE_DIR
60 |
61 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
62 | mkdir -p $TMPDIR/wordpress-nightly
63 | download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip
64 | unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
65 | mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR
66 | else
67 | if [ $WP_VERSION == 'latest' ]; then
68 | local ARCHIVE_NAME='latest'
69 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
70 | # https serves multiple offers, whereas http serves single.
71 | download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json
72 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
73 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
74 | LATEST_VERSION=${WP_VERSION%??}
75 | else
76 | # otherwise, scan the releases and get the most up to date minor version of the major release
77 | local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'`
78 | LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1)
79 | fi
80 | if [[ -z "$LATEST_VERSION" ]]; then
81 | local ARCHIVE_NAME="wordpress-$WP_VERSION"
82 | else
83 | local ARCHIVE_NAME="wordpress-$LATEST_VERSION"
84 | fi
85 | else
86 | local ARCHIVE_NAME="wordpress-$WP_VERSION"
87 | fi
88 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz
89 | tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
90 | fi
91 |
92 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
93 | }
94 |
95 | install_test_suite() {
96 | # portable in-place argument for both GNU sed and Mac OSX sed
97 | if [[ $(uname -s) == 'Darwin' ]]; then
98 | local ioption='-i .bak'
99 | else
100 | local ioption='-i'
101 | fi
102 |
103 | # set up testing suite if it doesn't yet exist
104 | if [ ! -d $WP_TESTS_DIR ]; then
105 | # set up testing suite
106 | mkdir -p $WP_TESTS_DIR
107 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
108 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
109 | fi
110 |
111 | if [ ! -f wp-tests-config.php ]; then
112 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
113 | # remove all forward slashes in the end
114 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
115 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
116 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
117 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
118 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
119 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
120 | fi
121 |
122 | }
123 |
124 | install_db() {
125 |
126 | if [ ${SKIP_DB_CREATE} = "true" ]; then
127 | return 0
128 | fi
129 |
130 | # parse DB_HOST for port or socket references
131 | local PARTS=(${DB_HOST//\:/ })
132 | local DB_HOSTNAME=${PARTS[0]};
133 | local DB_SOCK_OR_PORT=${PARTS[1]};
134 | local EXTRA=""
135 |
136 | if ! [ -z $DB_HOSTNAME ] ; then
137 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
138 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
139 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then
140 | EXTRA=" --socket=$DB_SOCK_OR_PORT"
141 | elif ! [ -z $DB_HOSTNAME ] ; then
142 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
143 | fi
144 | fi
145 |
146 | # create database
147 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
148 | }
149 |
150 | install_wp
151 | install_test_suite
152 | install_db
153 |
--------------------------------------------------------------------------------
/templates/linkbacks.php:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 | 'reacji',
16 | ),
17 | Semantic_Linkbacks_Walker_Comment::$reactions
18 | );
19 | ?>
20 |
21 |
22 |
23 |
24 |
25 |
26 | 'like',
30 | ),
31 | get_linkbacks( 'like' )
32 | );
33 | ?>
34 |
35 |
36 |
37 |
38 |
39 |
40 | 'favorite',
44 | ),
45 | get_linkbacks( 'favorite' )
46 | );
47 | ?>
48 |
49 |
50 |
51 |
52 |
53 |
54 | 'bookmark',
58 | ),
59 | get_linkbacks( 'bookmark' )
60 | );
61 | ?>
62 |
63 |
64 |
65 |
66 |
67 |
68 | 'repost',
72 | ),
73 | get_linkbacks( 'repost' )
74 | );
75 | ?>
76 |
77 |
78 |
79 |
80 |
81 |
82 | 'tag',
86 | ),
87 | get_linkbacks( 'tag' )
88 | );
89 | ?>
90 |
91 |
92 |
93 |
94 |
95 |
96 | 'listen',
100 | ),
101 | get_linkbacks( 'listen' )
102 | );
103 | ?>
104 |
105 |
106 |
107 |
108 |
109 |
110 | 'read',
114 | ),
115 | get_linkbacks( 'read' )
116 | );
117 | ?>
118 |
119 |
120 |
121 |
122 |
123 |
124 | 'follow',
128 | ),
129 | get_linkbacks( 'follow' )
130 | );
131 | ?>
132 |
133 |
134 |
135 |
136 |
137 |
138 | 'watch',
142 | ),
143 | get_linkbacks( 'watch' )
144 | );
145 | ?>
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 | 'rsvp-yes',
161 | ),
162 | get_linkbacks( 'rsvp:yes' )
163 | );
164 | ?>
165 |
166 |
167 |
168 |
169 | 'invited',
173 | ),
174 | get_linkbacks( 'invited' )
175 | );
176 | ?>
177 |
178 |
179 |
180 |
181 | 'rsvp-maybe',
185 | ),
186 | get_linkbacks( 'rsvp:maybe' )
187 | );
188 | ?>
189 |
190 |
191 |
192 |
193 | 'rsvp-no',
197 | ),
198 | get_linkbacks( 'rsvp:no' )
199 | );
200 | ?>
201 |
202 |
203 |
204 |
205 | 'rsvp-interested',
209 | ),
210 | get_linkbacks( 'rsvp:interested' )
211 | );
212 | ?>
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 | 'mention',
224 | ),
225 | get_linkbacks( 'mention' )
226 | );
227 | ?>
228 |
229 |
230 |
--------------------------------------------------------------------------------
/tests/templates/tantek-com.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | going to Homebrew Website Club, 2014-05-21 18:30 @MozSF. indieweb: werd.io/2014/homebrew-website-club-5 silo: fb.com/events/1509367239285562/ - Tantek
4 |
5 |
6 |
7 |
8 |
9 |
20 |
21 |
22 |
23 |
34 |
35 |
36 |
37 |
42 |
43 |
49 |
50 | going to Homebrew Website Club, 2014-05-21 18:30 @MozSF . indieweb: werd.io/2014/homebrew-website-club-5 silo: fb.com/events/1509367239285562/
51 |
85 |
86 |
87 |
88 |
151 |
152 |
153 |
--------------------------------------------------------------------------------
/includes/class-linkbacks-walker-comment.php:
--------------------------------------------------------------------------------
1 | %s', $author );
40 | } else {
41 | $return = sprintf( '%s ', $url, $author );
42 | }
43 |
44 | /**
45 | * Filters the comment author's link for display.
46 | *
47 | * @since 1.5.0
48 | * @since 4.1.0 The `$author` and `$comment_ID` parameters were added
49 | * @param string $return The HTML-formatted comment author link.
50 | * Empty for an invalid URL.
51 | * @param string $author The comment author's username.
52 | * @param int $comment_ID The comment ID.
53 | */
54 | return apply_filters( 'get_comment_author_link', $return, $author, $comment->comment_ID );
55 | }
56 |
57 | protected static function is_reaction( $comment ) {
58 | // If this library is not installed then emoji detection will not work
59 | if ( ! function_exists( 'mb_internal_encoding' ) ) {
60 | return false;
61 | }
62 | return Emoji\is_single_emoji( trim( wp_strip_all_tags( $comment->comment_content ) ) ) && empty( $comment->comment_parent );
63 | }
64 |
65 | public function start_el( &$output, $comment, $depth = 0, $args = array(), $id = 0 ) {
66 | if ( self::is_reaction( $comment ) ) {
67 | self::$reactions[] = $comment;
68 | }
69 |
70 | if ( ! self::should_facepile( $comment ) ) {
71 | return parent::start_el( $output, $comment, $depth, $args, $id );
72 | }
73 | }
74 |
75 | public function end_el( &$output, $comment, $depth = 0, $args = array() ) {
76 | if ( ! self::should_facepile( $comment ) ) {
77 | return parent::end_el( $output, $comment, $depth, $args );
78 | }
79 | }
80 |
81 | protected function html5_comment( $comment, $depth, $args ) {
82 | // To use the default html5_comment set this filter to false
83 | if ( ! Linkbacks_Handler::render_comments() ) {
84 | parent::html5_comment( $comment, $depth, $args );
85 | return;
86 | }
87 | $tag = ( 'div' === $args['style'] ) ? 'div' : 'li';
88 | $cite = apply_filters( 'semantic_linkbacks_cite', ' @ %2s ' );
89 | $type = Linkbacks_Handler::get_type( $comment );
90 | $url = Linkbacks_Handler::get_url( $comment );
91 | $coins = Linkbacks_Handler::get_prop( $comment, 'mf2_swarm-coins' );
92 | $host = wp_parse_url( $url, PHP_URL_HOST );
93 | // strip leading www, if any
94 | $host = preg_replace( '/^www\./', '', $host );
95 | ?>
96 | < id="comment-" has_children ? 'parent' : '', $comment ); ?>>
97 |
98 |
144 |
145 |
148 |
149 | 'div-comment',
155 | 'depth' => $depth,
156 | 'max_depth' => $args['max_depth'],
157 | 'before' => '',
158 | 'after' => '
',
159 | )
160 | )
161 | );
162 | ?>
163 |
164 |
2 |
3 |
4 |
5 |
6 | New service: WebMentions for static pages
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
24 |
25 |
26 | New service: WebMentions for static pages
27 |
28 |
29 |
30 |
I'm now launching a service that can receive and embeds WebMentions for sites. Useful for eg. static blogs on GitHub Pages.
31 |
32 |
Earlier this year when I went to the IndieWebCamp in Brighton , a very nice event, the thing people were most excited about, at least when it came to the actually implementing it, was WebMentions .
33 |
34 |
So when it was time to get hands on with code I wanted to do so as well, my only trouble was: This blog is on Jekyll/GitHub Pages since two years and I like it there. How can I then accept something as dynamic as a comment from someone elses blog?
35 |
36 |
I know you’re already thinking it – a hosted solution like Disqus could surely solve my problem and yes indeed, therefore I went ahead and built one. Then came ordinary life and occupied me with other stuff, but now finally a very first rough version is ready for launch and can be found at webmention.herokuapp.com .
37 |
38 |
This post will prove that it works by appearing as the first WebMention on webmention.herokuapp.com itself and hopefully mentions from others will start appearing both there and on this blog eventually so that maybe I’ll become as cool as Aaron Parecki , Jeremy Keith and the others in the end.
39 |
40 |
I’m aware that this service isn’t totally inline with the spirit of the IndieWeb, that you should own and host your own stuff (I’m not even using IndieAuth yet!), but it’s my pragmatic solution for getting a step closer to such a world and a solution I’m happy to share with others.
41 |
42 |
I’m opening up just five accounts for others now, considering this is still an early alpha, and will keep the development open on GitHub – all feedback is welcome!
43 |
44 |
45 |
46 |
47 | 18 December 2013
48 | by
49 | Pelle Wessman
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | Have you written a response to this? Let me know the URL:
59 |
64 |
65 |
66 |
76 |
77 |
78 |
79 |
89 |
98 |
105 |
106 |
--------------------------------------------------------------------------------
/includes/class-linkbacks-avatar-handler.php:
--------------------------------------------------------------------------------
1 | comment_ID, 'avatar', true );
41 | // Backward Compatibility for Semantic Linkbacks
42 | if ( ! $avatar ) {
43 | $avatar = get_comment_meta( $comment->comment_ID, 'semantic_linkbacks_avatar', true );
44 | }
45 |
46 | return $avatar;
47 | }
48 |
49 |
50 | /**
51 | * Function to retrieve default avatar URL
52 | *
53 | *
54 | * @param string $type Default Avatar URL
55 | *
56 | * @return string|boolean $url
57 | */
58 | public static function get_default_avatar( $type = null ) {
59 | if ( ! $type ) {
60 | $type = get_option( 'avatar_default', 'mystery' );
61 | }
62 | switch ( $type ) {
63 | case 'mm':
64 | case 'mystery':
65 | case 'mysteryman':
66 | return plugin_dir_url( dirname( __FILE__ ) ) . 'img/mm.jpg';
67 | }
68 | return apply_filters( 'semantic_linkbacks_default_avatar', $type );
69 | }
70 |
71 | /**
72 | * Function to check if there is a gravatar
73 | *
74 | *
75 | * @param WP_Comment $comment
76 | *
77 | * @return boolean
78 | */
79 | public static function check_gravatar( $comment ) {
80 | $hash = md5( strtolower( trim( $comment->comment_author_email ) ) );
81 | $url = 'https://www.gravatar.com/avatar/' . $hash . '?d=404';
82 | $response = wp_remote_head( $url );
83 | if ( is_wp_error( $response ) || 404 === wp_remote_retrieve_response_code( $response ) ) {
84 | return false;
85 | }
86 | return true;
87 | }
88 |
89 |
90 | /**
91 | * Replaces the default avatar with a locally stored default
92 | *
93 | * @param array $args Arguments passed to get_avatar_data(), after processing.
94 | * @param int|string|object $id_or_email A user ID, email address, or comment object
95 | *
96 | * @return array $args
97 | */
98 | public static function anonymous_avatar_data( $args, $id_or_email ) {
99 | $local = apply_filters( 'semantic_linkbacks_local_avatars', array( 'mm', 'mystery', 'mysteryman' ) );
100 | if ( ! in_array( $args['default'], $local, true ) ) {
101 | return $args;
102 | }
103 | // Always override if default forced
104 | if ( $args['force_default'] ) {
105 | $args['url'] = self::get_default_avatar( $args['default'] );
106 | return $args;
107 | }
108 | if ( ! strpos( $args['url'], 'gravatar.com' ) ) {
109 | return $args;
110 | }
111 | $args['url'] = str_replace( 'd=' . $args['default'], 'd=' . self::get_default_avatar( $args['default'] ), $args['url'] );
112 | if ( $id_or_email instanceof WP_Comment ) {
113 | if ( ! empty( $id_or_email->comment_author_email ) ) {
114 | if ( self::check_gravatar( $id_or_email ) ) {
115 | update_comment_meta( $id_or_email->comment_ID, 'avatar', $args['url'] );
116 | } else {
117 | update_comment_meta( $id_or_email->comment_ID, 'avatar', self::get_default_avatar() );
118 | $args['url'] = self::get_default_avatar();
119 | }
120 | return $args;
121 | } else {
122 | $args['url'] = self::get_default_avatar();
123 | }
124 | }
125 | return $args;
126 | }
127 |
128 | /**
129 | * Replaces the default avatar with the WebMention uf2 photo
130 | *
131 | * @param array $args Arguments passed to get_avatar_data(), after processing.
132 | * @param int|string|object $id_or_email A user ID, email address, or comment object
133 | *
134 | * @return array $args
135 | */
136 | public static function pre_get_avatar_data( $args, $id_or_email ) {
137 | if ( ! $id_or_email instanceof WP_Comment ||
138 | ! isset( $id_or_email->comment_type ) ||
139 | $id_or_email->user_id ) {
140 | return $args;
141 | }
142 |
143 | $allowed_comment_types = apply_filters( 'get_avatar_comment_types', array( 'comment' ) );
144 | if ( ! empty( $id_or_email->comment_type ) && ! in_array( $id_or_email->comment_type, (array) $allowed_comment_types, true ) ) {
145 | $args['url'] = false;
146 | /** This filter is documented in wp-includes/link-template.php */
147 | return apply_filters( 'get_avatar_data', $args, $id_or_email );
148 | }
149 |
150 | $type = Linkbacks_Handler::get_type( $id_or_email );
151 | $type = explode( ':', $type );
152 |
153 | if ( is_array( $type ) ) {
154 | $type = $type[0];
155 | }
156 |
157 | $option = 'semantic_linkbacks_facepile_' . $type;
158 |
159 | if ( get_option( $option, false ) && 'blank' === $args['default'] ) {
160 | $args['default'] = 'anonymous';
161 | }
162 |
163 | // check if comment has an avatar
164 | $avatar = self::get_avatar_url( $id_or_email->comment_ID );
165 |
166 | if ( $avatar ) {
167 | if ( ! isset( $args['class'] ) || ! is_array( $args['class'] ) ) {
168 | $args['class'] = array( 'u-photo' );
169 | } else {
170 | $args['class'][] = 'u-photo';
171 | $args['class'] = array_unique( $args['class'] );
172 | }
173 | $args['url'] = $avatar;
174 | $args['class'][] = 'avatar-semantic-linkbacks';
175 | }
176 |
177 | return $args;
178 | }
179 |
180 | /**
181 | * Show avatars also on trackbacks and pingbacks
182 | *
183 | * @param array $types list of avatar enabled comment types
184 | *
185 | * @return array show avatars also on trackbacks and pingbacks
186 | */
187 | public static function get_avatar_comment_types( $types ) {
188 | $types[] = 'pingback';
189 | $types[] = 'trackback';
190 |
191 | return array_unique( $types );
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/vendor/mf2/mf2/LICENSE.md:
--------------------------------------------------------------------------------
1 | # Creative Commons Legal Code
2 |
3 | ## CC0 1.0 Universal
4 |
5 | http://creativecommons.org/publicdomain/zero/1.0
6 |
7 | Official translations of this legal tool are available> CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER.
8 |
9 | ### _Statement of Purpose_
10 |
11 | The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
12 |
13 | Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
14 |
15 | For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
16 |
17 | **1. Copyright and Related Rights.** A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
18 |
19 | 1. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
20 | 2. moral rights retained by the original author(s) and/or performer(s);
21 | 3. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
22 | 4. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
23 | 5. rights protecting the extraction, dissemination, use and reuse of data in a Work;
24 | 6. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
25 | 7. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
26 |
27 | **2. Waiver.** To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
28 |
29 | **3. Public License Fallback.** Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
30 |
31 | **4. Limitations and Disclaimers.**
32 |
33 | 1. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
34 | 2. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
35 | 3. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
36 | 4. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
37 |
--------------------------------------------------------------------------------
/semantic-linkbacks.php:
--------------------------------------------------------------------------------
1 | (may not work on all themes) for:', 'semantic-linkbacks' ),
76 | array( 'Semantic_Linkbacks_Plugin', 'facepile_checkboxes' ),
77 | $page,
78 | 'semantic-linkbacks'
79 | );
80 | add_settings_field(
81 | 'semantic_linkbacks_facepile_fold_limit',
82 | __( 'Initial number of faces to show in facepiles (0 for all) ', 'semantic-linkbacks' ),
83 | array( 'Semantic_Linkbacks_Plugin', 'facepile_fold_limit' ),
84 | $page,
85 | 'semantic-linkbacks'
86 | );
87 | }
88 |
89 | public static function facepile_fold_limit() {
90 | printf( ' ', get_option( 'semantic_linkbacks_facepiles_fold_limit' ) );
91 | }
92 |
93 | public static function facepile_checkboxes() {
94 | $strings = Linkbacks_Handler::get_comment_type_strings();
95 | $facepile = get_option( 'semantic_linkbacks_facepiles' );
96 | // If getting the facepiles hasn't worked, create an empty array to avoid generating errors
97 | if ( ! is_array( $facepile ) ) {
98 | $facepile = array();
99 | }
100 | echo '';
101 | foreach ( $strings as $key => $value ) {
102 | printf( '%3$s ', $key, checked( in_array( $key, $facepile, true ), true, false ), $value );
103 | }
104 | echo '
';
105 | }
106 |
107 | public static function register_settings() {
108 | $option_group = function_exists( 'webmention_init' ) ? 'webmention' : 'discussion';
109 | register_setting(
110 | $option_group,
111 | 'semantic_linkbacks_facepiles',
112 | array(
113 | 'type' => 'string',
114 | 'description' => __( 'Types to show in Facepiles', 'semantic-linkbacks' ),
115 | 'show_in_rest' => true,
116 | 'default' => array_keys( Linkbacks_Handler::get_comment_type_strings() ),
117 | )
118 | );
119 | register_setting(
120 | $option_group,
121 | 'semantic_linkbacks_facepiles_fold_limit',
122 | array(
123 | 'type' => 'integer',
124 | 'description' => __( 'Initial number of faces to show in facepiles', 'semantic-linkbacks' ),
125 | 'show_in_rest' => true,
126 | 'default' => 8,
127 | )
128 | );
129 | }
130 |
131 | /**
132 | * Load language files
133 | */
134 | public static function plugin_textdomain() {
135 | // Note to self, the third argument must not be hardcoded, to account for relocated folders.
136 | load_plugin_textdomain( 'semantic-linkbacks', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
137 | }
138 |
139 | /**
140 | * Add Semantic Linkbacks options to the webmentions settings page.
141 | */
142 | public static function settings() {
143 | _e( 'For webmentions that do not have avatars you can pick from several locally served default avatars in the Discussion Settings', 'semantic-linkbacks' );
144 |
145 | if ( ! function_exists( 'mb_internal_encoding' ) ) {
146 | ?>
147 |
148 | $post_id,
16 | 'count' => true,
17 | 'status' => 'approve',
18 | );
19 |
20 | if ( $type ) { // use type if set
21 | if ( 'mention' === $type ) {
22 | $args['type__not_in'] = 'comment';
23 | $args['meta_query'] = array(
24 | 'relation' => 'OR',
25 | array(
26 | 'key' => 'semantic_linkbacks_type',
27 | 'value' => '',
28 | ),
29 | array(
30 | 'key' => 'semantic_linkbacks_type',
31 | 'compare' => 'NOT EXISTS',
32 | ),
33 | array(
34 | 'key' => 'semantic_linkbacks_type',
35 | 'value' => 'mention',
36 | ),
37 | );
38 | } elseif ( 'rsvp' === $type ) {
39 | $args['meta_query'] = array(
40 | array(
41 | 'key' => 'semantic_linkbacks_type',
42 | 'value' => 'rsvp',
43 | 'compare' => 'LIKE',
44 | ),
45 | );
46 | } else {
47 | $args['meta_query'] = array(
48 | array(
49 | 'key' => 'semantic_linkbacks_type',
50 | 'value' => $type,
51 | ),
52 | );
53 | }
54 | } else { // check only if type exists
55 | $args['meta_query'] = array(
56 | array(
57 | 'key' => 'semantic_linkbacks_type',
58 | 'compare' => 'EXISTS',
59 | ),
60 | );
61 | }
62 |
63 | $comments = get_comments( $args );
64 | if ( $comments ) {
65 | return $comments;
66 | } else {
67 | return 0;
68 | }
69 | }
70 |
71 | /**
72 | * Returns comments of linkback type
73 | *
74 | * @param string $type the comment type
75 | * @param int $post_id the id of the post
76 | * @param string $order the order of the retrieved comments, ASC or DESC (default)
77 | *
78 | * @return the matching linkback "comments"
79 | */
80 | function get_linkbacks( $type = null, $post_id = null, $order = 'DESC' ) {
81 | if ( null === $post_id ) {
82 | $post_id = get_the_ID();
83 | }
84 | $args = array(
85 | 'post_id' => $post_id,
86 | 'status' => 'approve',
87 | 'order' => $order,
88 | );
89 |
90 | if ( $type ) { // use type if set
91 | if ( 'mention' === $type ) {
92 | $args['type__not_in'] = 'comment';
93 | $args['meta_query'] = array(
94 | 'relation' => 'OR',
95 | array(
96 | 'key' => 'semantic_linkbacks_type',
97 | 'value' => '',
98 | ),
99 | array(
100 | 'key' => 'semantic_linkbacks_type',
101 | 'compare' => 'NOT EXISTS',
102 | ),
103 | array(
104 | 'key' => 'semantic_linkbacks_type',
105 | 'value' => 'mention',
106 | ),
107 | );
108 | } elseif ( 'rsvp' === $type ) {
109 | $args['meta_query'] = array(
110 | array(
111 | 'key' => 'semantic_linkbacks_type',
112 | 'value' => 'rsvp',
113 | 'compare' => 'LIKE',
114 | ),
115 | );
116 | } else {
117 | $args['meta_query'] = array(
118 | array(
119 | 'key' => 'semantic_linkbacks_type',
120 | 'value' => $type,
121 | ),
122 | );
123 | }
124 | } else { // check only if type exists
125 | $args['meta_query'] = array(
126 | array(
127 | 'key' => 'semantic_linkbacks_type',
128 | 'compare' => 'EXISTS',
129 | ),
130 | );
131 | }
132 |
133 | return get_comments( $args );
134 | }
135 |
136 |
137 | function has_linkbacks( $type = null, $post_ID = null ) {
138 | if ( get_linkbacks( $type, $post_ID ) ) {
139 | return true;
140 | }
141 | return false;
142 | }
143 |
144 | /**
145 | * Return marked up linkbacks
146 | * Based on wp_list_comments()
147 | */
148 | function list_linkbacks( $args, $comments ) {
149 | $defaults = array(
150 | 'avatar_size' => 64,
151 | 'style' => 'ul', // What HTML type to wrap it in. Accepts 'ul', 'ol'.
152 | 'style-class' => 'mention-list', // What class to assign to the wrapper
153 | 'li-class' => null, // What class to assign to the list elements
154 | 'echo' => true, // Whether to echo the output or return
155 | 'type' => 'mention', // Type is the semantic linkbacks type and is here only to automatically add to the classes if present
156 | );
157 |
158 | $r = wp_parse_args( $args, $defaults );
159 | /**
160 | * Filters the arguments used in retrieving the linkbacks list
161 | *
162 | *
163 | * @param array $r An array of arguments for displaying linkbacks
164 | */
165 | $r = apply_filters( 'list_linkbacks_args', $r );
166 | if ( ! is_array( $comments ) ) {
167 | return;
168 | }
169 | if ( is_string( $r['li-class'] ) ) {
170 | $classes = explode( ' ', $r['li-class'] );
171 | } else {
172 | $classes = $r['li-class'];
173 | }
174 | if ( is_string( $r['style-class'] ) ) {
175 | $r['style-class'] = explode( ' ', $r['style-class'] );
176 | }
177 |
178 | $classes[] = 'linkback-' . $r['type'] . '-single';
179 | $r['style-class'][] = 'linkback-' . $r['type'];
180 |
181 | $return = sprintf( '<%1$s class="%2$s">', $r['style'], join( ' ', $r['style-class'] ) );
182 | $fold_at = function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() ? 0 : (int) get_option( 'semantic_linkbacks_facepiles_fold_limit', 8 );
183 |
184 | $type_labels = array(
185 | 'reacji' => __( 'Reacjis', 'semantic-linkbacks' ),
186 | 'like' => __( 'Likes', 'semantic-linkbacks' ),
187 | 'favorite' => __( 'Favourites', 'semantic-linkbacks' ),
188 | 'bookmark' => __( 'Bookmarks', 'semantic-linkbacks' ),
189 | 'repost' => __( 'Reposts', 'semantic-linkbacks' ),
190 | 'tag' => __( 'Tags', 'semantic-linkbacks' ),
191 | 'listen' => __( 'Listening', 'semantic-linkbacks' ),
192 | 'read' => __( 'Reading', 'semantic-linkbacks' ),
193 | 'follow' => __( 'Following', 'semantic-linkbacks' ),
194 | 'watch' => __( 'Watching', 'semantic-linkbacks' ),
195 | 'rsvp-yes' => __( 'RSVPs', 'semantic-linkbacks' ),
196 | 'invited' => __( 'Invited', 'semantic-linkbacks' ),
197 | 'rsvp-maybe' => __( 'Maybe', 'semantic-linkbacks' ),
198 | 'rsvp-no' => __( 'No', 'semantic-linkbacks' ),
199 | 'rsvp-interested' => __( 'Interested', 'semantic-linkbacks' ),
200 | 'mention' => __( 'Mentions', 'semantic-linkbacks' ),
201 | );
202 |
203 | foreach ( $comments as $i => $comment ) {
204 | if ( $fold_at && $i === $fold_at ) {
205 | $classes[] = 'additional-facepile';
206 |
207 | // Add show button.
208 | $return .= sprintf(
209 | '… %s ',
210 | sprintf( /* translators: s=Linback type */
211 | __( 'Show more %s', 'semantic-linkbacks' ),
212 | $type_labels[ $r['type'] ]
213 | )
214 | );
215 | }
216 |
217 | // If it's an emoji reaction, overlay the emoji.
218 | $overlay = '';
219 | $content = trim( wp_strip_all_tags( $comment->comment_content ) );
220 | $title = Linkbacks_Handler::comment_text_excerpt( '', $comment );
221 | if ( Emoji\is_single_emoji( $content ) ) {
222 | $overlay = ' ' . $content . ' ';
223 | $url = wp_parse_url( Linkbacks_Handler::get_url( $comment ), PHP_URL_HOST );
224 | $title = sprintf(
225 | '%1$s %2$s on %3$s.',
226 | $comment->comment_author,
227 | $content,
228 | preg_replace( '/^www\./', '', $url )
229 | );
230 | }
231 | $class = get_comment_class( $classes, $comment );
232 | $class = join( ' ', $class );
233 | $avatar = get_avatar( $comment, $r['avatar_size'] );
234 | // If the avatar comes back empty show the name
235 | if ( ! $avatar ) {
236 | $avatar = get_comment_author( $comment );
237 | }
238 | $return .= sprintf(
239 | '%2$s%8$s %4$s ',
240 | $class,
241 | $avatar,
242 | get_comment_author_url( $comment ),
243 | get_comment_author( $comment ),
244 | esc_attr( 'comment-' . $comment->comment_ID ),
245 | esc_attr( wp_strip_all_tags( $title ) ),
246 | esc_url_raw( Linkbacks_Handler::get_canonical_url( $comment ) ),
247 | $overlay
248 | );
249 | }
250 |
251 | if ( $fold_at && count( $comments ) > $fold_at ) {
252 | // Add hide button at end.
253 | $return .= sprintf(
254 | '… %s ',
255 | sprintf( /* translators: s=Linback type */
256 | __( 'Show fewer %s', 'semantic-linkbacks' ),
257 | $type_labels[ $r['type'] ]
258 | )
259 | );
260 | }
261 |
262 | $return .= sprintf( '%1$s>', $r['style'] );
263 | if ( $r['echo'] ) {
264 | echo $return;
265 | }
266 | return $return;
267 | }
268 |
--------------------------------------------------------------------------------
/includes/class-linkbacks-notifications.php:
--------------------------------------------------------------------------------
1 | comment_post_ID );
81 | if ( ! Linkbacks_Handler::get_type( $comment ) ) {
82 | return $notify_message;
83 | }
84 | $notify_message = self::notification( $comment );
85 | $notify_message .= self::notification_body( $comment );
86 | if ( user_can( $post->post_author, 'edit_comment', $comment->comment_ID ) ) {
87 | $notify_message .= self::moderate_text( $comment ) . "\r\n";
88 | }
89 | return $notify_message;
90 |
91 | }
92 |
93 | /**
94 | * Generate the moderation text
95 | *
96 | * @param int|WP_Comment $comment comment
97 | * @return string $message Appropriate text.
98 | */
99 | public static function moderate_text( $comment ) {
100 | $comment = get_comment( $comment );
101 | /* translators: Comment moderation. 1: Comment action URL */
102 | $message = sprintf( __( 'Approve it: %s', 'semantic-linkbacks' ), admin_url( "comment.php?action=approve&c={$comment_id}#wpbody-content" ) ) . "\r\n";
103 | if ( EMPTY_TRASH_DAYS ) {
104 | /* translators: Trash it URL */
105 | $message .= sprintf( __( 'Trash it: %s', 'semantic-linkbacks' ), admin_url( "comment.php?action=trash&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
106 | } else {
107 | /* translators: Delete it URL */
108 | $message .= sprintf( __( 'Delete it: %s', 'semantic-linkbacks' ), admin_url( "comment.php?action=delete&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
109 | }
110 | /* translators: Spam it URL */
111 | $message .= sprintf( __( 'Spam it: %s', 'semantic-linkbacks' ), admin_url( "comment.php?action=spam&c={$comment->comment_ID}#wpbody-content" ) ) . "\r\n";
112 | return $message;
113 | }
114 |
115 | /**
116 | * Filter the comment notification subject
117 | *
118 | * @param string $subject
119 | * @param int $comment_id comment
120 | * @return string $subject
121 | */
122 | public static function comment_notification_subject( $subject, $comment_id ) {
123 | $comment = get_comment( $comment_id );
124 | $type = Linkbacks_Handler::get_type( $comment );
125 | if ( ! $type ) {
126 | return $subject;
127 | }
128 | $title = get_the_title( $comment->comment_post_ID );
129 | $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
130 | /* translators: 1: blog name, 2: Semantic Linkbacks type, 3: post title */
131 | return sprintf( __( '[%1$s] %2$s: %3$s', 'semantic-linkbacks' ), $blogname, $type, $title );
132 | }
133 |
134 | /**
135 | * Filter the comment moderation text
136 | *
137 | * @param string $notify_message
138 | * @param int $comment_id comment
139 | * @return string $notify_message
140 | */
141 | public static function comment_moderation_text( $notify_message, $comment_id ) {
142 | $comment = get_comment( $comment_id );
143 | $notify_message = self::moderation( $comment ) . "\r\n\r\n";
144 | $notify_message .= self::notification_body( $comment ) . "\r\n\r\n";
145 | $notify_message .= self::moderate_text( $comment );
146 | return $notify_message;
147 | }
148 |
149 | /**
150 | * Filter a notification text
151 | * @param string $notify_messsage
152 | * @param int|WP_Comment $comment
153 | * @return string $notify_basic
154 | */
155 | public static function basic_notification_text( $notify_message, $comment_id ) {
156 | $comment = get_comment( $comment_id );
157 | $type = Linkbacks_Handler::get_type( $comment );
158 | if ( ! $type ) {
159 | return '';
160 | }
161 | $type = Linkbacks_Handler::get_comment_type_strings( $type );
162 | $comment_link = get_comment_link( $comment );
163 | $notify_basic = wp_strip_all_tags( get_comment_text( $comment ) );
164 | return $notify_basic;
165 | }
166 |
167 |
168 | /**
169 | * Filter the comment moderation subject
170 | *
171 | * @param string $subject
172 | * @param int $comment_id comment
173 | * @return string $subject
174 | */
175 | public static function comment_moderation_subject( $subject, $comment_id ) {
176 | $comment = get_comment( $comment_id );
177 | $type = Linkbacks_Handler::get_type( $comment );
178 | if ( ! $type ) {
179 | return $subject;
180 | }
181 | $type = Linkbacks_Handler::get_comment_type_strings( $type );
182 | $title = get_the_title( $comment->comment_post_ID );
183 | $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES );
184 | /* translators: Comment moderation notification email subject. 1: Site name, 2: Semantic Linkbacks type, 3: Post title */
185 | return sprintf( __( '[%1$s] Please moderate a %2$s: %3$s', 'semantic-linkbacks' ), $blogname, $type, $title );
186 | }
187 |
188 |
189 | /**
190 | * Generate the body of the notification
191 | *
192 | * @param int|WP_Comment $comment comment
193 | * @return string $message Appropriate text.
194 | */
195 | public static function notification_body( $comment ) {
196 | $comment = get_comment( $comment );
197 | $type = Linkbacks_Handler::get_type( $comment );
198 | if ( ! $type ) {
199 | return '';
200 | }
201 | $type = Linkbacks_Handler::get_comment_type_strings( $type );
202 | /* translators: 1: website name, 2: website URL */
203 | $notify_text[] = sprintf( __( 'Author: %1$1s(%2$2s) ', 'semantic-linkbacks' ), get_comment_author( $comment ), Linkbacks_Handler::get_author_url( $comment ) );
204 | /* translators: Semantic Linkback comment type string */
205 | $notify_text[] = sprintf( __( 'Semantic Type: %s', 'semantic-linkbacks' ), $type );
206 | /* translators: URL */
207 | $notify_text[] = sprintf( __( 'URL: %s ', 'semantic-linkbacks' ), Linkbacks_Handler::get_url( $comment ) );
208 | /* translators: Comment Text */
209 | $notify_text[] = sprintf( __( 'Text: %s', 'semantic-linkbacks' ), "\r\n" . wp_strip_all_tags( get_comment_text( $comment ) ) );
210 | /* translators: 1. Post Permalink */
211 | $notify_text[] = "\r\n" . sprintf( __( 'You can see all responses to this post here: %1s#comments', 'semantic-linkbacks' ), get_permalink( $comment->comment_post_ID ) );
212 | /* translators: Comment Permalink */
213 | $notify_text[] = sprintf( __( 'Permalink: %s', 'semantic-linkbacks' ), get_comment_link( $comment ) );
214 | return implode( "\r\n", $notify_text );
215 | }
216 |
217 | /**
218 | * Generate the notification which can be placed at the top of the email or as a text notification
219 | *
220 | * @param int|WP_Comment $comment comment
221 | * @return string $message Appropriate text.
222 | */
223 | public static function notification( $comment ) {
224 | $comment = get_comment( $comment );
225 | $type = Linkbacks_Handler::get_type( $comment );
226 | if ( ! $type ) {
227 | return '';
228 | }
229 | $type = Linkbacks_Handler::get_comment_type_strings( $type );
230 | /* translators: 1. Semantic Linkbacks type, 2. Post Title */
231 | return sprintf( __( 'New %1$1s on your post "%2$2s"', 'semantic-linkbacks' ), $type, get_the_title( $comment->comment_post_ID ) );
232 | }
233 |
234 | /**
235 | * Generate the moderation notification which can be placed at the top of the email or as a text notification
236 | *
237 | * @param int|WP_Comment $comment comment
238 | * @return string $message Appropriate text.
239 | */
240 | public static function moderation( $comment ) {
241 | $type = Linkbacks_Handler::get_type( $comment );
242 | if ( ! $type ) {
243 | return '';
244 | }
245 | $type = Linkbacks_Handler::get_comment_type_strings( $type );
246 | /* translators: 1. Semantic Linkbacks type, 2. Post Title */
247 | return sprintf( __( 'A new %1$1s on the post "%2$2s" is waiting for your approval', 'semantic-linkbacks' ), $type, get_the_title( $comment->comment_post_ID ) );
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/languages/semantic-linkbacks.pot:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2020 Matthias Pfefferle
2 | # This file is distributed under the MIT.
3 | msgid ""
4 | msgstr ""
5 | "Project-Id-Version: Semantic-Linkbacks 3.10.1\n"
6 | "Report-Msgid-Bugs-To: "
7 | "https://wordpress.org/support/plugin/wordpress-semantic-linkbacks\n"
8 | "POT-Creation-Date: 2020-04-07 12:37:56+00:00\n"
9 | "MIME-Version: 1.0\n"
10 | "Content-Type: text/plain; charset=utf-8\n"
11 | "Content-Transfer-Encoding: 8bit\n"
12 | "PO-Revision-Date: 2020-MO-DA HO:MI+ZONE\n"
13 | "Last-Translator: FULL NAME \n"
14 | "Language-Team: LANGUAGE \n"
15 | "X-Generator: grunt-wp-i18n 1.0.3\n"
16 |
17 | #: includes/class-linkbacks-avatar-handler.php:23
18 | msgid "Mystery Person (hosted locally)"
19 | msgstr ""
20 |
21 | #: includes/class-linkbacks-handler.php:57
22 | msgid "Semantic Linkbacks Data"
23 | msgstr ""
24 |
25 | #: includes/class-linkbacks-handler.php:274
26 | #. translators: Name verb on domain
27 | msgid "%1$s mentioned %2$s on %4$s ."
28 | msgstr ""
29 |
30 | #: includes/class-linkbacks-handler.php:276
31 | #. translators: Name verb on domain
32 | msgid "%1$s replied to %2$s on %4$s ."
33 | msgstr ""
34 |
35 | #: includes/class-linkbacks-handler.php:278
36 | #. translators: Name verb on domain
37 | msgid "%1$s reposted %2$s on %4$s ."
38 | msgstr ""
39 |
40 | #: includes/class-linkbacks-handler.php:280
41 | #. translators: Name verb on domain
42 | msgid "%1$s liked %2$s on %4$s ."
43 | msgstr ""
44 |
45 | #: includes/class-linkbacks-handler.php:282
46 | #. translators: Name verb on domain
47 | msgid "%1$s favorited %2$s on %4$s ."
48 | msgstr ""
49 |
50 | #: includes/class-linkbacks-handler.php:284
51 | #. translators: Name verb on domain
52 | msgid "%1$s tagged %2$s on %4$s ."
53 | msgstr ""
54 |
55 | #: includes/class-linkbacks-handler.php:286
56 | #. translators: Name verb on domain
57 | msgid "%1$s bookmarked %2$s on %4$s ."
58 | msgstr ""
59 |
60 | #: includes/class-linkbacks-handler.php:288
61 | #. translators: Name verb on domain
62 | msgid "%1$s is attending ."
63 | msgstr ""
64 |
65 | #: includes/class-linkbacks-handler.php:290
66 | #. translators: Name verb on domain
67 | msgid "%1$s is not attending ."
68 | msgstr ""
69 |
70 | #: includes/class-linkbacks-handler.php:292
71 | #. translators: Name verb on domain
72 | msgid "Maybe %1$s will be attending ."
73 | msgstr ""
74 |
75 | #: includes/class-linkbacks-handler.php:294
76 | #. translators: Name verb on domain
77 | msgid "%1$s is interested in this event."
78 | msgstr ""
79 |
80 | #: includes/class-linkbacks-handler.php:296
81 | #. translators: Name verb on domain
82 | msgid "%1$s is invited ."
83 | msgstr ""
84 |
85 | #: includes/class-linkbacks-handler.php:298
86 | #. translators: Name verb on domain
87 | msgid "%1$s listened to %2$s (via %4$s )."
88 | msgstr ""
89 |
90 | #: includes/class-linkbacks-handler.php:300
91 | #. translators: Name verb on domain
92 | msgid "%1$s watched %2$s (via %4$s )."
93 | msgstr ""
94 |
95 | #: includes/class-linkbacks-handler.php:302
96 | #. translators: Name verb on domain
97 | msgid "%1$s read %2$s (via %4$s )."
98 | msgstr ""
99 |
100 | #: includes/class-linkbacks-handler.php:304
101 | #. translators: Name verb on domain
102 | msgid "%1$s followed %2$s (via %4$s )."
103 | msgstr ""
104 |
105 | #: includes/class-linkbacks-handler.php:320
106 | msgid "Mention"
107 | msgstr ""
108 |
109 | #: includes/class-linkbacks-handler.php:322
110 | msgid "Reply"
111 | msgstr ""
112 |
113 | #: includes/class-linkbacks-handler.php:323
114 | msgid "Repost"
115 | msgstr ""
116 |
117 | #: includes/class-linkbacks-handler.php:324
118 | msgid "Like"
119 | msgstr ""
120 |
121 | #: includes/class-linkbacks-handler.php:325
122 | msgid "Favorite"
123 | msgstr ""
124 |
125 | #: includes/class-linkbacks-handler.php:326
126 | msgid "Tag"
127 | msgstr ""
128 |
129 | #: includes/class-linkbacks-handler.php:327
130 | msgid "Bookmark"
131 | msgstr ""
132 |
133 | #: includes/class-linkbacks-handler.php:328
134 | msgid "RSVP Yes"
135 | msgstr ""
136 |
137 | #: includes/class-linkbacks-handler.php:329
138 | msgid "RSVP No"
139 | msgstr ""
140 |
141 | #: includes/class-linkbacks-handler.php:330
142 | msgid "RSVP Maybe"
143 | msgstr ""
144 |
145 | #: includes/class-linkbacks-handler.php:331
146 | msgid "RSVP Interested"
147 | msgstr ""
148 |
149 | #: includes/class-linkbacks-handler.php:332 templates/linkbacks.php:161
150 | msgid "Invited"
151 | msgstr ""
152 |
153 | #: includes/class-linkbacks-handler.php:333
154 | msgid "Listen"
155 | msgstr ""
156 |
157 | #: includes/class-linkbacks-handler.php:334
158 | msgid "Watch"
159 | msgstr ""
160 |
161 | #: includes/class-linkbacks-handler.php:335
162 | msgid "Read"
163 | msgstr ""
164 |
165 | #: includes/class-linkbacks-handler.php:336
166 | msgid "Follow"
167 | msgstr ""
168 |
169 | #: includes/class-linkbacks-handler.php:337
170 | msgid "Reacji"
171 | msgstr ""
172 |
173 | #: includes/class-linkbacks-handler.php:365
174 | msgid "this Post"
175 | msgstr ""
176 |
177 | #: includes/class-linkbacks-handler.php:366
178 | msgid "this Page"
179 | msgstr ""
180 |
181 | #: includes/class-linkbacks-handler.php:368
182 | msgid "this Article"
183 | msgstr ""
184 |
185 | #: includes/class-linkbacks-handler.php:369
186 | msgid "this Aside"
187 | msgstr ""
188 |
189 | #: includes/class-linkbacks-handler.php:370
190 | msgid "this Chat"
191 | msgstr ""
192 |
193 | #: includes/class-linkbacks-handler.php:371
194 | msgid "this Gallery"
195 | msgstr ""
196 |
197 | #: includes/class-linkbacks-handler.php:372
198 | msgid "this Link"
199 | msgstr ""
200 |
201 | #: includes/class-linkbacks-handler.php:373
202 | msgid "this Image"
203 | msgstr ""
204 |
205 | #: includes/class-linkbacks-handler.php:374
206 | msgid "this Quote"
207 | msgstr ""
208 |
209 | #: includes/class-linkbacks-handler.php:375
210 | msgid "this Status"
211 | msgstr ""
212 |
213 | #: includes/class-linkbacks-handler.php:376
214 | msgid "this Video"
215 | msgstr ""
216 |
217 | #: includes/class-linkbacks-handler.php:377
218 | msgid "this Audio"
219 | msgstr ""
220 |
221 | #: includes/class-linkbacks-notifications.php:34
222 | msgid "Resend Comment Email"
223 | msgstr ""
224 |
225 | #: includes/class-linkbacks-notifications.php:35
226 | msgid "Resend Moderation Email"
227 | msgstr ""
228 |
229 | #: includes/class-linkbacks-notifications.php:37
230 | msgid "Send Pushover Note"
231 | msgstr ""
232 |
233 | #: includes/class-linkbacks-notifications.php:40
234 | msgid "Send Pushbullet Note"
235 | msgstr ""
236 |
237 | #: includes/class-linkbacks-notifications.php:102
238 | #. translators: Comment moderation. 1: Comment action URL
239 | msgid "Approve it: %s"
240 | msgstr ""
241 |
242 | #: includes/class-linkbacks-notifications.php:105
243 | #. translators: Trash it URL
244 | msgid "Trash it: %s"
245 | msgstr ""
246 |
247 | #: includes/class-linkbacks-notifications.php:108
248 | #. translators: Delete it URL
249 | msgid "Delete it: %s"
250 | msgstr ""
251 |
252 | #: includes/class-linkbacks-notifications.php:111
253 | #. translators: Spam it URL
254 | msgid "Spam it: %s"
255 | msgstr ""
256 |
257 | #: includes/class-linkbacks-notifications.php:131
258 | #. translators: 1: blog name, 2: Semantic Linkbacks type, 3: post title
259 | msgid "[%1$s] %2$s: %3$s"
260 | msgstr ""
261 |
262 | #: includes/class-linkbacks-notifications.php:185
263 | #. translators: Comment moderation notification email subject. 1: Site name, 2:
264 | #. Semantic Linkbacks type, 3: Post title
265 | msgid "[%1$s] Please moderate a %2$s: %3$s"
266 | msgstr ""
267 |
268 | #: includes/class-linkbacks-notifications.php:203
269 | #. translators: 1: website name, 2: website URL
270 | msgid "Author: %1$1s(%2$2s) "
271 | msgstr ""
272 |
273 | #: includes/class-linkbacks-notifications.php:205
274 | #. translators: Semantic Linkback comment type string
275 | msgid "Semantic Type: %s"
276 | msgstr ""
277 |
278 | #: includes/class-linkbacks-notifications.php:207
279 | #. translators: URL
280 | msgid "URL: %s "
281 | msgstr ""
282 |
283 | #: includes/class-linkbacks-notifications.php:209
284 | #. translators: Comment Text
285 | msgid "Text: %s"
286 | msgstr ""
287 |
288 | #: includes/class-linkbacks-notifications.php:211
289 | #. translators: 1. Post Permalink
290 | msgid "You can see all responses to this post here: %1s#comments"
291 | msgstr ""
292 |
293 | #: includes/class-linkbacks-notifications.php:213
294 | #. translators: Comment Permalink
295 | msgid "Permalink: %s"
296 | msgstr ""
297 |
298 | #: includes/class-linkbacks-notifications.php:231
299 | #. translators: 1. Semantic Linkbacks type, 2. Post Title
300 | msgid "New %1$1s on your post \"%2$2s\""
301 | msgstr ""
302 |
303 | #: includes/class-linkbacks-notifications.php:247
304 | #. translators: 1. Semantic Linkbacks type, 2. Post Title
305 | msgid "A new %1$1s on the post \"%2$2s\" is waiting for your approval"
306 | msgstr ""
307 |
308 | #: includes/class-linkbacks-walker-comment.php:104
309 | #. translators: %s: comment author link
310 | msgid "%s says: "
311 | msgstr ""
312 |
313 | #: includes/class-linkbacks-walker-comment.php:118
314 | #. translators: Number of Swarm Coins
315 | msgid "+%d coin"
316 | msgid_plural "+%d coins"
317 | msgstr[0] ""
318 | msgstr[1] ""
319 |
320 | #: includes/class-linkbacks-walker-comment.php:129
321 | #. translators: 1: comment date, 2: comment time
322 | msgid "%1$s at %2$s"
323 | msgstr ""
324 |
325 | #: includes/class-linkbacks-walker-comment.php:133
326 | msgid "Edit"
327 | msgstr ""
328 |
329 | #: includes/class-linkbacks-walker-comment.php:137
330 | msgid "Your response is awaiting moderation."
331 | msgstr ""
332 |
333 | #: semantic-linkbacks.php:67
334 | msgid "Semantic Linkbacks Settings"
335 | msgstr ""
336 |
337 | #: semantic-linkbacks.php:73
338 | msgid ""
339 | "Automatically embed facepiles (may not work on all themes) "
340 | "for:"
341 | msgstr ""
342 |
343 | #: semantic-linkbacks.php:80
344 | msgid "Initial number of faces to show in facepiles (0 for all) "
345 | msgstr ""
346 |
347 | #: semantic-linkbacks.php:108
348 | msgid "Types to show in Facepiles"
349 | msgstr ""
350 |
351 | #: semantic-linkbacks.php:118
352 | msgid "Initial number of faces to show in facepiles"
353 | msgstr ""
354 |
355 | #: semantic-linkbacks.php:137
356 | msgid ""
357 | "For webmentions that do not have avatars you can pick from several locally "
358 | "served default avatars in the Discussion Settings"
359 | msgstr ""
360 |
361 | #: semantic-linkbacks.php:141
362 | msgid ""
363 | "This server does not have the php-mbstring package installed and Emoji "
364 | "reactions have been disabled."
365 | msgstr ""
366 |
367 | #: semantic-linkbacks.php:159
368 | msgid ""
369 | "For received webmentions, pingbacks and trackbacks, such as responding to a "
370 | "post or article, this site stores information retrieved from the source\n"
371 | "\t\t\t\tin order to provide a richer comment. Items such as author name and "
372 | "image, summary of the text, etc may be stored if present in the source and "
373 | "are\n"
374 | "\t\t\t\tsolely to provide richer comments. We will remove any of this on "
375 | "request."
376 | msgstr ""
377 |
378 | #: templates/linkbacks-edit-comment-form.php:2
379 | msgid "Source"
380 | msgstr ""
381 |
382 | #: templates/linkbacks-edit-comment-form.php:6
383 | msgid "Canonical"
384 | msgstr ""
385 |
386 | #: templates/linkbacks-edit-comment-form.php:10
387 | msgid "Avatar"
388 | msgstr ""
389 |
390 | #: templates/linkbacks-edit-comment-form.php:14
391 | msgid "Type"
392 | msgstr ""
393 |
394 | #: templates/linkbacks.php:4
395 | msgid "Reacjis"
396 | msgstr ""
397 |
398 | #: templates/linkbacks.php:18
399 | msgid "Likes"
400 | msgstr ""
401 |
402 | #: templates/linkbacks.php:32
403 | msgid "Favourites"
404 | msgstr ""
405 |
406 | #: templates/linkbacks.php:46
407 | msgid "Bookmarks"
408 | msgstr ""
409 |
410 | #: templates/linkbacks.php:60
411 | msgid "Reposts"
412 | msgstr ""
413 |
414 | #: templates/linkbacks.php:74
415 | msgid "Tags"
416 | msgstr ""
417 |
418 | #: templates/linkbacks.php:88
419 | msgid "Listening"
420 | msgstr ""
421 |
422 | #: templates/linkbacks.php:102
423 | msgid "Reading"
424 | msgstr ""
425 |
426 | #: templates/linkbacks.php:116
427 | msgid "Following"
428 | msgstr ""
429 |
430 | #: templates/linkbacks.php:130
431 | msgid "Watching"
432 | msgstr ""
433 |
434 | #: templates/linkbacks.php:146
435 | msgid "RSVPs"
436 | msgstr ""
437 |
438 | #: templates/linkbacks.php:149
439 | msgid "Yes"
440 | msgstr ""
441 |
442 | #: templates/linkbacks.php:173
443 | msgid "Maybe"
444 | msgstr ""
445 |
446 | #: templates/linkbacks.php:185
447 | msgid "No"
448 | msgstr ""
449 |
450 | #: templates/linkbacks.php:197
451 | msgid "Interested"
452 | msgstr ""
453 |
454 | #: templates/linkbacks.php:212
455 | msgid "Mentions"
456 | msgstr ""
457 |
458 | #. Plugin Name of the plugin/theme
459 | msgid "Semantic-Linkbacks"
460 | msgstr ""
461 |
462 | #. Plugin URI of the plugin/theme
463 | msgid "https://github.com/pfefferle/wordpress-semantic-linkbacks"
464 | msgstr ""
465 |
466 | #. Description of the plugin/theme
467 | msgid "Semantic Linkbacks for WebMentions, Trackbacks and Pingbacks"
468 | msgstr ""
469 |
470 | #. Author of the plugin/theme
471 | msgid "Matthias Pfefferle"
472 | msgstr ""
473 |
474 | #. Author URI of the plugin/theme
475 | msgid "https://notiz.blog/"
476 | msgstr ""
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === Semantic-Linkbacks ===
2 | Contributors: pfefferle, dshanske, edent
3 | Donate link: https://notiz.blog/donate/
4 | Tags: webmention, pingback, trackback, linkback, microformats, comments, indieweb
5 | Requires at least: 4.9
6 | Requires PHP: 5.6
7 | Tested up to: 6.1
8 | Stable tag: 3.11.3
9 | License: MIT
10 | License URI: http://opensource.org/licenses/MIT
11 |
12 | Richer Comments and Linkbacks for WordPress!
13 |
14 | == Description ==
15 |
16 | Generates richer WordPress comments from linkbacks such as [Webmention](https://wordpress.org/plugins/webmention) or classic linkback protocols like Trackback or Pingback.
17 |
18 | The limited display for trackbacks and linkbacks is replaced by a clean full sentence, such as "Bob mentioned this article on bob.com." If Bob's site uses markup that the plugin can interpret, it may add his profile picture or other parts of his page to display as a full comment. It will optionally show collections of profile pictures, known as Facepiles, instead of the full setences.
19 |
20 | Semantic Linkbacks uses [Microformats 2](http://microformats.org/wiki/microformats2) to get information about the linked post and it is highly extensible to also add support for other forms of markup.
21 |
22 | == Privacy and Data Collection ==
23 |
24 | This plugin collects data from sites that send webmentions for the purpose of displaying richer comments on a site. This data is under the control of the site owner. It is the personal responsibility of that individual or individuals to remove any information at the request of the original content creator. Over time, we will add additional tools to assist in doing so.
25 |
26 | == Frequently Asked Questions ==
27 |
28 | = Do I need to mark up my site? =
29 |
30 | Most modern WordPress themes support the older Microformats standard, which means the plugin should be able to get basic information from to enhance linkbacks. The plugin is most useful with the [WordPress Webmention plugin](https://wordpress.org/plugins/webmention/) and sites/themes that support Microformats 2.
31 |
32 | = Why Webmentions? =
33 |
34 | [Webmention](http://indiewebcamp.com/webmention) is a modern reimplementation of Pingback and is now a W3C Recommendation.
35 |
36 | = What about the semantic "comment" types? =
37 |
38 | The IndieWeb community defines several types of feedback:
39 |
40 | * Replies:
41 | * Reposts:
42 | * Likes:
43 | * Favorites:
44 | * RSVPs:
45 | * Tagging:
46 | * Listen:
47 | * Watch:
48 | * Read:
49 | * Follow:
50 | * Classic "Mentions":
51 |
52 | = How do I extend this plugin? =
53 |
54 | See [Extensions](https://indieweb.org/Semantic_Linkbacks#Extensions)
55 |
56 | = How do I add this into my plugin? =
57 |
58 | The plugin will automatically enhance webmentions, trackbacks, and pingbacks with an avatar and additional context. It will also automatically add a facepile instead of individual comments, but this feature can either be turned off by an aware theme or under Discussion in your Settings.
59 |
60 | = Why do some [emoji reactions](https://indieweb.org/reacji) not show up? =
61 |
62 | Some emoji characters in webmentions you might receive, e.g. Facebook reactions from [Bridgy](https://brid.gy/), take more than two bytes to encode. (In technical terms, these Unicode characters are [above the Basic Multilingual Plane](https://en.wikipedia.org/wiki/Plane_(Unicode)).) To handle them, you need MySQL 5.5.3 or higher, and your database and tables need to use the [`utf8mb4` charset](https://dev.mysql.com/doc/refman/5.7/en/charset-mysql.html). [Usually WordPress does this automatically](https://make.wordpress.org/core/2015/04/02/the-utf8mb4-upgrade/), but not always.
63 |
64 | First, [follow these instructions](https://wordpress.stackexchange.com/questions/195046/relaunch-4-2-utf8mb4-databse-upgrade/244992#244992) to switch your MySQL database to `utf8mb4`. Then, make sure `DB_CHARSET` and `DB_COLLATE` in your `wp-config.php` are either unset, set to the blank string, or set to these values:
65 |
66 | define('DB_CHARSET', 'utf8mb4');
67 | define('DB_COLLATE', 'utf8mb4_general_ci');
68 |
69 | = Who made the logos? =
70 |
71 | The Webmention and Pingback logos are made by [Aaron Parecki](http://aaronparecki.com) and the Microformats logo is made by [Dan Cederholm](http://simplebits.com/work/microformats/).
72 |
73 | = Why are you providing avatars? =
74 |
75 | The plugin attempts to store the URL to an actual profile image on the source site. The default avatar set by WordPress is only used if there is no such image found.
76 |
77 | Even the WordPress default avatars are served by querying Gravatar.com which serves the file. Gravatar works by you providing an email address which it uses to match the image.
78 | The majority of linkbacks enhanced by this plugin do not have email addresses therefore we know that gravatar will not have anything on file.
79 |
80 | If there is no email address it will serve the local avatar. If there is an email, it will go out to gravatar.com and see if they
81 | have a gravatar on file. If there is it will store the gravatar URL, otherwise it will store the URL for the local avatar if set.
82 |
83 | The plugin uses a locally cached version of the mystery icon normally provided by WordPress and Gravatar.
84 |
85 | == Changelog ==
86 |
87 | Project actively developed on Github at [pfefferle/wordpress-semantic-linkbacks](https://github.com/pfefferle/wordpress-semantic-linkbacks). Please file support issues there.
88 |
89 | = 3.11.3 =
90 |
91 | * Deactivate itself if Webmention Version is 5.0.0 or above
92 |
93 | = 3.11.2 =
94 |
95 | * Fix array access issue
96 | * Fix coding standards
97 |
98 | = 3.11.1 =
99 |
100 | * Fix HTML issue
101 |
102 | = 3.11.0 =
103 |
104 | * Fix AMP compatibility. Props to [Milind More](https://github.com/milindmore22).
105 |
106 | = 3.10.4 =
107 |
108 | * Whitelist `vendor` folder
109 |
110 | = 3.10.3 =
111 |
112 | * Remove jQuery dependency. Props to [Florian Brinkmann](https://github.com/florianbrinkmann).
113 | * Fix Facepile errors. Props to [Terence Eden](https://github.com/edent).
114 | * Remove refbacks as a default. Add a filter to decide on what comment types would be used.
115 | * Use `comment` as default comment-type: https://core.trac.wordpress.org/ticket/49236
116 |
117 | = 3.10.2 =
118 |
119 | * Fix default value of `semantic_linkbacks_facepiles` (props @prtksxna)
120 |
121 | = 3.10.1 =
122 |
123 | * Fix load issues with Webmention vs Semantic Linkbacks
124 |
125 | = 3.10.0 =
126 |
127 | * Use Webmentions avatar-handler if available
128 |
129 | = 3.9.3 =
130 |
131 | * Fixes https://github.com/pfefferle/wordpress-semantic-linkbacks/issues/41
132 |
133 | = 3.9.2 =
134 |
135 | * Only load MF2 parser inside parsing function to ensure bundled version is loaded
136 | * Update development dependencies
137 |
138 | = 3.9.1 =
139 |
140 | * Quick fix facepile problem id comments are closed
141 |
142 | = 3.9.0 =
143 |
144 | * Support rel-alternate (mf2-json)
145 | * New php-mf2 version
146 |
147 | = 3.8.1 =
148 |
149 | * Add follow post as type
150 | * Add warning to settings page if php-mbstring not installed
151 | * Return false in Emoji function if php-mbstring not installed
152 | * Add approve link to emails sent
153 |
154 | = 3.8.0 =
155 |
156 | * Add locally hosted copy of the mystery man icon and serve it if there is no gravatar
157 | * Redo settings and settings page
158 | * Settings page to merge with Webmentions page if webmentions loaded
159 |
160 | = 3.7.7 =
161 |
162 | * Add read type
163 | * Capture read-status if available
164 | * Change string as previous tense was off
165 |
166 | = 3.7.6 =
167 |
168 | * Update Parser to version 0.4.3
169 | * Introduce watch and listen properties
170 |
171 | = 3.7.5 =
172 |
173 | * fixed Reacji UI ((#154)[https://github.com/pfefferle/wordpress-semantic-linkbacks/issues/154])
174 |
175 | = 3.7.4 =
176 |
177 | * Replace `rsvp-invite` property which is not in use with `invite` property and add unit tests
178 | * Enhance post type returns to include post, page, and sitename
179 | * Add basic person tagging support
180 |
181 | = 3.7.3 =
182 |
183 | * Replace tracking with interested property as noted on https://indieweb.org/rsvp
184 | * Remove `h-as` properties
185 | * Remove hard-coded microformats2 properties from facepile and move them to being generated from comment_class
186 | * Remove unused properties
187 | * Introduce type argument in list_linkbacks to generate unique ideas for each list of linkbacks without having to specify them using style and li-class
188 | * Whitelist property swarm-coins, used by [OwnYourSwarm](https://ownyourswarm.p3k.io/docs#coins) and display it if using built-in comment handler.
189 |
190 | = 3.7.2 =
191 |
192 | * Bugfix: "Normal comments" hidden in comment-section (https://github.com/pfefferle/wordpress-semantic-linkbacks/issues/140)
193 |
194 | = 3.7.1 =
195 |
196 | * Fixed reacjis and facepiles
197 |
198 | = 3.7.0 =
199 |
200 | * Add settings to enable each type independently in the Facepile
201 | * Optionally render mentions as normal comments again
202 | * Support Reacji...aka single-emoji reactions
203 | * Bump minimum PHP to 5.4 due emoji detector library dependency issues
204 | * Overlay emoji on individual avatars in reactions facepile
205 | * Offer mf2 compatible template for comments
206 | * Fix semantic_linkbacks_cite filter as was previously filtering the entire comment text
207 | * Switch semantic_links_cite filter to filtering the format for the citation instead of the prepared citation
208 | * Count correct text length for unicode characters
209 | * Facepile Template improvements
210 | * Allow new comment template to be overridden by filter or theme declaring microformats2 support
211 | * Code standards compliance changes
212 | * Improved testing for PHP versions 5.4 and up to ensure compatibility
213 | * Remove direct calls to comment meta in favor of helper functions to ensure future proofing
214 |
215 | = 3.6.0 =
216 |
217 | * Only show the first 8 avatars in a facepile by default. If there are more, include a clickable ellipsis to show the rest. Customizable via the `FACEPILE_FOLD_LIMIT` constant.
218 | * Link facepile avatars to user profile/home page, not response post
219 | * Always show avatar images with correct aspect ratio
220 |
221 | = 3.5.1 =
222 |
223 | * Bugfix release
224 |
225 | = 3.5.0 =
226 |
227 | * Add Facepile code
228 | * Add setting to disable automatic facepile include
229 | * Add filter to allow themes to disable the setting and the feature if they facepile themselves
230 | * Add PHP requirement to readme file
231 |
232 | = 3.4.1 =
233 |
234 | * Abstract out linkback retrieval functions to allow for easier changes in future
235 | * Fix retrieval issue
236 | * Remove merge and compatibility function creating double slashing due update in 4.7.1
237 | * Replace blacklist for properties with whitelist for select properties
238 | * Update avatar function to not override if user_id is set on assumption local overrides remote
239 |
240 | = 3.4.0 =
241 |
242 | * Fix Tests and Error in Authorship
243 | * Update Parser
244 | * Switch to looser restrictions if WP_DEBUG is enabled and stricter ones otherwise
245 | * Enhance Author Properties to allow for retrieving remote h-card
246 | * Store mf2 properties
247 | * Store location in WordPress Geodata
248 | * Use rel-syndication if not u-syndication
249 | * Support new webmention source meta key
250 |
251 | = 3.3.1 =
252 |
253 | * fixed https://github.com/pfefferle/wordpress-semantic-linkbacks/issues/68
254 |
255 | = 3.3.0 =
256 |
257 | * Due to changes in WordPress 4.4 through 4.7 and version 3.0.0 of the Webmentions plugin this plugin can act on the retrieved remote source
258 | rather than rerequesting this information.
259 | * Major enhancement work is done in preprocessing now rather than post-processing
260 | * Refactoring
261 | * Render full mention content if short enough. Introduce MAX_INLINE_MENTION_LENGTH which defaults to 300 characters to implement same.
262 | * Fix text domain
263 |
264 | = 3.2.1 =
265 |
266 | * updated hooks/filters
267 |
268 | = 3.2.0 =
269 |
270 | * changed hook from `_post` to `comment_post` (thanks to @dshanske)
271 | * used the WordPress Coding Standard
272 | * small code improvements
273 |
274 | = 3.1.0 =
275 | * I18n support
276 | * German translation
277 | * some small changes and bugfixes
278 |
279 | = 3.0.5 =
280 |
281 | * quick fix to prevent crash if Mf2 lib is used by a second plugin
282 |
283 | = 3.0.4 =
284 |
285 | * added counter functions for comments by type (props to David Shanske)
286 | * some bugfixes
287 |
288 | = 3.0.3 =
289 |
290 | * some small tweaks
291 | * added custom comment classes based on the linkback-type (props to David Shanske for the idea)
292 |
293 | = 3.0.2 =
294 |
295 | * added support for threaded comments
296 |
297 | = 3.0.1 =
298 |
299 | * fixed bug in comments section
300 |
301 | = 3.0.0 =
302 |
303 | * nicer integration with trackbacks, linkbacks and webmentions
304 | * cleanup
305 |
306 | = 2.0.1 =
307 |
308 | * "via" links for indieweb "reply"s (thanks to @snarfed for the idea)
309 | * simplified output for all other indieweb "comment" types
310 | * better parser (thanks to voxpelly for his test-pinger)
311 | * now ready to use in a bundle
312 |
313 | = 2.0.0 =
314 |
315 | * initial release
316 |
317 | == Thanks to ==
318 |
319 | * Pelle Wessman ([@voxpelli](https://github.com/voxpelli)) for his awesome [WebMention test-pinger](https://github.com/voxpelli/node-webmention-testpinger)
320 | * Ryan Barrett ([@snarfed](https://github.com/snarfed)) for his feedback and pull requests
321 | * Barnaby Walters ([@barnabywalters](https://github.com/barnabywalters)) for his awesome [mf2 parser](https://github.com/indieweb/php-mf2)
322 | * David Shanske ([@dshanske](https://github.com/dshanske)) for his feedback and a lot of pull requests
323 | * ([@acegiak](https://github.com/acegiak)) for the initial plugin
324 |
325 | == Installation ==
326 |
327 | 1. Upload the `semantic-linkbacks`-folder to the `/wp-content/plugins/` directory
328 | 2. Activate the plugin through the *Plugins* menu in WordPress
329 | 3. ...and that's it :)
330 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | The plugin was merged into the [Webmention plugin](https://github.com/pfefferle/wordpress-webmention).
4 |
5 | ----
6 |
7 | # Semantic-Linkbacks #
8 | **Contributors:** [pfefferle](https://profiles.wordpress.org/pfefferle/), [dshanske](https://profiles.wordpress.org/dshanske/), [edent](https://profiles.wordpress.org/edent/)
9 | **Donate link:** https://notiz.blog/donate/
10 | **Tags:** webmention, pingback, trackback, linkback, microformats, comments, indieweb
11 | **Requires at least:** 4.9
12 | **Requires PHP:** 5.6
13 | **Tested up to:** 6.1
14 | **Stable tag:** 3.11.3
15 | **License:** MIT
16 | **License URI:** http://opensource.org/licenses/MIT
17 |
18 | Richer Comments and Linkbacks for WordPress!
19 |
20 | ## Description ##
21 |
22 | Generates richer WordPress comments from linkbacks such as [Webmention](https://wordpress.org/plugins/webmention) or classic linkback protocols like Trackback or Pingback.
23 |
24 | The limited display for trackbacks and linkbacks is replaced by a clean full sentence, such as "Bob mentioned this article on bob.com." If Bob's site uses markup that the plugin can interpret, it may add his profile picture or other parts of his page to display as a full comment. It will optionally show collections of profile pictures, known as Facepiles, instead of the full setences.
25 |
26 | Semantic Linkbacks uses [Microformats 2](http://microformats.org/wiki/microformats2) to get information about the linked post and it is highly extensible to also add support for other forms of markup.
27 |
28 | ## Privacy and Data Collection ##
29 |
30 | This plugin collects data from sites that send webmentions for the purpose of displaying richer comments on a site. This data is under the control of the site owner. It is the personal responsibility of that individual or individuals to remove any information at the request of the original content creator. Over time, we will add additional tools to assist in doing so.
31 |
32 | ## Frequently Asked Questions ##
33 |
34 | ### Do I need to mark up my site? ###
35 |
36 | Most modern WordPress themes support the older Microformats standard, which means the plugin should be able to get basic information from to enhance linkbacks. The plugin is most useful with the [WordPress Webmention plugin](https://wordpress.org/plugins/webmention/) and sites/themes that support Microformats 2.
37 |
38 | ### Why Webmentions? ###
39 |
40 | [Webmention](http://indiewebcamp.com/webmention) is a modern reimplementation of Pingback and is now a W3C Recommendation.
41 |
42 | ### What about the semantic "comment" types? ###
43 |
44 | The IndieWeb community defines several types of feedback:
45 |
46 | * Replies:
47 | * Reposts:
48 | * Likes:
49 | * Favorites:
50 | * RSVPs:
51 | * Tagging:
52 | * Listen:
53 | * Watch:
54 | * Read:
55 | * Follow:
56 | * Classic "Mentions":
57 |
58 | ### How do I extend this plugin? ###
59 |
60 | See [Extensions](https://indieweb.org/Semantic_Linkbacks#Extensions)
61 |
62 | ### How do I add this into my plugin? ###
63 |
64 | The plugin will automatically enhance webmentions, trackbacks, and pingbacks with an avatar and additional context. It will also automatically add a facepile instead of individual comments, but this feature can either be turned off by an aware theme or under Discussion in your Settings.
65 |
66 | ### Why do some [emoji reactions](https://indieweb.org/reacji) not show up? ###
67 |
68 | Some emoji characters in webmentions you might receive, e.g. Facebook reactions from [Bridgy](https://brid.gy/), take more than two bytes to encode. (In technical terms, these Unicode characters are [above the Basic Multilingual Plane](https://en.wikipedia.org/wiki/Plane_(Unicode)).) To handle them, you need MySQL 5.5.3 or higher, and your database and tables need to use the [`utf8mb4` charset](https://dev.mysql.com/doc/refman/5.7/en/charset-mysql.html). [Usually WordPress does this automatically](https://make.wordpress.org/core/2015/04/02/the-utf8mb4-upgrade/), but not always.
69 |
70 | First, [follow these instructions](https://wordpress.stackexchange.com/questions/195046/relaunch-4-2-utf8mb4-databse-upgrade/244992#244992) to switch your MySQL database to `utf8mb4`. Then, make sure `DB_CHARSET` and `DB_COLLATE` in your `wp-config.php` are either unset, set to the blank string, or set to these values:
71 |
72 | define('DB_CHARSET', 'utf8mb4');
73 | define('DB_COLLATE', 'utf8mb4_general_ci');
74 |
75 | ### Who made the logos? ###
76 |
77 | The Webmention and Pingback logos are made by [Aaron Parecki](http://aaronparecki.com) and the Microformats logo is made by [Dan Cederholm](http://simplebits.com/work/microformats/).
78 |
79 | ### Why are you providing avatars? ###
80 |
81 | The plugin attempts to store the URL to an actual profile image on the source site. The default avatar set by WordPress is only used if there is no such image found.
82 |
83 | Even the WordPress default avatars are served by querying Gravatar.com which serves the file. Gravatar works by you providing an email address which it uses to match the image.
84 | The majority of linkbacks enhanced by this plugin do not have email addresses therefore we know that gravatar will not have anything on file.
85 |
86 | If there is no email address it will serve the local avatar. If there is an email, it will go out to gravatar.com and see if they
87 | have a gravatar on file. If there is it will store the gravatar URL, otherwise it will store the URL for the local avatar if set.
88 |
89 | The plugin uses a locally cached version of the mystery icon normally provided by WordPress and Gravatar.
90 |
91 | ## Changelog ##
92 |
93 | Project actively developed on Github at [pfefferle/wordpress-semantic-linkbacks](https://github.com/pfefferle/wordpress-semantic-linkbacks). Please file support issues there.
94 |
95 | ### 3.11.3 ###
96 |
97 | * Deactivate itself if Webmention Version is 5.0.0 or above
98 |
99 | ### 3.11.2 ###
100 |
101 | * Fix array access issue
102 | * Fix coding standards
103 |
104 | ### 3.11.1 ###
105 |
106 | * Fix HTML issue
107 |
108 | ### 3.11.0 ###
109 |
110 | * Fix AMP compatibility. Props to [Milind More](https://github.com/milindmore22).
111 |
112 | ### 3.10.4 ###
113 |
114 | * Whitelist `vendor` folder
115 |
116 | ### 3.10.3 ###
117 |
118 | * Remove jQuery dependency. Props to [Florian Brinkmann](https://github.com/florianbrinkmann).
119 | * Fix Facepile errors. Props to [Terence Eden](https://github.com/edent).
120 | * Remove refbacks as a default. Add a filter to decide on what comment types would be used.
121 | * Use `comment` as default comment-type: https://core.trac.wordpress.org/ticket/49236
122 |
123 | ### 3.10.2 ###
124 |
125 | * Fix default value of `semantic_linkbacks_facepiles` (props @prtksxna)
126 |
127 | ### 3.10.1 ###
128 |
129 | * Fix load issues with Webmention vs Semantic Linkbacks
130 |
131 | ### 3.10.0 ###
132 |
133 | * Use Webmentions avatar-handler if available
134 |
135 | ### 3.9.3 ###
136 |
137 | * Fixes https://github.com/pfefferle/wordpress-semantic-linkbacks/issues/41
138 |
139 | ### 3.9.2 ###
140 |
141 | * Only load MF2 parser inside parsing function to ensure bundled version is loaded
142 | * Update development dependencies
143 |
144 | ### 3.9.1 ###
145 |
146 | * Quick fix facepile problem id comments are closed
147 |
148 | ### 3.9.0 ###
149 |
150 | * Support rel-alternate (mf2-json)
151 | * New php-mf2 version
152 |
153 | ### 3.8.1 ###
154 |
155 | * Add follow post as type
156 | * Add warning to settings page if php-mbstring not installed
157 | * Return false in Emoji function if php-mbstring not installed
158 | * Add approve link to emails sent
159 |
160 | ### 3.8.0 ###
161 |
162 | * Add locally hosted copy of the mystery man icon and serve it if there is no gravatar
163 | * Redo settings and settings page
164 | * Settings page to merge with Webmentions page if webmentions loaded
165 |
166 | ### 3.7.7 ###
167 |
168 | * Add read type
169 | * Capture read-status if available
170 | * Change string as previous tense was off
171 |
172 | ### 3.7.6 ###
173 |
174 | * Update Parser to version 0.4.3
175 | * Introduce watch and listen properties
176 |
177 | ### 3.7.5 ###
178 |
179 | * fixed Reacji UI ((#154)[https://github.com/pfefferle/wordpress-semantic-linkbacks/issues/154])
180 |
181 | ### 3.7.4 ###
182 |
183 | * Replace `rsvp-invite` property which is not in use with `invite` property and add unit tests
184 | * Enhance post type returns to include post, page, and sitename
185 | * Add basic person tagging support
186 |
187 | ### 3.7.3 ###
188 |
189 | * Replace tracking with interested property as noted on https://indieweb.org/rsvp
190 | * Remove `h-as` properties
191 | * Remove hard-coded microformats2 properties from facepile and move them to being generated from comment_class
192 | * Remove unused properties
193 | * Introduce type argument in list_linkbacks to generate unique ideas for each list of linkbacks without having to specify them using style and li-class
194 | * Whitelist property swarm-coins, used by [OwnYourSwarm](https://ownyourswarm.p3k.io/docs#coins) and display it if using built-in comment handler.
195 |
196 | ### 3.7.2 ###
197 |
198 | * Bugfix: "Normal comments" hidden in comment-section (https://github.com/pfefferle/wordpress-semantic-linkbacks/issues/140)
199 |
200 | ### 3.7.1 ###
201 |
202 | * Fixed reacjis and facepiles
203 |
204 | ### 3.7.0 ###
205 |
206 | * Add settings to enable each type independently in the Facepile
207 | * Optionally render mentions as normal comments again
208 | * Support Reacji...aka single-emoji reactions
209 | * Bump minimum PHP to 5.4 due emoji detector library dependency issues
210 | * Overlay emoji on individual avatars in reactions facepile
211 | * Offer mf2 compatible template for comments
212 | * Fix semantic_linkbacks_cite filter as was previously filtering the entire comment text
213 | * Switch semantic_links_cite filter to filtering the format for the citation instead of the prepared citation
214 | * Count correct text length for unicode characters
215 | * Facepile Template improvements
216 | * Allow new comment template to be overridden by filter or theme declaring microformats2 support
217 | * Code standards compliance changes
218 | * Improved testing for PHP versions 5.4 and up to ensure compatibility
219 | * Remove direct calls to comment meta in favor of helper functions to ensure future proofing
220 |
221 | ### 3.6.0 ###
222 |
223 | * Only show the first 8 avatars in a facepile by default. If there are more, include a clickable ellipsis to show the rest. Customizable via the `FACEPILE_FOLD_LIMIT` constant.
224 | * Link facepile avatars to user profile/home page, not response post
225 | * Always show avatar images with correct aspect ratio
226 |
227 | ### 3.5.1 ###
228 |
229 | * Bugfix release
230 |
231 | ### 3.5.0 ###
232 |
233 | * Add Facepile code
234 | * Add setting to disable automatic facepile include
235 | * Add filter to allow themes to disable the setting and the feature if they facepile themselves
236 | * Add PHP requirement to readme file
237 |
238 | ### 3.4.1 ###
239 |
240 | * Abstract out linkback retrieval functions to allow for easier changes in future
241 | * Fix retrieval issue
242 | * Remove merge and compatibility function creating double slashing due update in 4.7.1
243 | * Replace blacklist for properties with whitelist for select properties
244 | * Update avatar function to not override if user_id is set on assumption local overrides remote
245 |
246 | ### 3.4.0 ###
247 |
248 | * Fix Tests and Error in Authorship
249 | * Update Parser
250 | * Switch to looser restrictions if WP_DEBUG is enabled and stricter ones otherwise
251 | * Enhance Author Properties to allow for retrieving remote h-card
252 | * Store mf2 properties
253 | * Store location in WordPress Geodata
254 | * Use rel-syndication if not u-syndication
255 | * Support new webmention source meta key
256 |
257 | ### 3.3.1 ###
258 |
259 | * fixed https://github.com/pfefferle/wordpress-semantic-linkbacks/issues/68
260 |
261 | ### 3.3.0 ###
262 |
263 | * Due to changes in WordPress 4.4 through 4.7 and version 3.0.0 of the Webmentions plugin this plugin can act on the retrieved remote source
264 | rather than rerequesting this information.
265 | * Major enhancement work is done in preprocessing now rather than post-processing
266 | * Refactoring
267 | * Render full mention content if short enough. Introduce MAX_INLINE_MENTION_LENGTH which defaults to 300 characters to implement same.
268 | * Fix text domain
269 |
270 | ### 3.2.1 ###
271 |
272 | * updated hooks/filters
273 |
274 | ### 3.2.0 ###
275 |
276 | * changed hook from `_post` to `comment_post` (thanks to @dshanske)
277 | * used the WordPress Coding Standard
278 | * small code improvements
279 |
280 | ### 3.1.0 ###
281 | * I18n support
282 | * German translation
283 | * some small changes and bugfixes
284 |
285 | ### 3.0.5 ###
286 |
287 | * quick fix to prevent crash if Mf2 lib is used by a second plugin
288 |
289 | ### 3.0.4 ###
290 |
291 | * added counter functions for comments by type (props to David Shanske)
292 | * some bugfixes
293 |
294 | ### 3.0.3 ###
295 |
296 | * some small tweaks
297 | * added custom comment classes based on the linkback-type (props to David Shanske for the idea)
298 |
299 | ### 3.0.2 ###
300 |
301 | * added support for threaded comments
302 |
303 | ### 3.0.1 ###
304 |
305 | * fixed bug in comments section
306 |
307 | ### 3.0.0 ###
308 |
309 | * nicer integration with trackbacks, linkbacks and webmentions
310 | * cleanup
311 |
312 | ### 2.0.1 ###
313 |
314 | * "via" links for indieweb "reply"s (thanks to @snarfed for the idea)
315 | * simplified output for all other indieweb "comment" types
316 | * better parser (thanks to voxpelly for his test-pinger)
317 | * now ready to use in a bundle
318 |
319 | ### 2.0.0 ###
320 |
321 | * initial release
322 |
323 | ## Thanks to ##
324 |
325 | * Pelle Wessman ([@voxpelli](https://github.com/voxpelli)) for his awesome [WebMention test-pinger](https://github.com/voxpelli/node-webmention-testpinger)
326 | * Ryan Barrett ([@snarfed](https://github.com/snarfed)) for his feedback and pull requests
327 | * Barnaby Walters ([@barnabywalters](https://github.com/barnabywalters)) for his awesome [mf2 parser](https://github.com/indieweb/php-mf2)
328 | * David Shanske ([@dshanske](https://github.com/dshanske)) for his feedback and a lot of pull requests
329 | * ([@acegiak](https://github.com/acegiak)) for the initial plugin
330 |
331 | ## Installation ##
332 |
333 | 1. Upload the `semantic-linkbacks`-folder to the `/wp-content/plugins/` directory
334 | 2. Activate the plugin through the *Plugins* menu in WordPress
335 | 3. ...and that's it :)
336 |
--------------------------------------------------------------------------------
/tests/templates/aaronparecki-com.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | @adactio Crossing my fingers that this post makes it! #indiewebcampuk #webmention - Aaron Parecki
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
63 |
64 |
65 |
66 |
67 |
68 |
75 |
88 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
115 |
116 |
132 |
133 |
148 |
149 |
150 |
151 |
152 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
224 |
225 |
226 |
227 |
228 |
283 |
284 |
285 |
286 |
287 |
--------------------------------------------------------------------------------
/tests/templates/sandeep-io.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 | sandeep.io
9 |
10 |
11 |
12 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
22 |
25 |
26 |
28 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
50 |
51 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | What idno is
87 |
88 | This site runs on idno: an open source social publishing platform that I've been working on for the past few months in my own time...
89 |
90 |
91 |
# indieweb # idno
92 |
93 |
Liked [What idno is](http://werd.io/entry/51d3c98cbed7de6715d8fd15/what-idno-is)
94 | #indieweb #idno
95 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
118 |
119 |
120 |
121 |
122 |
Likes
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
Reposts
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
Mentions
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
153 |
154 |
155 |
306 |
307 |
308 |
310 |
311 |
312 |
313 |
314 |
356 |
357 |
358 |
359 |
--------------------------------------------------------------------------------
/includes/class-linkbacks-mf2-handler.php:
--------------------------------------------------------------------------------
1 | parse( true );
149 |
150 | // check for rel-alternate links
151 | $alternate_source = self::get_alternate_source( $mf_array );
152 | if ( $alternate_source ) {
153 | $mf_array = $alternate_source;
154 | }
155 |
156 | // get all 'relevant' entries
157 | $entries = self::get_entries( $mf_array );
158 |
159 | if ( empty( $entries ) ) {
160 | return $commentdata;
161 | }
162 |
163 | // get the entry of interest
164 | $entry = self::get_representative_entry( $entries, $commentdata['target'] );
165 |
166 | if ( empty( $entry ) ) {
167 | return $commentdata;
168 | }
169 |
170 | $commentdata['remote_source_mf2'] = $entry;
171 | $properties = array_filter( self::flatten_microformats( $entry ) );
172 | $commentdata['remote_source_properties'] = $properties;
173 | $rels = $mf_array['rels'];
174 | $commentdata['remote_source_rels'] = $rels;
175 |
176 | // try to find some content
177 | // @link http://indieweb.org/comments-presentation
178 | if ( isset( $properties['summary'] ) ) {
179 | $commentdata['comment_content'] = wp_slash( $properties['summary'] );
180 | } elseif ( isset( $properties['content'] ) ) {
181 | $commentdata['comment_content'] = wp_filter_kses( $properties['content']['html'] );
182 | } elseif ( isset( $properties['name'] ) ) {
183 | $commentdata['comment_content'] = wp_slash( $properties['name'] );
184 | }
185 | $commentdata['comment_content'] = trim( $commentdata['comment_content'] );
186 |
187 | // set the right date
188 | if ( isset( $properties['published'] ) ) {
189 | $commentdata['comment_date'] = self::convert_time( $properties['published'] );
190 | } elseif ( isset( $properties['updated'] ) ) {
191 | $commentdata['comment_date'] = self::convert_time( $properties['updated'] );
192 | }
193 | $commentdata['comment_date_gmt'] = get_gmt_from_date( $commentdata['comment_date'] );
194 |
195 | $author = null;
196 |
197 | // check if h-card has an author
198 | if ( isset( $properties['author'] ) ) {
199 | $author = $properties['author'];
200 | } else {
201 | $author = self::get_representative_author( $mf_array, $source );
202 | $author = self::flatten_microformats( $author );
203 | }
204 | // If this is an invite than the invite should be displayed instead of the author
205 | if ( isset( $properties['invitee'] ) ) {
206 | $author = $properties['invitee'];
207 | }
208 |
209 | // if author is present use the informations for the comment
210 | if ( $author ) {
211 | if ( ! is_array( $author ) ) {
212 | if ( self::is_url( $author ) ) {
213 | $response = Linkbacks_Handler::retrieve( $author );
214 | if ( ! is_wp_error( $response ) ) {
215 | $parser = new Parser( wp_remote_retrieve_body( $response ), $author );
216 | $author_array = $parser->parse( true );
217 | $author = self::flatten_microformats( self::get_representative_author( $author_array, $author ) );
218 | $properties['author'] = $author;
219 | }
220 | } else {
221 | $commentdata['comment_author'] = wp_slash( $author );
222 | }
223 | }
224 |
225 | if ( is_array( $author ) ) {
226 | if ( ! isset( $author['me'] ) ) {
227 | if ( isset( $mf_array['rels']['me'] ) ) {
228 | $author['me'] = $mf_array['rels']['me'];
229 | $properties['author']['me'] = $author['me'];
230 | }
231 | }
232 | if ( isset( $author['name'] ) ) {
233 | $commentdata['comment_author'] = wp_slash( self::first( $author['name'] ) );
234 | }
235 |
236 | if ( isset( $author['email'] ) ) {
237 | $commentdata['comment_author_email'] = wp_slash( self::first( $author['email'] ) );
238 | }
239 |
240 | if ( isset( $author['url'] ) ) {
241 | $commentdata['comment_meta']['semantic_linkbacks_author_url'] = self::first( $author['url'] );
242 | }
243 |
244 | if ( isset( $author['photo'] ) ) {
245 | $commentdata['comment_meta']['avatar'] = self::first( $author['photo'] );
246 | }
247 | }
248 | }
249 |
250 | // set canonical url (u-url)
251 | if ( isset( $properties['url'] ) ) {
252 | $commentdata['comment_meta']['semantic_linkbacks_canonical'] = self::first( $properties['url'] );
253 | // If the source URL and the canonical URL are not on the same domain, set this flag in the comment data to trigger actions in the Linkbacks_Handler class
254 | if ( wp_parse_url( $properties['url'], PHP_URL_HOST ) !== wp_parse_url( $source, PHP_URL_HOST ) ) {
255 | $commentdata['proxy_mention'] = 1;
256 | }
257 | } else {
258 | $commentdata['comment_meta']['semantic_linkbacks_canonical'] = esc_url_raw( $source );
259 | }
260 |
261 | // If u-syndication is not set use rel syndication
262 | if ( array_key_exists( 'syndication', $rels ) && ! array_key_exists( 'syndication', $properties ) ) {
263 | $properties['syndication'] = $rels['syndication'];
264 | }
265 |
266 | // Check and parse for location property
267 | if ( array_key_exists( 'location', $properties ) ) {
268 | $location = $properties['location'];
269 | if ( is_array( $location ) ) {
270 | if ( array_key_exists( 'latitude', $location ) ) {
271 | $commentdata['comment_meta']['geo_latitude'] = self::first( $location['latitude'] );
272 | }
273 | if ( array_key_exists( 'longitude', $location ) ) {
274 | $commentdata['comment_meta']['geo_longitude'] = self::first( $location['longitude'] );
275 | }
276 | if ( array_key_exists( 'name', $location ) ) {
277 | $commentdata['comment_meta']['geo_address'] = self::first( $location['name'] );
278 | }
279 | } else {
280 | if ( substr( $location, 0, 4 ) === 'geo:' ) {
281 | $geo = explode( ':', substr( urldecode( $location ), 4 ) );
282 | $geo = explode( ';', $geo[0] );
283 | $coords = explode( ',', $geo[0] );
284 | $commentdata['comment_meta']['geo_latitude'] = trim( $coords[0] );
285 | $commentdata['comment_meta']['geo_longitude'] = trim( $coords[1] );
286 | } else {
287 | $commentdata['comment_meta']['geo_address'] = $location;
288 | }
289 | }
290 | }
291 |
292 | // check rsvp property
293 | if ( isset( $properties['rsvp'] ) ) {
294 | $commentdata['comment_meta']['semantic_linkbacks_type'] = wp_slash( 'rsvp:' . self::first( $properties['rsvp'] ) );
295 | } else {
296 | // get post type
297 | $commentdata['comment_meta']['semantic_linkbacks_type'] = wp_slash( self::get_entry_type( $commentdata['target'], $entry, $mf_array ) );
298 | }
299 | if ( isset( $properties['invitee'] ) ) {
300 | $commentdata['comment_meta']['semantic_linkbacks_type'] = wp_slash( 'invite' );
301 | }
302 |
303 | // Check for person tagging
304 | if ( isset( $properties['category'] ) ) {
305 | if ( in_array( $commentdata['target'], $properties['category'], true ) ) {
306 | $commentdata['category'] = array( $commentdata['target'] );
307 | }
308 | }
309 |
310 | $whitelist = array(
311 | 'author',
312 | 'location',
313 | 'syndication',
314 | 'uid',
315 | 'video',
316 | 'audio',
317 | 'photo',
318 | 'featured',
319 | 'swarm-coins', // https://ownyourswarm.p3k.io/docs#coins
320 | 'read-status', // https://indieweb.org/read
321 | );
322 |
323 | // Add in supported properties
324 | $whitelist = array_merge( $whitelist, array_keys( self::get_class_mapper() ) );
325 | $whitelist = apply_filters( 'semantic_linkbacks_mf2_props_whitelist', $whitelist );
326 | foreach ( $properties as $key => $value ) {
327 | if ( in_array( $key, $whitelist, true ) ) {
328 | if ( self::is_url( $value ) ) {
329 | $value = esc_url_raw( $value );
330 | }
331 | if ( is_string( $value ) ) {
332 | $value = wp_kses_post( $value );
333 | }
334 | $commentdata['comment_meta'][ 'mf2_' . $key ] = $value;
335 | }
336 | }
337 | $commentdata['comment_meta'] = array_filter( $commentdata['comment_meta'] );
338 |
339 | return $commentdata;
340 | }
341 |
342 | public static function convert_time( $time ) {
343 | $time = strtotime( $time );
344 | // If it can't read the time it will return null which will mean the comment time will be set to now.
345 | if ( $time ) {
346 | return get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $time ), 'Y-m-d H:i:s' );
347 | }
348 | return null;
349 | }
350 |
351 | public static function get_property( $key, $properties ) {
352 | if ( isset( $properties[ $key ] ) && isset( $properties[ $key ][0] ) ) {
353 | if ( is_array( $properties[ $key ] ) ) {
354 | $properties[ $key ] = array_unique( $properties[ $key ] );
355 | }
356 | if ( 1 === count( $properties[ $key ] ) ) {
357 | return $properties[ $key ][0];
358 | }
359 | return $properties[ $key ];
360 | }
361 | return null;
362 | }
363 |
364 | /**
365 | * Returns the first item in $val if it's a non-empty array, otherwise $val itself.
366 | */
367 | public static function first( $val ) {
368 | if ( $val && is_array( $val ) ) {
369 | return $val[0];
370 | }
371 | return $val;
372 | }
373 |
374 | /**
375 | * Is string a URL.
376 | *
377 | * @param array $string
378 | * @return bool
379 | */
380 | public static function is_url( $string ) {
381 | if ( ! is_string( $string ) ) {
382 | return false;
383 | }
384 | // If debugging is on just validate that URL is validly formatted
385 | if ( WP_DEBUG ) {
386 | return filter_var( $string, FILTER_VALIDATE_URL ) !== false;
387 | }
388 | // If debugging is off limit based on WordPress parameters
389 | return wp_http_validate_url( $string );
390 | }
391 |
392 | // Accepted h types
393 | public static function is_h( $string ) {
394 | return in_array( $string, array( 'h-cite', 'h-entry', 'h-feed', 'h-product', 'h-event', 'h-review', 'h-recipe' ), true );
395 | }
396 |
397 | public static function flatten_microformats( $item ) {
398 | $flat = array();
399 | if ( 1 === count( $item ) ) {
400 | $item = $item[0];
401 | }
402 | if ( array_key_exists( 'type', $item ) ) {
403 | // If there are multiple types strip out everything but the standard one.
404 | if ( 1 < count( $item['type'] ) ) {
405 | $item['type'] = array_filter( $item['type'], array( 'Linkbacks_MF2_Handler', 'is_h' ) );
406 | }
407 | $flat['type'] = $item['type'][0];
408 | }
409 | if ( array_key_exists( 'properties', $item ) ) {
410 | $properties = $item['properties'];
411 | foreach ( $properties as $key => $value ) {
412 | $flat[ $key ] = self::get_property( $key, $properties );
413 |
414 | if ( ! is_string( $flat[ $key ] ) && 1 < count( $flat[ $key ] ) ) {
415 | $flat[ $key ] = self::flatten_microformats( $flat[ $key ] );
416 | }
417 | }
418 | } else {
419 | $flat = $item;
420 | }
421 | foreach ( $flat as $key => $value ) {
422 | // Sanitize all URL properties
423 | if ( self::is_url( $value ) ) {
424 | $flat[ $key ] = esc_url_raw( $value );
425 | }
426 | }
427 |
428 | // If name and URL are the same, remove name.
429 | if ( array_key_exists( 'name', $flat ) && array_key_exists( 'url', $flat ) ) {
430 | if ( $flat['name'] === $flat['url'] ) {
431 | unset( $flat['name'] );
432 | }
433 | }
434 |
435 | // Duplicate url values for a property may be caused by implied urls https://github.com/indieweb/php-mf2/issues/110
436 | if ( array_key_exists( 'url', $flat ) && is_array( $flat['url'] ) ) {
437 | $flat['url'] = $flat['url'][0];
438 | }
439 | $flat = array_filter( $flat );
440 | return $flat;
441 | }
442 |
443 | public static function has_alternate_url( $mf_array ) {
444 | return (bool) self::get_alternate_url( $mf_array );
445 | }
446 |
447 | public static function get_alternate_url( $mf_array ) {
448 | if ( ! array_key_exists( 'rel-urls', $mf_array ) ) {
449 | return false;
450 | }
451 |
452 | foreach ( $mf_array['rel-urls'] as $url => $meta ) {
453 | if (
454 | isset( $meta['type'] ) &&
455 | 'application/mf2+json' === trim( $meta['type'] ) &&
456 | isset( $meta['rels'] ) &&
457 | in_array( 'alternate', $meta['rels'], true ) &&
458 | filter_var( $url, FILTER_VALIDATE_URL ) !== false
459 | ) {
460 | return $url;
461 | }
462 | }
463 |
464 | return false;
465 | }
466 |
467 | public static function get_alternate_source( $mf_array ) {
468 | $url = self::get_alternate_url( $mf_array );
469 |
470 | if ( ! $url ) {
471 | return false;
472 | }
473 |
474 | $user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' ) );
475 | $args = array(
476 | 'timeout' => 100,
477 | 'limit_response_size' => 153600,
478 | 'redirection' => 20,
479 | 'user-agent' => "$user_agent; Semantic-Linkbacks/Webmention read rel-alternate source",
480 | );
481 |
482 | $response = wp_safe_remote_get( $url, $args );
483 | // check if source is accessible
484 | if ( is_wp_error( $response ) ) {
485 | return false;
486 | }
487 |
488 | $body = wp_remote_retrieve_body( $response );
489 |
490 | return json_decode( $body, true );
491 | }
492 |
493 | /**
494 | * get all h-entry items
495 | *
496 | * @param array $mf_array the microformats array
497 | * @param array the h-entry array
498 | *
499 | * @return array
500 | */
501 | public static function get_entries( $mf_array ) {
502 | $entries = array();
503 |
504 | // some basic checks
505 | if ( ! is_array( $mf_array ) ) {
506 | return $entries;
507 | }
508 | if ( ! isset( $mf_array['items'] ) ) {
509 | return $entries;
510 | }
511 | if ( 0 === count( $mf_array['items'] ) ) {
512 | return $entries;
513 | }
514 |
515 | // get first item
516 | $first_item = $mf_array['items'][0];
517 |
518 | // check if it is an h-feed
519 | if ( isset( $first_item['type'] ) &&
520 | in_array( 'h-feed', $first_item['type'], true ) &&
521 | isset( $first_item['children'] ) ) {
522 | $mf_array['items'] = $first_item['children'];
523 | }
524 |
525 | // iterate array
526 | foreach ( $mf_array['items'] as $mf ) {
527 | if ( isset( $mf['type'] ) && in_array( 'h-entry', $mf['type'], true ) ) {
528 | $entries[] = $mf;
529 | }
530 | }
531 |
532 | // return entries
533 | return $entries;
534 | }
535 |
536 | /**
537 | * helper to find the correct author node
538 | *
539 | * @param array $mf_array the parsed microformats array
540 | * @param string $source the source url
541 | *
542 | * @return array|null the h-card node or null
543 | */
544 | public static function get_representative_author( $mf_array, $source ) {
545 | foreach ( $mf_array['items'] as $mf ) {
546 | if ( isset( $mf['type'] ) ) {
547 | if ( in_array( 'h-card', $mf['type'], true ) ) {
548 | // check domain
549 | if ( isset( $mf['properties'] ) && isset( $mf['properties']['url'] ) ) {
550 | foreach ( $mf['properties']['url'] as $url ) {
551 | if ( wp_parse_url( $url, PHP_URL_HOST ) === wp_parse_url( $source, PHP_URL_HOST ) ) {
552 | if ( isset( $mf_array['rels']['me'] ) ) {
553 | $mf['properties']['me'] = $mf_array['rels']['me'];
554 | }
555 | return $mf;
556 | }
557 | }
558 | }
559 | }
560 | }
561 | }
562 |
563 | // If there is no h-card then return rel=author and see what can be done with it
564 | if ( isset( $mf_array['rels']['author'] ) ) {
565 | return $mf_array['rels']['author'];
566 | }
567 |
568 | return null;
569 | }
570 |
571 | /**
572 | * helper to find the correct h-entry node
573 | *
574 | * @param array $mf_array the parsed microformats array
575 | * @param string $target the target url
576 | *
577 | * @return array the h-entry node or false
578 | */
579 | public static function get_representative_entry( $entries, $target ) {
580 | // iterate array
581 | foreach ( $entries as $entry ) {
582 | // check properties
583 | if ( isset( $entry['properties'] ) ) {
584 | // check properties if target urls was mentioned
585 | foreach ( $entry['properties'] as $key => $values ) {
586 | // check "normal" links
587 | if ( self::compare_urls( $target, $values ) ) {
588 | return $entry;
589 | }
590 |
591 | // check included h-* formats and their links
592 | foreach ( $values as $obj ) {
593 | // check if reply is a "cite"
594 | if ( isset( $obj['type'] ) && array_intersect( array( 'h-cite', 'h-entry' ), $obj['type'] ) ) {
595 | // check url
596 | if ( isset( $obj['properties'] ) && isset( $obj['properties']['url'] ) ) {
597 | // check target
598 | if ( self::compare_urls( $target, $obj['properties']['url'] ) ) {
599 | return $entry;
600 | }
601 | }
602 | }
603 | }
604 | }
605 |
606 | // check properties if target urls was mentioned
607 | foreach ( $entry['properties'] as $key => $values ) {
608 | // check content for the link
609 | if ( 'content' === $key &&
610 | preg_match_all( '/]+?' . preg_quote( $target, '/' ) . '[^>]*>([^>]+?)<\/a>/i', $values[0]['html'], $context ) ) {
611 | return $entry;
612 | } elseif ( 'summary' === $key &&
613 | preg_match_all( '/ ]+?' . preg_quote( $target, '/' ) . '[^>]*>([^>]+?)<\/a>/i', $values[0], $context ) ) {
614 | return $entry;
615 | }
616 | }
617 | }
618 | }
619 |
620 | // return first h-entry
621 | return $entries[0];
622 | }
623 |
624 | /**
625 | * check entry classes or document rels for post-type
626 | *
627 | * @param string $target the target url
628 | * @param array $entry the represantative entry
629 | * @param array $mf_array the document
630 | *
631 | * @return string the post-type
632 | */
633 | public static function get_entry_type( $target, $entry, $mf_array = array() ) {
634 | $classes = self::get_class_mapper();
635 |
636 | // check properties for target-url
637 | foreach ( $entry['properties'] as $key => $values ) {
638 | // check u-* params
639 | if ( in_array( $key, array_keys( $classes ), true ) ) {
640 | // check "normal" links
641 | if ( self::compare_urls( $target, $values ) ) {
642 | return $classes[ $key ];
643 | }
644 |
645 | // iterate in-reply-tos
646 | foreach ( $values as $obj ) {
647 | // check if reply is a "cite" or "entry"
648 | if ( isset( $obj['type'] ) && array_intersect( array( 'h-cite', 'h-entry' ), $obj['type'] ) ) {
649 | // check url
650 | if ( isset( $obj['properties'] ) && isset( $obj['properties']['url'] ) ) {
651 | // check target
652 | if ( self::compare_urls( $target, $obj['properties']['url'] ) ) {
653 | return $classes[ $key ];
654 | }
655 | }
656 | }
657 | }
658 | }
659 | }
660 |
661 | // check if site has any rels
662 | if ( ! isset( $mf_array['rels'] ) ) {
663 | return 'mention';
664 | }
665 |
666 | $rels = self::get_rel_mapper();
667 |
668 | // check rels for target-url
669 | foreach ( $mf_array['rels'] as $key => $values ) {
670 | // check rel params
671 | if ( in_array( $key, array_keys( $rels ), true ) ) {
672 | foreach ( $values as $value ) {
673 | if ( $value === $target ) {
674 | return $rels[ $key ];
675 | }
676 | }
677 | }
678 | }
679 |
680 | return 'mention';
681 | }
682 |
683 | /**
684 | * compare an url with a list of urls
685 | *
686 | * @param string $needle the target url
687 | * @param array $haystack a list of urls
688 | * @param boolean $schemelesse define if the target url should be checked with http:// and https://
689 | *
690 | * @return boolean
691 | */
692 | public static function compare_urls( $needle, $haystack, $schemeless = true ) {
693 | if ( ! self::is_url( $needle ) ) {
694 | return false;
695 | }
696 | if ( is_array( reset( $haystack ) ) ) {
697 | return false;
698 | }
699 | if ( true === $schemeless ) {
700 | // remove url-scheme
701 | $schemeless_target = preg_replace( '/^https?:\/\//i', '', $needle );
702 |
703 | // add both urls to the needle
704 | $needle = array( 'http://' . $schemeless_target, 'https://' . $schemeless_target );
705 | } else {
706 | // make $needle an array
707 | $needle = array( $needle );
708 | }
709 |
710 | // compare both arrays
711 | return array_intersect( $needle, $haystack );
712 | }
713 | }
714 |
--------------------------------------------------------------------------------