├── views
├── kind-note.php
├── kind-article.php
├── kind-rsvp.php
├── kind-photo.php
├── kind-checkin.php
├── kind-event.php
├── kind-favorite.php
├── kind-drink.php
├── kind-like.php
├── kind-issue.php
├── kind-bookmark.php
├── kind-audio.php
├── kind-eat.php
├── kind-jam.php
├── kind-itinerary.php
├── kind-watch.php
├── kind-video.php
├── kind-read.php
├── kind-listen.php
└── kind.php
├── svgs
├── bookmark.svg
├── watch.svg
├── note.svg
├── like.svg
├── reply.svg
├── drink.svg
├── jam.svg
├── itinerary.svg
├── favorite.svg
├── sleep.svg
├── tag.svg
├── review.svg
├── issue.svg
├── read.svg
├── listen.svg
├── firehose.svg
├── play.svg
├── quote.svg
├── eat.svg
├── wish.svg
├── trip.svg
├── follow.svg
├── question.svg
├── photo.svg
├── rsvp.svg
├── acquisition.svg
├── recipe.svg
├── mood.svg
├── checkin.svg
├── repost.svg
├── craft.svg
├── event.svg
├── article.svg
├── video.svg
├── audio.svg
├── exercise.svg
└── weather.svg
├── .eslintrc.js
├── lib
└── parse-this
│ ├── lib
│ ├── html5
│ │ ├── HTML5
│ │ │ ├── Exception.php
│ │ │ ├── Parser
│ │ │ │ ├── ParseError.php
│ │ │ │ ├── FileInputStream.php
│ │ │ │ ├── CharacterReference.php
│ │ │ │ ├── README.md
│ │ │ │ ├── InputStream.php
│ │ │ │ ├── TreeBuildingRules.php
│ │ │ │ ├── EventHandler.php
│ │ │ │ └── UTF8Utils.php
│ │ │ ├── Serializer
│ │ │ │ ├── README.md
│ │ │ │ ├── RulesInterface.php
│ │ │ │ └── Traverser.php
│ │ │ └── InstructionProcessor.php
│ │ ├── autoloader.php
│ │ ├── UPGRADING.md
│ │ └── RELEASE.md
│ └── mf2
│ │ └── LICENSE.md
│ ├── includes
│ ├── autoload.php
│ ├── class-parse-this-json.php
│ ├── class-parse-this-youtube.php
│ ├── class-parse-this-opml.php
│ ├── class-parse-this-twitter.php
│ ├── class-parse-this-jsonfeed.php
│ ├── class-rest-parse-this.php
│ ├── class-parse-this-instagram.php
│ ├── compat-functions.php
│ └── class-parse-this-base.php
│ ├── parse-this.php
│ ├── readme.md
│ └── readme.txt
├── .distignore
├── css
├── kind.min.css.map
├── kind.css.map
├── kind.admin.min.css.map
├── kind.min.css
├── kind.admin.min.css
└── kind.css
├── .editorconfig
├── develop.txt
├── templates
├── reply-author.php
├── reply-details.php
├── reply-time.php
├── reply-metabox.php
├── feed-atom.php
├── feed-rss2.php
├── feed-rss2-comments.php
└── feed-atom-comments.php
├── js
├── clone-media-fragment.js
└── citation.js
├── includes
├── class-post-kind.php
├── compat.php
├── class-kind-media-view.php
├── class-kind-post-widget.php
├── class-kind-menu-widget.php
├── kind-functions.php
└── class-kind-plugins.php
└── fontawesome-license.txt
/views/kind-note.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "jquery": true,
5 | "es6": true,
6 | },
7 | "extends": "wordpress",
8 | "rules": {}
9 | };
10 |
--------------------------------------------------------------------------------
/svgs/watch.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Exception.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/like.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/reply.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/drink.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/jam.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/itinerary.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/favorite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.distignore:
--------------------------------------------------------------------------------
1 | /.wordpress-org
2 | /.git
3 | /.github
4 | /node_modules
5 | /bin
6 | /sass
7 | /vendor
8 | /test
9 | .eslintrc.js
10 | readme.md
11 | package.json
12 | composer.json
13 | composer.lock
14 | Gruntfile.js
15 | push.sh
16 | phpunit.xml
17 | phpcs.xml
18 | README.md
19 | readme.md
20 | .travis.yml
21 | .distignore
22 | .gitignore
23 |
--------------------------------------------------------------------------------
/svgs/sleep.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/tag.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/review.svg:
--------------------------------------------------------------------------------
1 |
3 |
5 |
6 |
--------------------------------------------------------------------------------
/css/kind.min.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["../sass/_kinds.scss","../sass/_frontend.scss"],"names":[],"mappings":"AAAA,cACC,kBACA,YACA,sBACA,cAIA,cACC,WACA,YACA,WAKD,2FACC,aACA,cACA,WACA,0BAED,sFACC,qBAKM,cACQ,WACA,YACd,WACc,0BAEf,iBACC,YCnCF,UACI,mBACA,gBACA,iBACA,YACA,eACA,4BACF,qBACC,iBAED,qBACC,iBAGF,iBACC,qBAGD,kBACC,qBAGD,cACE,UACA,WAIH,SACI,gBACA,eAGJ,aACI,kBACA,kBACA,WACA,eAGJ,sBACI","file":"kind.min.css"}
--------------------------------------------------------------------------------
/css/kind.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["../sass/main.scss","../sass/_kinds.scss","../sass/_frontend.scss"],"names":[],"mappings":"AAAA;AAAA;ACAA;EACC;EACA;EACA;EACA;;;AAIA;EACC;EACA;EACA;;;AAKD;EACC;EACA;EACA;EACA;;AAED;EACC;;;AAKM;EACQ;EACA;EACd;EACc;;AAEf;EACC;;;ACnCF;EACI;EACA;EACA;EACA;EACA;EACA;;AACF;EACC;;AAED;EACC;;AAGF;EACC;;AAGD;EACC;;AAGD;EACE;EACA;;;AAIH;EACI;EACA;;;AAGJ;EACI;EACA;EACA;EACA;;;AAGJ;EACI","file":"kind.css"}
--------------------------------------------------------------------------------
/svgs/issue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/autoloader.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/css/kind.admin.min.css.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sourceRoot":"","sources":["../sass/_kinds.scss","../sass/_meta-box.scss"],"names":[],"mappings":"AAAA,cACC,kBACA,YACA,sBACA,cAIA,cACC,WACA,YACA,WAKD,2FACC,aACA,cACA,WACA,0BAED,sFACC,qBAKM,cACQ,WACA,YACd,WACc,0BAEf,iBACC,YCnCF,mCACC,mBAIA,qBACC,cAED,0BACC,cACA,YAED,qBACC,WACA,UACA,YAED,mCACC,cACA,UAED,kCACC,eACA,UAED,wBACC,WACA,eACA,UAED,uBACC,WACA,eACA,gBACA,SAED,kCACC,WAED,+BACC,WACA,UAED,oCACC,mBAED,wBACC,kBACA,kBACA,MACA,SACA,OACA,QACA,WACA,sBACA,gCAED,gCACC,cACA,cACA,kBACA","file":"kind.admin.min.css"}
--------------------------------------------------------------------------------
/svgs/listen.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/parse-this/includes/autoload.php:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | # WordPress Coding Standards
5 | # https://make.wordpress.org/core/handbook/coding-standards/
6 |
7 | root = true
8 |
9 | [*]
10 | charset = utf-8
11 | end_of_line = lf
12 | insert_final_newline = true
13 | trim_trailing_whitespace = true
14 | indent_style = tab
15 |
16 | [*.yml]
17 | indent_style = space
18 | indent_size = 2
19 |
20 | [*.md]
21 | trim_trailing_whitespace = false
22 |
23 | [{*.txt,wp-config-sample.php}]
24 | end_of_line = crlf
25 |
--------------------------------------------------------------------------------
/svgs/play.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/quote.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/eat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/wish.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/UPGRADING.md:
--------------------------------------------------------------------------------
1 | From 1.x to 2.x
2 | =================
3 |
4 | - All classes uses `Masterminds` namespace.
5 | - All public static methods has been removed from `HTML5` class and the general API to access the HTML5 functionalities has changed.
6 |
7 | Before:
8 |
9 | $dom = \HTML5::loadHTML('....');
10 | \HTML5::saveHTML($dom);
11 |
12 | After:
13 |
14 | use Masterminds\HTML5;
15 |
16 | $html5 = new HTML5();
17 |
18 | $dom = $html5->loadHTML('....');
19 | echo $html5->saveHTML($dom);
20 |
21 |
22 |
--------------------------------------------------------------------------------
/svgs/trip.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/follow.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/question.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/photo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/rsvp.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/acquisition.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/recipe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/mood.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/checkin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/repost.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/views/kind-rsvp.php:
--------------------------------------------------------------------------------
1 | get( 'rsvp', true );
12 |
13 | ?>
14 |
15 |
16 |
17 | ' . sprintf( Kind_View::rsvp_text( $rsvp ), $url, $title ) . '';
23 | }
24 | }
25 | ?>
26 |
27 | %1s', $embed );
30 | } elseif ( array_key_exists( 'summary', $cite ) ) {
31 | echo sprintf( '%1s ', $cite['summary'] );
32 | }
33 |
34 | // Close Response
35 | ?>
36 |
37 |
38 |
--------------------------------------------------------------------------------
/views/kind-photo.php:
--------------------------------------------------------------------------------
1 | get_cite();
14 | $cite = $photos_attachment->normalize_cite( $cite );
15 | $author = Kind_View::get_hcard( $cite['author'] );
16 | }
17 | }
18 |
19 | ?>
20 |
21 |
22 | %1s', $cite['name'] );
26 | }
27 | ?>
28 |
29 | %1s', $embed );
32 | } elseif ( $photos ) {
33 | $view = new Kind_Media_View( $photos, 'photo' );
34 | echo $view->get();
35 | } ?>
36 |
37 |
--------------------------------------------------------------------------------
/svgs/event.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/svgs/article.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/views/kind-checkin.php:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 | %2s', $url, $cite['name'] );
23 | } else {
24 | echo sprintf( '%1s ', $cite['name'] );
25 | }
26 | }
27 | ?>
28 |
29 | %1s', $embed );
33 | }
34 | }
35 |
36 | if ( $photos && ! has_post_thumbnail( get_the_ID() ) ) {
37 | $view = new Kind_Media_View( $photos, 'photo' );
38 | echo $view->get();
39 | }
40 | // Close Response
41 | ?>
42 |
43 |
44 |
--------------------------------------------------------------------------------
/views/kind-event.php:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
16 | get( 'start' );
18 | $end = $kind_post->get( 'end' );
19 | $timestring = '%1$s: %4$s
';
20 |
21 | if ( $start ) {
22 | printf( $timestring, __( 'Start', 'indieweb-post-kinds' ), 'dt-start', $start->format( DATE_W3C ), display_formatted_datetime( $start ) );
23 | }
24 | if ( $end ) {
25 | printf( $timestring, __( 'End', 'indieweb-post-kinds' ), 'dt-end', $end->format( DATE_W3C ), display_formatted_datetime( $end ) );
26 | }
27 | if ( $photos && ! has_post_thumbnail( get_the_ID() ) ) {
28 | $view = new Kind_Media_View( $photos, 'photo' );
29 | echo $view->get();
30 | }
31 | // Close Response
32 | ?>
33 |
34 |
--------------------------------------------------------------------------------
/lib/parse-this/parse-this.php:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 | %2s', $url, $cite['name'] );
20 | } else {
21 | echo sprintf( '%1s ', $cite['name'] );
22 | }
23 | if ( $author ) {
24 | echo ' ' . __( 'by', 'indieweb-post-kinds' ) . ' ' . $author;
25 | }
26 | if ( ! empty( $cite['publication'] ) ) {
27 | echo sprintf( ' (%1s ) ', $cite['publication'] );
28 | }
29 | }
30 | ?>
31 |
32 | %1s', $embed );
36 | } elseif ( array_key_exists( 'summary', $cite ) ) {
37 | echo sprintf( '%1s ', $cite['summary'] );
38 | }
39 | }
40 |
41 | // Close Response
42 | ?>
43 |
44 |
45 |
--------------------------------------------------------------------------------
/views/kind-drink.php:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 | %2s', $url, $cite['name'] );
19 | } else {
20 | echo sprintf( '%1s ', $cite['name'] );
21 | }
22 | if ( array_key_exists( 'publication', $cite ) ) {
23 | echo sprintf( ' (%1s ) ', $cite['publication'] );
24 | }
25 | }
26 | ?>
27 |
28 | %1s', $embed );
32 | } elseif ( array_key_exists( 'summary', $cite ) ) {
33 | echo sprintf( '%1s ', $cite['summary'] );
34 | }
35 | }
36 |
37 | if ( $photos && ! has_post_thumbnail( get_the_ID() ) ) {
38 | $view = new Kind_Media_View( $photos, 'photo' );
39 | echo $view->get();
40 | }
41 | // Close Response
42 | ?>
43 |
44 |
--------------------------------------------------------------------------------
/views/kind-like.php:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 | %2s', $url, $cite['name'] );
21 | } else {
22 | echo sprintf( '%1s ', $cite['name'] );
23 | }
24 | if ( $author ) {
25 | echo ' ' . __( 'by', 'indieweb-post-kinds' ) . ' ' . $author;
26 | }
27 | if ( ! empty( $cite['publication'] ) ) {
28 | echo sprintf( ' (%1s ) ', $cite['publication'] );
29 | }
30 | }
31 | ?>
32 |
33 | %1s', $embed );
37 | } elseif ( array_key_exists( 'summary', $cite ) ) {
38 | echo sprintf( '%1s ', $cite['summary'] );
39 | }
40 | }
41 |
42 | // Close Response
43 | ?>
44 |
45 |
46 |
12 |
13 |
14 |
15 | %2s', $url, $cite['name'] );
20 | } else {
21 | echo sprintf( '%1s ', $cite['name'] );
22 | }
23 | if ( $author ) {
24 | echo ' ' . __( 'by', 'indieweb-post-kinds' ) . ' ' . $author;
25 | }
26 | if ( ! empty( $cite['publication'] ) ) {
27 | echo sprintf( ' (%1s ) ', $cite['publication'] );
28 | }
29 | }
30 | ?>
31 |
32 | %1s', $embed );
36 | } elseif ( array_key_exists( 'summary', $cite ) ) {
37 | echo sprintf( '%1s ', $cite['summary'] );
38 | }
39 | }
40 |
41 | // Close Response
42 | ?>
43 |
44 |
45 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/views/kind-bookmark.php:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 | %2s', $cite['url'], $cite['name'] );
21 | } else {
22 | echo sprintf( '%1s ', esc_html( $cite['name'] ) );
23 | }
24 | if ( $author ) {
25 | echo ' ' . __( 'by', 'indieweb-post-kinds' ) . ' ' . $author;
26 | }
27 | if ( ! empty( $cite['publication'] ) ) {
28 | echo sprintf( ' (%1s ) ', esc_html( $cite['publication'] ) );
29 | }
30 | }
31 | ?>
32 |
33 | %1s', $embed );
36 | } elseif ( ! empty( $cite['summary'] ) ) {
37 | echo sprintf( '%1s ', $cite['summary'] );
38 | }
39 |
40 | // Close Response
41 | ?>
42 |
43 |
44 |
--------------------------------------------------------------------------------
/views/kind-audio.php:
--------------------------------------------------------------------------------
1 | get_audio();
7 | $duration = null;
8 | $publication = null;
9 |
10 | if ( is_array( $audios ) ) {
11 | if ( 1 === count( $audios ) && 0 !== $audios[0] ) {
12 | $audio_attachment = new Kind_Post( $audios[0] );
13 | $cite = mf2_to_jf2( $audio_attachment->get_cite() );
14 | if ( ! $cite ) {
15 | $cite = array();
16 | }
17 | if ( array_key_exists( 'author', $cite ) ) {
18 | $author = Kind_View::get_hcard( $cite['author'] );
19 | } else {
20 | $author = null;
21 | }
22 | if ( array_key_exists( 'duration', $cite ) ) {
23 | $duration = Kind_View::display_duration( $cite['duration'] );
24 | } else {
25 | $duration = null;
26 | }
27 | }
28 | }
29 | ?>
30 |
31 |
32 | %1s', $cite['name'] );
35 | }
36 | if ( $duration ) {
37 | printf( '(%1$s)', $duration );
38 | }
39 | ?>
40 |
41 |
42 | %1s', $embed );
45 | } elseif ( $audios ) {
46 | $view = new Kind_Media_View( $audios, 'audio' );
47 | echo $view->get();
48 | }
49 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Parser/FileInputStream.php:
--------------------------------------------------------------------------------
1 | get( 'rating', true );
12 | ?>
13 |
14 |
15 |
16 |
17 | %2s', $url, $cite['name'] );
22 | } else {
23 | echo sprintf( '%1s ', $cite['name'] );
24 | }
25 | if ( ! empty( $cite['publication'] ) ) {
26 | echo sprintf( ' (%1s ) ', $cite['publication'] );
27 | }
28 | }
29 | ?>
30 |
31 | %1s', $embed );
35 | } elseif ( array_key_exists( 'summary', $cite ) ) {
36 | echo sprintf( '%1s ', $cite['summary'] );
37 | }
38 | }
39 |
40 | if ( $rating ) {
41 | echo '' . sprintf( Kind_View::rating_text( $rating ), $url, $title ) . ' ';
42 | }
43 |
44 | if ( $photos && ! has_post_thumbnail( get_the_ID() ) ) {
45 | $view = new Kind_Media_View( $photos, 'photo' );
46 | echo $view->get();
47 | }
48 | // Close Response
49 | ?>
50 |
51 |
--------------------------------------------------------------------------------
/svgs/exercise.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/parse-this/readme.md:
--------------------------------------------------------------------------------
1 |
2 | Parse This turns URLs into structured jf2 data
3 |
4 | ## Description
5 |
6 | Parse This is based on a variety of projects including the parsing code from Press This, which was removed from WordPress.
7 |
8 | * It supports parsing from MF2 if present
9 | * For sites that are not marked up with Microformats 2(MF2) it will fall back onto parsing JSON-LD, then HTML/OpenGraph/Dublin Core Tags/etc.
10 | * It supports parsing of JSONFeed and RSS/Atom feeds
11 | * It supports parsing of WordPress REST API endpoints to generate a site feed
12 |
13 | The goal is to produce structured jf2 data that can be used for previewing links as well as feed readers and other options. It is also bundled in the Post Kinds and Yarns Microsub plugins as a library.
14 |
15 | It can be installed as a standalone plugin which will provide the necessary libraries and functionality as well as the REST API endpoint for getting JF2 data from an arbitrary URL or a WordPress Post.
16 |
17 | ## Frequently Asked Questions
18 |
19 |
20 |
21 | ## Changelog
22 |
23 | ### 1.0.1 ( 2021-04-02 )
24 |
25 | * Remove SimplePie as a dependency as the latest version 1.5.6 is now bundled with WordPress as of 5.6.
26 | * Remove MB polyfill due issues with PHP8.0 compatibility in favor of simpler solution.
27 |
28 | ### 1.0.0 ( 2020-12-15 )
29 |
30 | * First Official Release. Prior to this point it was in a point release.
31 |
32 |
--------------------------------------------------------------------------------
/views/kind-jam.php:
--------------------------------------------------------------------------------
1 | get( 'duration', true );
13 | if ( ! $duration ) {
14 | $duration = calculate_duration( $kind_post->get( 'start' ), $kind_post->get( 'end' ) );
15 | }
16 |
17 | ?>
18 |
19 |
20 |
21 | ' . $site_name . '';
32 | }
33 | if ( $duration ) {
34 | echo Kind_View::display_duration( $duration );
35 | }
36 | }
37 | ?>
38 |
39 | %1s', $embed );
43 | } elseif ( array_key_exists( 'summary', $cite ) ) {
44 | echo sprintf( '%1s ', $cite['summary'] );
45 | }
46 | }
47 |
48 | if ( $photos && ! has_post_thumbnail( get_the_ID() ) ) {
49 | $view = new Kind_Media_View( $photos, 'photo' );
50 | echo $view->get();
51 | }
52 | // Close Response
53 | ?>
54 |
55 |
--------------------------------------------------------------------------------
/includes/class-post-kind.php:
--------------------------------------------------------------------------------
1 | slug = $slug;
27 | $this->set_props( $args );
28 | }
29 |
30 | public function set_props( $args ) {
31 | $defaults = array(
32 | 'name' => $this->slug,
33 | 'singular_name' => $this->slug,
34 | 'verb' => $this->slug,
35 | 'format' => 'standard',
36 | 'icon' => $this->slug,
37 | 'show' => 'false',
38 | 'property' => '',
39 | );
40 | $args = wp_parse_args( $args, $defaults );
41 | foreach ( $args as $property_name => $property_value ) {
42 | $this->$property_name = $property_value;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/views/kind-itinerary.php:
--------------------------------------------------------------------------------
1 | get( 'itinerary', false );
9 |
10 | foreach( $itineraries as $key => $value ) {
11 | $itineraries [ $key ] = mf2_to_jf2( $value );
12 | }
13 |
14 | ?>
15 |
16 |
17 |
18 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
50 |
51 |
52 | get( 'duration', true );
13 | $rating = $kind_post->get( 'rating', true );
14 | if ( ! $duration ) {
15 | $duration = calculate_duration( $kind_post->get( 'start' ), $kind_post->get( 'end' ) );
16 | }
17 |
18 |
19 | ?>
20 |
21 |
22 | ' . $site_name . '';
33 | }
34 | if ( $duration ) {
35 | echo Kind_View::display_duration( $duration );
36 | }
37 | }
38 | ?>
39 |
40 | %1s', $embed );
44 | } elseif ( array_key_exists( 'summary', $cite ) ) {
45 | echo sprintf( '%1s ', $cite['summary'] );
46 | }
47 | }
48 |
49 | // Close Response
50 | ?>
51 |
52 |
53 | ' . sprintf( Kind_View::rating_text( $rating ), $url, $title ) . '';
55 | } ?>
56 |
57 |
58 | query( "//script[@type='application/json']" ) as $script ) {
20 | $content = $script->textContent; // phpcs:ignore
21 | $json[] = json_decode( $content, true );
22 | }
23 | $json = array_filter( $json );
24 |
25 | $jf2 = array();
26 |
27 | if ( 1 === count( $json ) && wp_is_numeric_array( $json ) ) {
28 | $json = $json[0];
29 | if ( array_key_exists( 'props', $json ) ) {
30 | $props = $json['props'];
31 | if ( array_key_exists( 'pageProps', $props ) ) {
32 | $props = $props['pageProps'];
33 | if ( array_key_exists( 'article', $props ) ) {
34 | $jf2['type'] = 'entry';
35 | $jf2['name'] = ifset( $props['article']['title'] );
36 | if ( array_key_exists( 'meta', $props['article'] ) ) {
37 | $jf2['published'] = normalize_iso8601( ifset( $props['article']['meta']['date'] ) );
38 | $jf2['category'] = ifset( $props['article']['meta']['tags'] );
39 | }
40 | }
41 | }
42 | }
43 | }
44 | $jf2 = array_filter( $jf2 );
45 |
46 | if ( WP_DEBUG ) {
47 | $jf2['_json'] = $json;
48 | }
49 | return array_filter( $jf2 );
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/views/kind-video.php:
--------------------------------------------------------------------------------
1 | get_video();
8 | $duration = null;
9 | if ( is_array( $videos ) ) {
10 | if ( 1 === count( $videos ) && 0 !== $videos[0] ) {
11 | $video_attachment = new Kind_Post( $videos[0] );
12 | $cite = $video_attachment->get_cite();
13 | $cite = $video_attachment->normalize_cite( $cite );
14 | $author = Kind_View::get_hcard( $cite['author'] );
15 |
16 | if ( array_key_exists( 'duration', $cite ) ) {
17 | $duration = Kind_View::display_duration( $cite['duration'] );
18 | } else {
19 | $duration = null;
20 | }
21 | }
22 | }
23 | $first_photo = null;
24 | if ( is_countable( $photos ) ) {
25 | $first_photo = $photos[0];
26 | }
27 | if ( is_array( $cite ) && ! $videos ) {
28 | if ( ! $embed ) {
29 | $view = new Kind_Media_View( $url, 'video' );
30 | $embed = $view->get();
31 | }
32 | }
33 |
34 |
35 | ?>
36 |
37 |
38 | %1s', $cite['name'] );
42 | }
43 |
44 | if ( $author ) {
45 | echo ' ' . __( 'by', 'indieweb-post-kinds' ) . ' ' . $author;
46 | }
47 | if ( $duration ) {
48 | printf( '(%1$s)', $duration );
49 | }
50 |
51 | ?>
52 |
53 |
54 | %1s', $embed );
57 | } elseif ( $videos ) {
58 |
59 | $poster = wp_get_attachment_image_url( $first_photo, 'full' );
60 | $view = new Kind_Media_View( $videos, 'video' );
61 | echo $view->get();
62 | }
63 | ?>
64 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/lib/parse-this/readme.txt:
--------------------------------------------------------------------------------
1 | === Parse This ===
2 | Contributors: dshanske
3 | Tags: indieweb
4 | Stable tag: trunk
5 | Requires at least: 4.9
6 | Requires PHP: 5.6
7 | Tested up to: 5.6
8 | License: GPLv2 or later
9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html
10 |
11 | Parse This turns URLs into structured jf2 data
12 |
13 | == Description ==
14 |
15 | Parse This is based on a variety of projects including the parsing code from Press This, which was removed from WordPress.
16 |
17 | * It supports parsing from MF2 if present
18 | * For sites that are not marked up with Microformats 2(MF2) it will fall back onto parsing JSON-LD, then HTML/OpenGraph/Dublin Core Tags/etc.
19 | * It supports parsing of JSONFeed and RSS/Atom feeds
20 | * It supports parsing of WordPress REST API endpoints to generate a site feed
21 |
22 | The goal is to produce structured jf2 data that can be used for previewing links as well as feed readers and other options. It is also bundled in the Post Kinds and Yarns Microsub plugins as a library.
23 |
24 | It can be installed as a standalone plugin which will provide the necessary libraries and functionality as well as the REST API endpoint for getting JF2 data from an arbitrary URL or a WordPress Post.
25 |
26 |
27 | == Frequently Asked Questions ==
28 |
29 | == Changelog ==
30 |
31 | = 1.0.1 ( 2021-04-02 ) =
32 | * Remove SimplePie as a dependency as the latest version 1.5.6 is now bundled with WordPress as of 5.6.
33 | * Remove MB polyfill due issues with PHP8.0 compatibility in favor of simpler solution.
34 |
35 | = 1.0.0 ( 2020-12-15 ) =
36 | * First Official Release. Prior to this point it was in a point release.
37 |
--------------------------------------------------------------------------------
/fontawesome-license.txt:
--------------------------------------------------------------------------------
1 | Font Awesome Free License
2 | -------------------------
3 |
4 | Font Awesome Free is free, open source, and GPL friendly. You can use it for
5 | commercial projects, open source projects, or really almost whatever you want.
6 | Full Font Awesome Free license: https://fontawesome.com/license.
7 |
8 | # Icons: CC BY 4.0 License (https://creativecommons.org/licenses/by/4.0/)
9 | In the Font Awesome Free download, the CC BY 4.0 license applies to all icons
10 | packaged as SVG and JS file types.
11 |
12 | # Fonts: SIL OFL 1.1 License (https://scripts.sil.org/OFL)
13 | In the Font Awesome Free download, the SIL OLF license applies to all icons
14 | packaged as web and desktop font files.
15 |
16 | # Code: MIT License (https://opensource.org/licenses/MIT)
17 | In the Font Awesome Free download, the MIT license applies to all non-font and
18 | non-icon files.
19 |
20 | # Attribution
21 | Attribution is required by MIT, SIL OLF, and CC BY licenses. Downloaded Font
22 | Awesome Free files already contain embedded comments with sufficient
23 | attribution, so you shouldn't need to do anything additional when using these
24 | files normally.
25 |
26 | We've kept attribution comments terse, so we ask that you do not actively work
27 | to remove them from files, especially code. They're a great way for folks to
28 | learn about Font Awesome.
29 |
30 | # Brand Icons
31 | All brand icons are trademarks of their respective owners. The use of these
32 | trademarks does not indicate endorsement of the trademark holder by Font
33 | Awesome, nor vice versa. **Please do not use brand logos for any purpose except
34 | to represent the company, product, or service to which they refer.**
35 |
--------------------------------------------------------------------------------
/css/kind.css:
--------------------------------------------------------------------------------
1 | /*--------------------------------------------------------------
2 | --------------------------------------------------------------*/
3 | .svg-icon svg {
4 | fill: currentColor;
5 | padding: 2px;
6 | vertical-align: middle;
7 | top: -0.0625em;
8 | }
9 |
10 | #kind-all svg {
11 | width: 1rem;
12 | height: 1rem;
13 | color: grey;
14 | }
15 |
16 | #kind-menu svg, #kind-posts svg, #tempus-onthisday svg, #tempus-thisweek svg, .kind-titles svg {
17 | width: 1.5rem;
18 | height: 1.5rem;
19 | color: white;
20 | mix-blend-mode: difference;
21 | }
22 | #kind-menu ul, #kind-posts ul, #tempus-onthisday ul, #tempus-thisweek ul, .kind-titles ul {
23 | list-style-type: none;
24 | }
25 |
26 | .response svg {
27 | width: 2rem;
28 | height: 2rem;
29 | color: white;
30 | mix-blend-mode: difference;
31 | }
32 | .response figure {
33 | margin: auto;
34 | }
35 |
36 | .response {
37 | margin-bottom: 10px;
38 | margin-top: 10px;
39 | margin-left: 20px;
40 | padding: 5px;
41 | font-size: 16px;
42 | border-bottom: inset #D6D6D6;
43 | }
44 | .response .kind-icon {
45 | margin-right: 3px;
46 | }
47 | .response blockquote {
48 | margin-left: 25px;
49 | }
50 | .response a:link {
51 | text-decoration: none;
52 | }
53 | .response a:hover {
54 | text-decoration: none;
55 | }
56 | .response img {
57 | padding: 0;
58 | margin: 2px;
59 | }
60 |
61 | ul.cites {
62 | list-style: none;
63 | display: inline;
64 | }
65 |
66 | .kind-embeds {
67 | text-align: center;
68 | position: relative;
69 | width: 100%;
70 | margin-top: 5px;
71 | }
72 |
73 | .kind-photo-thumbnail {
74 | margin-bottom: 5px;
75 | }
76 |
77 | /*# sourceMappingURL=kind.css.map */
78 |
--------------------------------------------------------------------------------
/views/kind-read.php:
--------------------------------------------------------------------------------
1 | get( 'read-status', true );
11 | $rating = $kind_post->get( 'rating', true );
12 |
13 | ?>
14 |
15 |
16 | %1s', Kind_View::read_text( $read ) );
21 | }
22 | if ( ! empty( $url ) ) {
23 | echo sprintf( '%2s ', $url, $cite['name'] );
24 | } else {
25 | echo sprintf( '%1s ', $cite['name'] );
26 | }
27 | if ( $author ) {
28 | echo ' ' . __( 'by', 'indieweb-post-kinds' ) . ' ' . $author;
29 | }
30 | if ( empty( $cite['publication'] ) ) {
31 | echo sprintf( ' (%1s ) ', $cite['publication'] );
32 | }
33 | }
34 | ?>
35 |
36 | %1s', $embed );
40 | } elseif ( array_key_exists( 'summary', $cite ) && ! empty( $cite['summary'] ) ) {
41 | echo sprintf( '%1s ', $cite['summary'] );
42 | }
43 | }
44 |
45 | if ( $rating ) {
46 | echo '' . sprintf( Kind_View::rating_text( $rating ), $url, $title ) . ' ';
47 | }
48 |
49 |
50 | if ( $photos && ! has_post_thumbnail( get_the_ID() ) ) {
51 | $view = new Kind_Media_View( $photos, 'photo' );
52 | echo $view->get();
53 | }
54 | // Close Response
55 | ?>
56 |
57 |
--------------------------------------------------------------------------------
/views/kind-listen.php:
--------------------------------------------------------------------------------
1 | get( 'duration', true );
13 | $rating = $kind_post->get( 'rating', true );
14 | if ( ! $duration ) {
15 | $duration = calculate_duration( $kind_post->get( 'start' ), $kind_post->get( 'end' ) );
16 | }
17 |
18 |
19 | ?>
20 |
21 |
22 |
23 | ' . $site_name . '';
34 | }
35 | if ( $duration ) {
36 | echo '(' . Kind_View::display_duration( $duration ) . ')';
37 | }
38 | }
39 | ?>
40 |
41 | %1s', $embed );
45 | } elseif ( array_key_exists( 'summary', $cite ) ) {
46 | echo sprintf( '%1s ', $cite['summary'] );
47 | }
48 | }
49 |
50 | // Close Response
51 | ?>
52 |
53 |
54 | ' . sprintf( Kind_View::rating_text( $rating ), $url, $title ) . '';
56 | } ?>
57 |
58 | get();
62 | }
63 | ?>
64 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Parser/CharacterReference.php:
--------------------------------------------------------------------------------
1 | ifset( $details['videoID'] ),
31 | 'name' => ifset( $details['title'] ),
32 | 'duration' => seconds_to_iso8601( ifset( $details['lengthSeconds'] ) ),
33 | 'category' => ifset( $details['keywords'] ),
34 | 'summary' => ifset( $details['shortDescription'] ),
35 | 'published' => normalize_iso8601( ifset( $microformat['publishDate'] ) ),
36 | );
37 | $author = array(
38 | 'type' => 'card',
39 | 'url' => ifset( $microformat['ownerProfileUrl'] ),
40 | 'name' => ifset( $details['author'] ),
41 | );
42 | $jf2['author'] = array_filter( $author );
43 |
44 | if ( isset( $details['thumbnail'] ) ) {
45 | $thumbnail = end( $details['thumbnail']['thumbnails'] );
46 | $jf2['featured'] = $thumbnail['url'];
47 | }
48 | if ( isset( $microformat['embed'] ) ) {
49 | $jf2['video'] = ifset( $microformat['embed']['iframeUrl'] );
50 | }
51 | if ( WP_DEBUG ) {
52 | $jf2['_yt'] = $decode;
53 | }
54 | return array_filter( $jf2 );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/svgs/weather.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Parser/README.md:
--------------------------------------------------------------------------------
1 | # The Parser Model
2 |
3 | The parser model here follows the model in section
4 | [8.2.1](http://www.w3.org/TR/2012/CR-html5-20121217/syntax.html#parsing)
5 | of the HTML5 specification, though we do not assume a networking layer.
6 |
7 | [ InputStream ] // Generic support for reading input.
8 | ||
9 | [ Scanner ] // Breaks down the stream into characters.
10 | ||
11 | [ Tokenizer ] // Groups characters into syntactic
12 | ||
13 | [ Tree Builder ] // Organizes units into a tree of objects
14 | ||
15 | [ DOM Document ] // The final state of the parsed document.
16 |
17 |
18 | ## InputStream
19 |
20 | This is an interface with at least two concrete implementations:
21 |
22 | - StringInputStream: Reads an HTML5 string.
23 | - FileInputStream: Reads an HTML5 file.
24 |
25 | ## Scanner
26 |
27 | This is a mechanical piece of the parser.
28 |
29 | ## Tokenizer
30 |
31 | This follows section 8.4 of the HTML5 spec. It is (roughly) a recursive
32 | descent parser. (Though there are plenty of optimizations that are less
33 | than purely functional.
34 |
35 | ## EventHandler and DOMTree
36 |
37 | EventHandler is the interface for tree builders. Since not all
38 | implementations will necessarily build trees, we've chosen a more
39 | generic name.
40 |
41 | The event handler emits tokens during tokenization.
42 |
43 | The DOMTree is an event handler that builds a DOM tree. The output of
44 | the DOMTree builder is a DOMDocument.
45 |
46 | ## DOMDocument
47 |
48 | PHP has a DOMDocument class built-in (technically, it's part of libxml.)
49 | We use that, thus rendering the output of this process compatible with
50 | SimpleXML, QueryPath, and many other XML/HTML processing tools.
51 |
52 | For cases where the HTML5 is a fragment of a HTML5 document a
53 | DOMDocumentFragment is returned instead. This is another built-in class.
54 |
--------------------------------------------------------------------------------
/includes/compat.php:
--------------------------------------------------------------------------------
1 | have_posts() ) {
10 | // Fallback to last time any post was modified or published.
11 | return get_lastpostmodified( 'GMT' );
12 | }
13 |
14 | // Extract the post modified times from the posts.
15 | $modified_times = wp_list_pluck( $wp_query->posts, 'post_modified_gmt' );
16 |
17 | // If this is a comment feed, check those objects too.
18 | if ( $wp_query->is_comment_feed() && $wp_query->comment_count ) {
19 | // Extract the comment modified times from the comments.
20 | $comment_times = wp_list_pluck( $wp_query->comments, 'comment_date_gmt' );
21 |
22 | // Add the comment times to the post times for comparison.
23 | $modified_times = array_merge( $modified_times, $comment_times );
24 | }
25 |
26 | // Determine the maximum modified time.
27 | $max_modified_time = max(
28 | array_map(
29 | function ( $time ) use ( $format ) {
30 | return mysql2date( $format, $time, false );
31 | },
32 | $modified_times
33 | )
34 | );
35 |
36 | /**
37 | * Filters the date the last post or comment in the query was modified.
38 | *
39 | * @since 5.2.0
40 | *
41 | * @param string $max_modified_time Date the last post or comment was modified in the query.
42 | * @param string $format The date format requested in get_feed_build_date.
43 | */
44 | return apply_filters( 'get_feed_build_date', $max_modified_time, $format );
45 | }
46 | }
47 |
48 | // Backcompat for wp_filter_content_tags introduced in 5.5.
49 | if ( ! function_exists( 'wp_filter_content_tags' ) ) {
50 | function wp_filter_content_tags( $content ) {
51 | return wp_make_content_images_responsive( $content ); // phpcs:ignore
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/InstructionProcessor.php:
--------------------------------------------------------------------------------
1 | get( 'duration', true );
10 | if ( ! $duration ) {
11 | $duration = calculate_duration( $kind_post->get( 'start' ), $kind_post->get( 'end' ) );
12 | }
13 | $rsvp = $kind_post->get( 'rsvp', true );
14 | $rating = $kind_post->get( 'rating', true );
15 |
16 | if ( ! $kind ) {
17 | return;
18 | }
19 |
20 | // Add in the appropriate type
21 | if ( ! empty( $type ) ) {
22 | $type = ( empty( $url ) ? 'p-' : 'u-' ) . $type;
23 | }
24 | ?>
25 |
26 |
27 |
28 | (' . $site_name . ')';
39 | }
40 | if ( in_array( $kind, array( 'jam', 'listen', 'play', 'read', 'watch', 'audio', 'video' ) ) ) {
41 | if ( $duration ) {
42 | echo ' ' . Kind_View::display_duration( $duration );
43 | }
44 | }
45 | }
46 | ?>
47 |
48 | %1s', $embed );
52 | } elseif ( array_key_exists( 'summary', $cite ) ) {
53 | echo sprintf( '%1s ', $cite['summary'] );
54 | }
55 | }
56 |
57 | // Close Response
58 | ?>
59 |
60 |
61 | ' . $rsvp . '';
63 | }
64 |
65 | if ( $rating ) {
66 | echo '' . sprintf( Kind_View::rating_text( $rating ), $url, $title ) . ' ';
67 | } ?>
68 |
69 | get_duration() ); ?>
2 |
3 |
39 |
--------------------------------------------------------------------------------
/lib/parse-this/includes/class-parse-this-opml.php:
--------------------------------------------------------------------------------
1 | 15,
23 | 'limit_response_size' => 1048576,
24 | 'redirection' => 5,
25 | // Use an explicit user-agent for Parse This
26 | );
27 | $links = array();
28 |
29 | $response = wp_safe_remote_get( $url, $args );
30 | $response_code = wp_remote_retrieve_response_code( $response );
31 | $content_type = wp_remote_retrieve_header( $response, 'content-type' );
32 |
33 | if ( in_array( $response_code, array( 403, 415 ), true ) ) {
34 | $args['user-agent'] = $user_agent;
35 | $response = wp_safe_remote_get( $url, $args );
36 | $response_code = wp_remote_retrieve_response_code( $response );
37 | if ( in_array( $response_code, array( 403, 415 ), true ) ) {
38 | return new WP_Error( 'source_error', 'Unable to Retrieve' );
39 | }
40 | }
41 |
42 | // Strip any character set off the content type
43 | $ct = explode( ';', $content_type );
44 | if ( is_array( $ct ) ) {
45 | $content_type = array_shift( $ct );
46 | }
47 | $content_type = trim( $content_type );
48 |
49 | $content = wp_remote_retrieve_body( $response );
50 | return $content;
51 | }
52 |
53 | public function convert( $content ) {
54 | $xml = simplexml_load_string( $content );
55 | $xml = $xml->body;
56 | $return = array();
57 | foreach ( $xml->outline as $outline ) {
58 | $top = array(
59 | 'title' => $outline['title'],
60 | 'children' => array(),
61 | );
62 | foreach ( $outline as $feed ) {
63 | $top['children'][] = array(
64 | 'name' => $feed['title'],
65 | 'url' => $feed['xmlUrl'],
66 | );
67 | }
68 | $return[] = $top;
69 | }
70 | return $return;
71 | }
72 | }
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/lib/parse-this/includes/class-parse-this-twitter.php:
--------------------------------------------------------------------------------
1 | 15,
17 | 'limit_response_size' => 1048576,
18 | 'redirection' => 5,
19 | // Use an explicit user-agent for Parse This
20 | 'user_agent' => 'Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:57.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 Parse This/WP',
21 | );
22 | $url = add_query_arg( 'url', $url, 'https://publish.twitter.com/oembed' );
23 | $response = wp_safe_remote_get( $url, $args );
24 | $oembed = json_decode( wp_remote_retrieve_body( $response ), true );
25 | $jf2 = array();
26 | if ( array_key_exists( 'url', $oembed ) ) {
27 | $jf2['url'] = $oembed['url'];
28 | }
29 | if ( array_key_exists( 'html', $oembed ) ) {
30 | $html = $oembed['html'];
31 | $dom = pt_load_domdocument( $html );
32 | $html = explode( '—', $html );
33 | $html = $html[0];
34 | $text = wp_strip_all_tags( $html );
35 | $text = explode( '—', $text );
36 | $text = $text[0];
37 |
38 | $links = $dom->getElementsByTagName( 'a' );
39 | $names = array();
40 | $category = array();
41 | foreach ( $links as $link ) {
42 | $key = wp_strip_all_tags( $link->nodeValue ); // phpcs:ignore
43 | $value = $link->getAttribute( 'href' );
44 | $parse = wp_parse_url( $value );
45 | unset( $parse['query'] );
46 | $value = build_url( $parse );
47 | if ( '#' === $key[0] ) {
48 | $category[] = str_replace( '#', '', $key );
49 | } elseif ( '@' === $key[0] ) {
50 | $category[] = $value;
51 | } elseif ( $jf2['url'] === $value ) {
52 | $published = new DateTime( $key );
53 | $jf2['published'] = $published->format( DATE_W3C );
54 | } else {
55 | $names[ wp_strip_all_tags( $key ) ] = normalize_url( $value ); // phpcs:ignore
56 | }
57 | }
58 | $jf2['links'] = $names;
59 | $jf2['category'] = $category;
60 | $jf2['content'] = array(
61 | 'html' => Parse_This::clean_content( $html, array( 'blockquote' => array() ) ),
62 | 'value' => $text,
63 | );
64 | $jf2['summary'] = $jf2['content']['html'];
65 | }
66 | $jf2['author'] = array_filter(
67 | array(
68 | 'type' => 'card',
69 | 'name' => ifset( $oembed['author_name'] ),
70 | 'url' => ifset( $oembed['author_url'] ),
71 | )
72 | );
73 | $jf2['publication'] = 'Twitter';
74 | if ( WP_DEBUG ) {
75 | $jf2['_ombed'] = $oembed;
76 | }
77 |
78 | return array_filter( $jf2 );
79 | }
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Parser/InputStream.php:
--------------------------------------------------------------------------------
1 | ids = $ids;
14 | $this->type = $type;
15 | }
16 |
17 | public function get() {
18 | switch ( $this->type ) {
19 | case 'photo':
20 | return $this->photo( $this->ids );
21 | case 'audio':
22 | return $this->audio( $this->ids );
23 | case 'video':
24 | return $this->video( $this->ids );
25 | }
26 | return '';
27 | }
28 |
29 | private function photo( $photos ) {
30 | return gallery_shortcode(
31 | array(
32 | 'ids' => $photos,
33 | 'size' => 'large',
34 | 'columns' => 1,
35 | 'link' => 'file',
36 | )
37 | );
38 | }
39 |
40 | /**
41 | * Return a media view for the audio post kind.
42 | *
43 | * @access private
44 | * @param int|string $id Audio attachment ID or audio URL
45 | * @param mixed $args Arguments for the audio media view.
46 | *
47 | * @return array|string|void
48 | */
49 | private function audio( $id, $args = null ) {
50 | $return = array();
51 | $default = array(
52 | 'class' => 'wp-audio-shortcode u-audio',
53 | );
54 | if ( is_array( $id ) ) {
55 | if ( 1 === count( $id ) ) {
56 | $id = array_shift( $id );
57 | } else {
58 | foreach ( $id as $i ) {
59 | $return[] = $this->audio( $i, $args );
60 | }
61 | return implode( ' ', $return );
62 | }
63 | }
64 |
65 | if ( wp_http_validate_url( $id ) ) {
66 | $args['src'] = $id;
67 | } elseif ( 0 === $id ) {
68 | return '';
69 | } else {
70 | $args['src'] = wp_get_attachment_url( (int) $id );
71 | }
72 | $return = '';
73 | if ( $args['src'] ) {
74 | $return = wp_audio_shortcode( $args );
75 | }
76 | return $return;
77 | }
78 |
79 | /**
80 | * Return a media view for the video post kind.
81 | *
82 | * @access private
83 | * @param int|string $id Video attachment ID or URL.
84 | * @param mixed $args Arguments for the video media view.
85 | *
86 | * @return array|string|void
87 | */
88 | private function video( $id, $args = null ) {
89 | $return = array();
90 | $defaults = array(
91 | 'class' => 'wp-video-shortcode u-video',
92 | );
93 | if ( is_array( $id ) ) {
94 | foreach ( $id as $i ) {
95 | $return[] = $this->video( $i, $args );
96 | }
97 | return implode( ' ', $return );
98 | } elseif ( wp_http_validate_url( $id ) ) {
99 | $args['src'] = $id;
100 | } if ( 0 === $id ) {
101 | return '';
102 | } else {
103 | $args['src'] = wp_get_attachment_url( (int) $id );
104 | }
105 | $return = '';
106 | if ( $args['src'] ) {
107 | $args = wp_parse_args( $args, $defaults );
108 | $return = wp_video_shortcode( $args );
109 | }
110 | return $return;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Serializer/RulesInterface.php:
--------------------------------------------------------------------------------
1 | get_kind();
5 | $type = Kind_Taxonomy::get_kind_info( $kind, 'property' ); // phpcs:ignore
6 | $cite = $kind_post->get_cite();
7 |
8 | if ( is_string( $cite ) ) {
9 | $cite = wp_http_validate_url( $cite ) ? array( 'url' => $cite ) : array( 'name' => $cite );
10 | }
11 |
12 | if ( empty( $cite['url'] ) && array_key_exists( 'kindurl', $_GET ) ) {
13 | $cite['url'] = esc_url_raw( $_GET['kindurl'] );
14 | }
15 |
16 | $attachment = 0;
17 |
18 |
19 | if ( in_array( $kind, array( 'audio', 'video', 'photo' ) ) ) {
20 | $attachment = attachment_url_to_postid( $cite['url'] );
21 | if ( $attachment ) {
22 | $attachment_post = new Kind_Post( $attachment );
23 | $cite = $attachment_post->get_cite();
24 | }
25 | }
26 |
27 | $cite = $kind_post->normalize_cite( $cite );
28 |
29 | ?>
30 |
31 |
32 | Upload or Attach Media
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | get( 'rsvp' ) ); ?>
48 |
49 |
50 | get( 'rating' ) ); ?>
51 |
52 |
53 |
54 |
>
55 | get();
62 | } elseif ( wp_attachment_is( 'video', $attachment ) ) {
63 | $view = new Kind_Media_View( $attachment, 'video' );
64 | echo $view->get();
65 | }
66 | }
67 | ?>
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | ';
12 |
13 | /** This action is documented in wp-includes/feed-rss2.php */
14 | do_action( 'rss_tag_pre', 'atom' );
15 | ?>
16 |
29 | >
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
50 |
51 |
52 |
53 |
54 |
55 |
65 |
66 |
67 |
68 | ]]>
69 |
70 |
71 |
72 |
73 |
74 |
75 | ]]>
76 |
77 | ]]>
78 |
79 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/lib/parse-this/includes/class-parse-this-jsonfeed.php:
--------------------------------------------------------------------------------
1 | self::ifset( 'name', $element ),
23 | 'url' => self::ifset( 'url', $element ),
24 | 'photo' => self::ifset( 'avatar', $element ),
25 | )
26 | );
27 | }
28 | $return = array_filter( $return );
29 | if ( 1 === count( $return ) ) {
30 | return $return[0];
31 | }
32 | return $return;
33 | }
34 |
35 | public static function to_jf2( $content, $url ) {
36 | $return = array_filter(
37 | array(
38 | 'type' => 'feed',
39 | '_feed_type' => 'jsonfeed',
40 | 'name' => self::ifset( 'title', $content ),
41 | 'url' => $url,
42 | 'summary' => self::ifset( 'description', $content ),
43 | 'photo' => self::ifset( 'icon', $content ),
44 | 'author' => self::get_author( $content ),
45 | 'language' => self::ifset( 'language', $content ),
46 | )
47 | );
48 | $return['items'] = array();
49 | foreach ( $content['items'] as $item ) {
50 | $newitem = array_filter(
51 | array(
52 | 'uid' => self::ifset( 'id', $item ),
53 | 'url' => self::ifset( 'url', $item ),
54 | 'in-reply-to' => self::ifset( 'external_url', $item ),
55 | 'name' => self::ifset( 'title', $item ),
56 | 'content' => array_filter(
57 | array(
58 | 'html' => Parse_This::clean_content( self::ifset( 'content_html', $item ) ),
59 | 'text' => self::ifset( 'content_text', $item ),
60 | )
61 | ),
62 | 'summary' => self::ifset( 'summary', $item ),
63 | 'featured' => self::ifset( 'image', $item ),
64 | 'published' => normalize_iso8601( self::ifset( 'date_published', $item ) ),
65 | 'updated' => normalize_iso8601( self::ifset( 'date_modified', $item ) ),
66 | 'author' => self::get_author( $item ),
67 | 'category' => self::ifset( 'tags', $item ),
68 | 'language' => self::ifset( 'language', $item ),
69 | )
70 | );
71 | if ( array_key_exists( 'attachments', $item ) ) {
72 | foreach ( $item['attachments'] as $attachment ) {
73 | $type = explode( '/', $attachment['mime_type'] );
74 | $type = array_shift( $type );
75 | switch ( $type ) {
76 | case 'audio':
77 | $newitem['audio'] = $attachment['url'];
78 | if ( isset( $attachment['duration_in_seconds'] ) ) {
79 | $newitem['duration'] = seconds_to_iso8601( $attachment['duration_in_seconds'] );
80 | }
81 | break;
82 | case 'image':
83 | $newitem['photo'] = $attachment['url'];
84 | break;
85 | case 'video':
86 | $newitem['video'] = $attachment['url'];
87 | if ( isset( $attachment['duration_in_seconds'] ) ) {
88 | $newitem['duration'] = seconds_to_iso8601( $attachment['duration_in_seconds'] );
89 | }
90 | break;
91 | }
92 | }
93 | }
94 | $return['items'][] = $newitem;
95 | }
96 | $return['_last_published'] = self::find_last_published( $return['items'] );
97 | $return['_last_updated'] = self::find_last_updated( $return['items'] );
98 | return $return;
99 | }
100 | }
101 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Parser/TreeBuildingRules.php:
--------------------------------------------------------------------------------
1 | 1,
20 | 'dd' => 1,
21 | 'dt' => 1,
22 | 'rt' => 1,
23 | 'rp' => 1,
24 | 'tr' => 1,
25 | 'th' => 1,
26 | 'td' => 1,
27 | 'thead' => 1,
28 | 'tfoot' => 1,
29 | 'tbody' => 1,
30 | 'table' => 1,
31 | 'optgroup' => 1,
32 | 'option' => 1,
33 | );
34 |
35 | /**
36 | * Returns true if the given tagname has special processing rules.
37 | */
38 | public function hasRules($tagname)
39 | {
40 | return isset(static::$tags[$tagname]);
41 | }
42 |
43 | /**
44 | * Evaluate the rule for the current tag name.
45 | *
46 | * This may modify the existing DOM.
47 | *
48 | * @return \DOMElement The new Current DOM element.
49 | */
50 | public function evaluate($new, $current)
51 | {
52 | switch ($new->tagName) {
53 | case 'li':
54 | return $this->handleLI($new, $current);
55 | case 'dt':
56 | case 'dd':
57 | return $this->handleDT($new, $current);
58 | case 'rt':
59 | case 'rp':
60 | return $this->handleRT($new, $current);
61 | case 'optgroup':
62 | return $this->closeIfCurrentMatches($new, $current, array(
63 | 'optgroup',
64 | ));
65 | case 'option':
66 | return $this->closeIfCurrentMatches($new, $current, array(
67 | 'option',
68 | ));
69 | case 'tr':
70 | return $this->closeIfCurrentMatches($new, $current, array(
71 | 'tr',
72 | ));
73 | case 'td':
74 | case 'th':
75 | return $this->closeIfCurrentMatches($new, $current, array(
76 | 'th',
77 | 'td',
78 | ));
79 | case 'tbody':
80 | case 'thead':
81 | case 'tfoot':
82 | case 'table': // Spec isn't explicit about this, but it's necessary.
83 |
84 | return $this->closeIfCurrentMatches($new, $current, array(
85 | 'thead',
86 | 'tfoot',
87 | 'tbody',
88 | ));
89 | }
90 |
91 | return $current;
92 | }
93 |
94 | protected function handleLI($ele, $current)
95 | {
96 | return $this->closeIfCurrentMatches($ele, $current, array(
97 | 'li',
98 | ));
99 | }
100 |
101 | protected function handleDT($ele, $current)
102 | {
103 | return $this->closeIfCurrentMatches($ele, $current, array(
104 | 'dt',
105 | 'dd',
106 | ));
107 | }
108 |
109 | protected function handleRT($ele, $current)
110 | {
111 | return $this->closeIfCurrentMatches($ele, $current, array(
112 | 'rt',
113 | 'rp',
114 | ));
115 | }
116 |
117 | protected function closeIfCurrentMatches($ele, $current, $match)
118 | {
119 | if (in_array($current->tagName, $match, true)) {
120 | $current->parentNode->appendChild($ele);
121 | } else {
122 | $current->appendChild($ele);
123 | }
124 |
125 | return $ele;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Parser/EventHandler.php:
--------------------------------------------------------------------------------
1 | ).
65 | *
66 | * @return int one of the Tokenizer::TEXTMODE_* constants
67 | */
68 | public function startTag($name, $attributes = array(), $selfClosing = false);
69 |
70 | /**
71 | * An end-tag.
72 | */
73 | public function endTag($name);
74 |
75 | /**
76 | * A comment section (unparsed character data).
77 | */
78 | public function comment($cdata);
79 |
80 | /**
81 | * A unit of parsed character data.
82 | *
83 | * Entities in this text are *already decoded*.
84 | */
85 | public function text($cdata);
86 |
87 | /**
88 | * Indicates that the document has been entirely processed.
89 | */
90 | public function eof();
91 |
92 | /**
93 | * Emitted when the parser encounters an error condition.
94 | */
95 | public function parseError($msg, $line, $col);
96 |
97 | /**
98 | * A CDATA section.
99 | *
100 | * @param string $data
101 | * The unparsed character data
102 | */
103 | public function cdata($data);
104 |
105 | /**
106 | * This is a holdover from the XML spec.
107 | *
108 | * While user agents don't get PIs, server-side does.
109 | *
110 | * @param string $name The name of the processor (e.g. 'php').
111 | * @param string $data The unparsed data.
112 | */
113 | public function processingInstruction($name, $data = null);
114 | }
115 |
--------------------------------------------------------------------------------
/templates/feed-rss2.php:
--------------------------------------------------------------------------------
1 | ';
12 |
13 | /**
14 | * Fires between the xml and rss tags in a feed.
15 | *
16 | * @since 4.0.0
17 | *
18 | * @param string $context Type of feed. Possible values include 'rss2', 'rss2-comments',
19 | * 'rdf', 'atom', and 'atom-comments'.
20 | */
21 | do_action( 'rss_tag_pre', 'rss2' );
22 | ?>
23 |
38 | >
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
61 |
62 |
63 |
76 |
77 |
88 | -
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 | ]]>
99 |
100 |
101 |
102 |
103 | ]]>
104 |
105 | ]]>
106 |
107 | 0 ) : ?>
108 | ]]>
109 |
110 | ]]>
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
126 |
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/includes/class-kind-post-widget.php:
--------------------------------------------------------------------------------
1 | 'kind_post_widget',
13 | 'description' => __( 'A widget that allows you to display a list of posts by type', 'indieweb-post-kinds' ),
14 | 'show_instance_in_rest' => true,
15 | )
16 | );
17 | } // end constructor
18 |
19 | /**
20 | * Front-end display of widget.
21 | *
22 | * @see WP_Widget::widget()
23 | *
24 | * @param array $args Widget arguments.
25 | * @param array $instance Saved values from database.
26 | */
27 | public function widget( $args, $instance ) {
28 | /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
29 | $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
30 |
31 | $kind = $instance['kind'] ?? 'note';
32 | // phpcs:ignore
33 | echo $args['before_widget'];
34 | if ( ! empty( $instance['title'] ) ) {
35 | echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore
36 | }
37 | $transient = get_transient( 'kind_post_widget' );
38 | if ( false === $transient ) {
39 | $query = array(
40 | 'tax_query' => array(
41 | array(
42 | 'taxonomy' => 'kind',
43 | 'field' => 'slug',
44 | 'terms' => $kind,
45 | ),
46 | ),
47 | 'numberposts' => $instance['number'] ?? 5,
48 | );
49 | $posts = get_posts( $query );
50 | }
51 |
52 | if ( 0 === count( $posts ) ) {
53 | return;
54 | }
55 | echo '';
56 | if ( 0 !== count( $posts ) ) {
57 | echo '
';
58 | foreach ( $posts as $post ) {
59 | printf( '%1$s ', kind_get_the_link( $post ) ); // phpcs:ignore
60 | }
61 | echo ' ';
62 | } else {
63 | esc_html_e( 'No Posts Found', 'indieweb-post-kinds' );
64 | }
65 | echo '
';
66 | echo $args['after_widget']; // phpcs:ignore
67 | }
68 |
69 | /**
70 | * Sanitize widget form values as they are saved.
71 | *
72 | * @see WP_Widget::update()
73 | *
74 | * @param array $new_instance Values just sent to be saved.
75 | * @param array $old_instance Previously saved values from database.
76 | *
77 | * @return array Updated safe values to be saved.
78 | */
79 | public function update( $new_instance, $old_instance ) {
80 | array_walk_recursive( $new_instance, 'sanitize_text_field' );
81 | return $new_instance;
82 | }
83 |
84 |
85 | /**
86 | * Create the form for the Widget admin
87 | *
88 | * @see WP_Widget::form()
89 | *
90 | * @param array $instance Previously saved values from database.
91 | */
92 | public function form( $instance ) {
93 | $instance['kind'] = ( $instance['kind'] ?? 'note' );
94 | ?>
95 |
96 | get_field_id( 'title' ) ); ?>"
97 | value="" />
98 |
99 | %2$s',
106 | esc_attr( $term ),
107 | Kind_Taxonomy::get_kind_info( $term, 'singular_name' ), // phpcs:ignore
108 | selected( $instance['kind'], $term )
109 | );
110 | printf( '%1$s %2$s ', Kind_Taxonomy::get_icon( $term ), esc_html( $value->singular_name ) ); //phpcs:ignore
111 | }
112 | ?>
113 |
114 |
115 |
116 |
117 | ';
11 |
12 | /** This action is documented in wp-includes/feed-rss2.php */
13 | do_action( 'rss_tag_pre', 'rss2-comments' );
14 | ?>
15 |
24 |
25 |
33 | >
34 |
35 |
36 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
58 |
59 |
60 |
64 |
65 | comment_post_ID );
77 | ?>
78 | -
79 |
80 | ID );
83 | /** This filter is documented in wp-includes/feed.php */
84 | $title = apply_filters( 'the_title_rss', $title );
85 | if ( $title ) {
86 | /* translators: Individual comment title. 1: Post title, 2: Comment author name */
87 | printf( ent2ncr( __( 'Comment on %1$s by %2$s' ) ), $title, get_comment_author_rss() );
88 | } else {
89 | printf( ent2ncr( __( 'Comment by %1$s' ) ), get_comment_author_rss() );
90 | }
91 | } else {
92 | /* translators: Comment author title. %s: Comment author name */
93 | printf( ent2ncr( __( 'By: %s' ) ), get_comment_author_rss() );
94 | }
95 | ?>
96 |
97 |
98 | ]]>
99 |
100 |
101 |
102 |
103 | ]]>
104 |
105 | ]]>
106 | ]]>
107 | comment_ID The ID of the comment being displayed.
115 | * @param int $comment_post->ID The ID of the post the comment is connected to.
116 | */
117 | do_action( 'commentrss2_item', $comment->comment_ID, $comment_post->ID );
118 | ?>
119 |
120 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Serializer/Traverser.php:
--------------------------------------------------------------------------------
1 | 'html',
21 | 'http://www.w3.org/1998/Math/MathML' => 'math',
22 | 'http://www.w3.org/2000/svg' => 'svg',
23 | );
24 |
25 | protected $dom;
26 |
27 | protected $options;
28 |
29 | protected $encode = false;
30 |
31 | protected $rules;
32 |
33 | protected $out;
34 |
35 | /**
36 | * Create a traverser.
37 | *
38 | * @param \DOMNode|\DOMNodeList $dom The document or node to traverse.
39 | * @param resource $out A stream that allows writing. The traverser will output into this
40 | * stream.
41 | * @param array $options An array of options for the traverser as key/value pairs. These include:
42 | * - encode_entities: A bool to specify if full encding should happen for all named
43 | * charachter references. Defaults to false which escapes &'<>".
44 | * - output_rules: The path to the class handling the output rules.
45 | */
46 | public function __construct($dom, $out, RulesInterface $rules, $options = array())
47 | {
48 | $this->dom = $dom;
49 | $this->out = $out;
50 | $this->rules = $rules;
51 | $this->options = $options;
52 |
53 | $this->rules->setTraverser($this);
54 | }
55 |
56 | /**
57 | * Tell the traverser to walk the DOM.
58 | *
59 | * @return resource $out Returns the output stream.
60 | */
61 | public function walk()
62 | {
63 | if ($this->dom instanceof \DOMDocument) {
64 | $this->rules->document($this->dom);
65 | } elseif ($this->dom instanceof \DOMDocumentFragment) {
66 | // Document fragments are a special case. Only the children need to
67 | // be serialized.
68 | if ($this->dom->hasChildNodes()) {
69 | $this->children($this->dom->childNodes);
70 | }
71 | } // If NodeList, loop
72 | elseif ($this->dom instanceof \DOMNodeList) {
73 | // If this is a NodeList of DOMDocuments this will not work.
74 | $this->children($this->dom);
75 | } // Else assume this is a DOMNode-like datastructure.
76 | else {
77 | $this->node($this->dom);
78 | }
79 |
80 | return $this->out;
81 | }
82 |
83 | /**
84 | * Process a node in the DOM.
85 | *
86 | * @param mixed $node A node implementing \DOMNode.
87 | */
88 | public function node($node)
89 | {
90 | // A listing of types is at http://php.net/manual/en/dom.constants.php
91 | switch ($node->nodeType) {
92 | case XML_ELEMENT_NODE:
93 | $this->rules->element($node);
94 | break;
95 | case XML_TEXT_NODE:
96 | $this->rules->text($node);
97 | break;
98 | case XML_CDATA_SECTION_NODE:
99 | $this->rules->cdata($node);
100 | break;
101 | case XML_PI_NODE:
102 | $this->rules->processorInstruction($node);
103 | break;
104 | case XML_COMMENT_NODE:
105 | $this->rules->comment($node);
106 | break;
107 | // Currently we don't support embedding DTDs.
108 | default:
109 | //print '';
110 | break;
111 | }
112 | }
113 |
114 | /**
115 | * Walk through all the nodes on a node list.
116 | *
117 | * @param \DOMNodeList $nl A list of child elements to walk through.
118 | */
119 | public function children($nl)
120 | {
121 | foreach ($nl as $node) {
122 | $this->node($node);
123 | }
124 | }
125 |
126 | /**
127 | * Is an element local?
128 | *
129 | * @param mixed $ele An element that implement \DOMNode.
130 | *
131 | * @return bool true if local and false otherwise.
132 | */
133 | public function isLocalElement($ele)
134 | {
135 | $uri = $ele->namespaceURI;
136 | if (empty($uri)) {
137 | return false;
138 | }
139 |
140 | return isset(static::$local_ns[$uri]);
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/RELEASE.md:
--------------------------------------------------------------------------------
1 | # Release Notes
2 |
3 | 2.7.6 (2021-08-18)
4 |
5 | - #218: Address comment handling issues
6 |
7 | 2.7.5 (2021-07-01)
8 |
9 | - #204: Travis: Enable tests on PHP 8.0
10 | - #207: Fix PHP 8.1 deprecations
11 |
12 | 2.7.4 (2020-10-01)
13 |
14 | - #191: Fix travisci build
15 | - #195: Add .gitattributes file with export-ignore rules
16 | - #194: Fix query parameter parsed as character entity
17 |
18 | 2.7.3 (2020-07-05)
19 |
20 | - #190: mitigate cyclic reference between output rules and the traverser objects
21 |
22 | 2.7.2 (2020-07-01)
23 |
24 | - #187: Fixed memory leak in HTML5::saveHTML()
25 | - #186: Add special case for end tag
26 |
27 | 2.7.1 (2020-06-14)
28 |
29 | - #171: add PHP 7.4 job
30 | - #178: Prevent infinite loop on un-terminated entity declaration at EOF
31 |
32 | 2.7.0 (2019-07-25)
33 |
34 | - #164: Drop HHVM support
35 | - #168: Set default encoding in the DOMDocument object
36 |
37 | 2.6.0 (2019-03-10)
38 |
39 | - #163: Allow to pass a charset to the Scanner
40 |
41 | 2.5.0 (2018-12-27)
42 |
43 | - #162, #161, #155, #154, #153, #151: big performance improvements
44 | - #156: fixed typos
45 | - #160: adopt and enforce code style
46 | - #159: remove deprecated php unit base test case
47 | - #150: backport changes from old master branch
48 |
49 | 2.4.0 (2018-11-17)
50 |
51 | - #148: Improve performance by moving sequence matching
52 | - #147: Improve the Tokenizer performance
53 | - #146: Improve performance by relying on a native string instead of InputStream
54 | - #144: Add DOM extension in composer.json
55 | - #145: Add more extensions on composer.json, improve phpdocs and remove dead code
56 | - #143: Remove experimental comment
57 |
58 | 2.3.1 (2018-10-18)
59 |
60 | - #121: Audio is not a block tag (fixed by #141)
61 | - #136: Handle illegal self-closing according to spec (fixed by #137)
62 | - #141: Minor fixes in the README
63 |
64 | 2.3.0 (2017-09-04)
65 |
66 | - #129: image within inline svg breaks system (fixed by #133)
67 | - #131: ² does not work (fixed by #132)
68 | - #134: Improve tokenizer performance by 20% (alternative version of #130 thanks to @MichaelHeerklotz)
69 | - #135: Raw & in attributes
70 |
71 | 2.2.2 (2016-09-22)
72 |
73 | - #116: In XML mode, tags are case sensitive
74 | - #115: Fix PHP Notice in OutputRules
75 | - #112: fix parsing of options of an optgroup
76 | - #111: Adding test for the address tag
77 |
78 | 2.2.1 (2016-05-10)
79 |
80 | - #109: Fixed issue where address tag could be written without closing tag (thanks sylus)
81 |
82 | 2.2.0 (2016-04-11)
83 |
84 | - #105: Enable composer cache (for CI/CD)
85 | - #100: Use mb_substitute_character inset of ini_set for environments where ini_set is disable (e.g., shared hosting)
86 | - #98: Allow link, meta, style tags in noscript tags
87 | - #96: Fixed xml:href on svgs that use the "use" breaking
88 | - #94: Counting UTF8 characters performance improvement
89 | - #93: Use newer version of coveralls package
90 | - #90: Remove duplicate test
91 | - #87: Allow multiple root nodes
92 |
93 | 2.1.2 (2015-06-07)
94 | - #82: Support for PHP7
95 | - #84: Improved boolean attribute handling
96 |
97 | 2.1.1 (2015-03-23)
98 | - #78: Fixes bug where unmatched entity like string drops everything after &.
99 |
100 | 2.1.0 (2015-02-01)
101 | - #74: Added `disable_html_ns` and `target_doc` dom parsing options
102 | - Unified option names
103 | - #73: Fixed alphabet, ß now can be detected
104 | - #75 and #76: Allow whitespace in RCDATA tags
105 | - #77: Fixed parsing blunder for json embeds
106 | - #72: Add options to HTML methods
107 |
108 | 2.0.2 (2014-12-17)
109 | - #50: empty document handling
110 | - #63: tags with strange capitalization
111 | - #65: dashes and underscores as allowed characters in tag names
112 | - #68: Fixed issue with non-inline elements inside inline containers
113 |
114 | 2.0.1 (2014-09-23)
115 | - #59: Fixed issue parsing some fragments.
116 | - #56: Incorrectly saw 0 as empty string
117 | - Sami as new documentation generator
118 |
119 | 2.0.0 (2014-07-28)
120 | - #53: Improved boolean attributes handling
121 | - #52: Facebook HHVM compatibility
122 | - #48: Adopted PSR-2 as coding standard
123 | - #47: Moved everything to Masterminds namespace
124 | - #45: Added custom namespaces
125 | - #44: Added support to XML-style namespaces
126 | - #37: Refactored HTML5 class removing static methods
127 |
128 | 1.0.5 (2014-06-10)
129 | - #38: Set the dev-master branch as the 1.0.x branch for composer (goetas)
130 | - #34: Tests use PSR-4 for autoloading. (goetas)
131 | - #40, #41: Fix entity handling in RCDATA sections. (KitaitiMakoto)
132 | - #32: Fixed issue where wharacter references were being incorrectly encoded in style tags.
133 |
134 | 1.0.4 (2014-04-29)
135 | - #30/#31 Don't throw an exception for invalid tag names.
136 |
137 | 1.0.3 (2014-02-28)
138 | - #23 and #29: Ignore attributes with illegal chars in name for the PHP DOM.
139 |
140 | 1.0.2 (2014-02-12)
141 | - #23: Handle missing tag close in attribute list.
142 | - #25: Fixed text escaping in the serializer (HTML% 8.3).
143 | - #27: Fixed tests on Windows: changed "\n" -> PHP_EOL.
144 | - #28: Fixed infinite loop for char "&" in unquoted attribute in parser.
145 | - #26: Updated tag name case handling to deal with uppercase usage.
146 | - #24: Newlines and tabs are allowed inside quoted attributes (HTML5 8.2.4).
147 | - Fixed Travis CI testing.
148 |
149 | 1.0.1 (2013-11-07)
150 | - CDATA encoding is improved. (Non-standard; Issue #19)
151 | - Some parser rules were not returning the new current element. (Issue #20)
152 | - Added, to the README, details on code test coverage and to packagist version.
153 | - Fixed processor instructions.
154 | - Improved test coverage and documentation coverage.
155 |
156 | 1.0.0 (2013-10-02)
157 | - Initial release.
158 |
--------------------------------------------------------------------------------
/templates/feed-atom-comments.php:
--------------------------------------------------------------------------------
1 | ';
10 |
11 | /** This action is documented in wp-includes/feed-rss2.php */
12 | do_action( 'rss_tag_pre', 'atom-comments' );
13 | ?>
14 |
29 | >
30 |
31 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
69 | comment_post_ID );
74 | ?>
75 |
76 |
77 | ID );
80 | /** This filter is documented in wp-includes/feed.php */
81 | $title = apply_filters( 'the_title_rss', $title );
82 | /* translators: Individual comment title. 1: Post title, 2: Comment author name */
83 | printf( ent2ncr( __( 'Comment on %1$s by %2$s' ) ), $title, get_comment_author_rss() );
84 | } else {
85 | /* translators: Comment author title. %s: Comment author name */
86 | printf( ent2ncr( __( 'By: %s' ) ), get_comment_author_rss() );
87 | }
88 | ?>
89 |
90 |
91 |
92 |
93 |
94 | ' . get_comment_author_url() . '';}
97 | ?>
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | ]]>
106 |
107 | ]]>
108 | comment_parent == 0 ) : // This comment is top level
112 | ?>
113 |
114 | comment_parent );
117 | // The rel attribute below and the id tag above should be GUIDs, but WP doesn't create them for comments (unlike posts). Either way, it's more important that they both use the same system
118 | ?>
119 |
120 | comment_ID, $comment_post->ID );
131 | ?>
132 |
133 |
137 |
138 |
--------------------------------------------------------------------------------
/lib/parse-this/includes/class-rest-parse-this.php:
--------------------------------------------------------------------------------
1 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
46 |
47 |
109 |
110 | WP_REST_Server::READABLE,
125 | 'callback' => array( $cls, 'read' ),
126 | 'args' => array(
127 | 'url' => array(
128 | 'required' => true,
129 | 'validate_callback' => array( $cls, 'is_valid_url' ),
130 | 'sanitize_callback' => 'esc_url_raw',
131 | ),
132 | ),
133 | 'permission_callback' => function () {
134 | return current_user_can( 'read' );
135 | },
136 | ),
137 | )
138 | );
139 | }
140 |
141 | public static function read( $request ) {
142 | $url = $request->get_param( 'url' );
143 | $mf2 = $request->get_param( 'mf2' );
144 | $return = $request->get_param( 'return' );
145 | $refs = $request->get_param( 'references' );
146 | $discovery = $request->get_param( 'discovery' );
147 | $location = $request->get_param( 'location' );
148 | $follow = $request->get_param( 'follow' );
149 | if ( $discovery ) {
150 | $parse = new Parse_This_Discovery();
151 | return $parse->fetch( $url );
152 | }
153 | $parse = new Parse_This( $url );
154 | $r = $parse->fetch();
155 |
156 | if ( is_wp_error( $r ) ) {
157 | return $r;
158 | }
159 | $parse->parse(
160 | array(
161 | 'return' => $return,
162 | 'follow' => $follow,
163 | 'references' => $refs,
164 | 'location' => $location,
165 | )
166 | );
167 | if ( $mf2 ) {
168 | return $parse->get( 'mf2' );
169 | }
170 | return $parse->get();
171 | }
172 |
173 | /**
174 | * Returns if valid URL for REST validation
175 | *
176 | * @param string $url
177 | *
178 | * @return boolean
179 | */
180 | public static function is_valid_url( $url, $request = null, $key = null ) {
181 | return wp_http_validate_url( $url );
182 | }
183 |
184 |
185 | public static function addscheme( $url, $scheme = 'http://' ) {
186 | return wp_parse_url( $url, PHP_URL_SCHEME ) === null ? $scheme . $url : $url;
187 | }
188 |
189 | }
190 |
191 | new REST_Parse_This();
192 |
--------------------------------------------------------------------------------
/includes/class-kind-menu-widget.php:
--------------------------------------------------------------------------------
1 | 'kind_menu_widget',
13 | 'description' => __( 'A widget that allows you to display a menu of kind archives', 'indieweb-post-kinds' ),
14 | 'show_instance_in_rest' => true,
15 | )
16 | );
17 | } // end constructor
18 |
19 | /**
20 | * Front-end display of widget.
21 | *
22 | * @see WP_Widget::widget()
23 | *
24 | * @param array $args Widget arguments.
25 | * @param array $instance Saved values from database.
26 | */
27 | public function widget( $args, $instance ) {
28 | /** This filter is documented in wp-includes/widgets/class-wp-widget-pages.php */
29 | $title = apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base );
30 |
31 | if ( array_key_exists( 'termslist', $instance ) ) {
32 | $include = $instance['termslist'];
33 | } else {
34 | $include = array();
35 | }
36 | $include = array_merge( $include, array( 'note', 'reply', 'article' ) );
37 | // Filter Kinds
38 | $include = array_unique( apply_filters( 'kind_include', $include ) );
39 | // Note cannot be removed or disabled without hacking the code
40 | if ( ! in_array( 'note', $include, true ) ) {
41 | $include[] = 'note';
42 | }
43 |
44 | // phpcs:ignore
45 | echo $args['before_widget'];
46 | if ( $title ) {
47 | echo $args['before_title'] . $title . $args['after_title']; // phpcs:ignore
48 | }
49 | ?>
50 |
51 |
83 |
84 | 1,
116 | 'all' => 1,
117 | 'termslist' => array(),
118 | );
119 | $instance = wp_parse_args( (array) $instance, $defaults );
120 | $termslist = (array) $instance['termslist'];
121 | ?>
122 |
123 | get_field_id( 'title' ) ); ?>" value="
124 | " />
125 |
126 | show ) {
130 | printf(
131 | ' ',
132 | esc_attr( $this->get_field_name( 'termslist' ) ),
133 | esc_attr( $this->get_field_id( 'termslist' ) ),
134 | esc_attr( $term ),
135 | checked( in_array( $term, $termslist, true ), true, false )
136 | );
137 | printf( '%1$s %2$s ', Kind_Taxonomy::get_icon( $term ), esc_html( $value->singular_name ) ); //phpcs:ignore
138 | }
139 | }
140 | ?>
141 |
142 |
143 |
144 |
145 | />
146 |
147 |
148 |
149 |
150 | />
151 |
152 | query( '//script' ) as $script ) {
16 | if ( preg_match( '/window\._sharedData = ({.+});/', $script->textContent, $match ) ) { // phpcs:ignore
17 | $data = json_decode( $match[1], true );
18 | }
19 | }
20 | if ( empty( $data ) ) {
21 | return array();
22 | }
23 |
24 | $jf2 = array();
25 | if ( $data && is_array( $data ) && array_key_exists( 'entry_data', $data ) ) {
26 | if ( is_array( $data['entry_data'] ) ) {
27 | if ( array_key_exists( 'PostPage', $data['entry_data'] ) ) {
28 | // Photo Page
29 | $jf2 = self::html_photo( $data, $url );
30 | } elseif ( array_key_exists( 'LocationsPage', $data['entry_data'] ) ) {
31 | // Locations Page
32 | $jf2 = self::html_location( $data, $url );
33 | } elseif ( array_key_exists( 'LoginAndSignupPage', $data['entry_data'] ) ) {
34 | return array();
35 | }
36 | }
37 | }
38 | if ( WP_DEBUG ) {
39 | $jf2['_ig'] = $data;
40 | }
41 | return array_filter( $jf2 );
42 | }
43 |
44 | private static function html_location( $data, $url ) {
45 | $post = $data['entry_data']['LocationsPage'];
46 | if ( isset( $post[0]['graphql']['location'] ) ) {
47 | $data = $post[0]['graphql']['location'];
48 | } else {
49 | return array();
50 | }
51 | return self::json_location( $data, $url );
52 | }
53 |
54 | private static function json_location( $data, $url ) {
55 | $address = isset( $data['address_json'] ) ? json_decode( $data['address_json'], true ) : array();
56 | $jf2 = array(
57 | 'address' => $address,
58 | 'name' => ifset( $data['name'] ),
59 | 'latitude' => ifset( $data['lat'] ),
60 | 'longitude' => ifset( $data['lng'] ),
61 | 'url' => ifset( $data['website'] ),
62 | 'street_address' => ifset( $address['street_address'] ),
63 | 'postal_code' => ifset( $address['zip_code'] ),
64 | 'region' => ifset( $address['region_name'] ),
65 | 'country' => ifset( $address['country_code'] ),
66 | );
67 | return array_filter( $jf2 );
68 | }
69 |
70 | private static function feed( $data, $url ) {
71 | return self::profile( $data );
72 | }
73 |
74 | private static function html_photo( $data, $url ) {
75 | $post = $data['entry_data']['PostPage'];
76 | if ( isset( $post[0]['graphql']['shortcode_media'] ) ) {
77 | $data = $post[0]['graphql']['shortcode_media'];
78 | } elseif ( isset( $post[0]['graphql']['media'] ) ) {
79 | $data = $post[0]['graphql']['media'];
80 | } elseif ( isset( $post[0]['media'] ) ) {
81 | $data = $post[0]['media'];
82 | }
83 | return self::json_photo( $data, $url );
84 | }
85 |
86 | public static function json_photo( $data, $url ) {
87 | // Start building the h-entry
88 | $entry = array(
89 | 'type' => 'entry',
90 | 'url' => $url,
91 | );
92 |
93 | // Content and hashtags
94 | $caption = false;
95 |
96 | if ( isset( $data['caption'] ) ) {
97 | $caption = $data['caption'];
98 | } elseif ( isset( $data['edge_media_to_caption']['edges'][0]['node']['text'] ) ) {
99 | $caption = $data['edge_media_to_caption']['edges'][0]['node']['text'];
100 | }
101 |
102 | if ( $caption ) {
103 | if ( preg_match_all( '/#([a-z0-9_-]+)/i', $caption, $matches ) ) {
104 | $entry['category'] = array();
105 | foreach ( $matches[1] as $match ) {
106 | $entry['category'][] = $match;
107 | }
108 | }
109 |
110 | $entry['content'] = array(
111 | 'text' => $caption,
112 | );
113 | }
114 |
115 | // Include the photo/video media URLs
116 | // (Always return arrays, even for single images)
117 | if ( array_key_exists( 'edge_sidecar_to_children', $data ) ) {
118 | $entry['photo'] = array();
119 | foreach ( $data['edge_sidecar_to_children']['edges'] as $edge ) {
120 | $entry['photo'][] = $edge['node']['display_url'];
121 | }
122 | } else {
123 | // Single photo or video
124 | if ( array_key_exists( 'display_src', $data ) ) {
125 | $entry['photo'] = array( $data['display_src'] );
126 | } elseif ( array_key_exists( 'display_url', $data ) ) {
127 | $entry['photo'] = array( $data['display_url'] );
128 | }
129 |
130 | if ( isset( $data['is_video'] ) && $data['is_video'] && isset( $data['video_url'] ) ) {
131 | $entry['video'] = array( $data['video_url'] );
132 | }
133 | }
134 |
135 | // Published date
136 | $published = new Datetime();
137 | if ( isset( $data['taken_at_timestamp'] ) ) {
138 | $published->setTimestamp( $data['taken_at_timestamp'] );
139 | } elseif ( isset( $data['date'] ) ) {
140 | $published = new DateTime( $data['date'] );
141 | }
142 | $entry['published'] = $published->format( DATE_W3C );
143 | if ( isset( $data['location'] ) ) {
144 | $entry['location'] = array();
145 | if ( isset( $data['location']['address_json'] ) ) {
146 | $address = json_decode( $data['location']['address_json'], true );
147 | $entry['location'] = array(
148 | 'street_address' => $address['street_address'],
149 | 'postal_code' => $address['zip_code'],
150 | 'region' => $address['region_name'],
151 | 'country' => $address['country_code'],
152 | );
153 | }
154 | $entry['location']['name'] = $data['location']['name'];
155 | $entry['location']['url'] = sprintf( 'https://www.instagram.com/explore/locations/%1$s', $data['location']['id'] );
156 | $entry['location'] = array_filter( $entry['location'] );
157 | }
158 | if ( isset( $data['owner'] ) ) {
159 | $entry['author'] = array(
160 | 'type' => 'card',
161 | 'name' => ifset( $data['owner']['full_name'] ),
162 | 'nickname' => ifset( $data['owner']['username'] ),
163 | 'url' => sprintf( 'https://www.instagram.com/%1$s/', $data['owner']['username'] ),
164 | 'photo' => ifset( $data['owner']['profile_pic_url'] ),
165 | );
166 | }
167 | return $entry;
168 | }
169 |
170 | private static function profile( $data ) {
171 | if ( isset( $data['entry_data']['ProfilePage'][0] ) ) {
172 | $profile = $data['entry_data']['ProfilePage'][0];
173 | if ( $profile && isset( $profile['graphql']['user'] ) ) {
174 | $user = $profile['graphql']['user'];
175 | return $user;
176 | }
177 | }
178 | return array();
179 | }
180 |
181 |
182 |
183 | }
184 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/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 |
--------------------------------------------------------------------------------
/includes/kind-functions.php:
--------------------------------------------------------------------------------
1 | %2$s';
157 | $time_string = sprintf(
158 | $time_string,
159 | esc_attr( get_the_date( DATE_W3C, $post ) ),
160 | get_the_date( '', $post ),
161 | $date_cls
162 | );
163 | return sprintf( '%1$s - %3$s', kind_get_the_title( $post, $kind ), get_the_permalink( $post ), $time_string, esc_attr( $cls ) );
164 | }
165 |
166 |
167 | /**
168 | * Get a Generated Title for a Post as Most Post Kinds do Not Have an Explicit Title.
169 | *
170 | * @param WP_Post|null Post to Display.
171 | * @param array $args Arguments.
172 | * @return string Marked up link to a post.
173 | **/
174 | function kind_get_the_title( $post = null, $args = array() ) {
175 | $defaults = array(
176 | 'photo_size' => array( 32, 32 ),
177 | );
178 |
179 | $args = wp_parse_args( $args, $defaults );
180 |
181 | $post = get_post( $post );
182 | $kind = get_post_kind_slug( $post );
183 | $title = get_the_title( $post );
184 | $before = Kind_Taxonomy::get_before_kind( $kind );
185 | $content = '';
186 | $kind_post = new Kind_Post( $post );
187 |
188 | if ( ! empty( $title ) ) {
189 | return $title;
190 | }
191 |
192 | if ( in_array( $kind, array( 'audio', 'video', 'photo' ), true ) ) {
193 | switch ( $kind ) {
194 | case 'photo':
195 | $photos = $kind_post->get_photo();
196 | $before = wp_get_attachment_image(
197 | $photos[0],
198 | $args['photo_size'],
199 | false,
200 | array(
201 | 'class' => 'kind-photo-thumbnail',
202 | )
203 | );
204 | }
205 | } elseif ( ! in_array( $kind, array( 'note', 'article' ), true ) ) {
206 | $cite = $kind_post->get_cite( 'name' );
207 | if ( false === $cite ) {
208 | $content = Kind_View::get_post_type_string( $kind_post->get_cite( 'url' ) );
209 | } else {
210 | $content = $cite;
211 | }
212 | } else {
213 | $content = $post->post_excerpt;
214 | // If no excerpt use content
215 | if ( ! $content ) {
216 | $content = $post->post_content;
217 | }
218 | // If no content use date
219 | if ( $content ) {
220 | $content = mb_strimwidth( wp_strip_all_tags( $content ), 0, 40, '...' );
221 | }
222 | }
223 | if ( is_array( $content ) ) {
224 | $content = wp_json_encode( $content );
225 | }
226 |
227 | $content = apply_filters( 'kind_get_the_title_content', $content, $post );
228 | $before = apply_filters( 'kind_get_the_title_before', $before, $post );
229 |
230 | return trim( sprintf( '%1$s %2$s', $before, $content ) );
231 | }
232 |
--------------------------------------------------------------------------------
/lib/parse-this/includes/compat-functions.php:
--------------------------------------------------------------------------------
1 | getTimestamp();
36 | }
37 | }
38 |
39 |
40 | if ( ! function_exists( 'get_post_datetime' ) ) {
41 | /**
42 | * Retrieve post published or modified time as a `DateTime` object instance.
43 | *
44 | * The object will be set to the timezone from WordPress settings.
45 | *
46 | * @since 5.3.0 - backported to Parse This
47 | *
48 | * @param int|WP_Post $post Optional. WP_Post object or ID. Default is global `$post` object.
49 | * @param string $field Optional. Post field to use. Accepts 'date' or 'modified'.
50 | * @return DateTime|false Time object on success, false on failure.
51 | */
52 | function get_post_datetime( $post = null, $field = 'date' ) {
53 | $post = get_post( $post );
54 | if ( ! $post ) {
55 | return false;
56 | }
57 | $time = ( 'modified' === $field ) ? $post->post_modified : $post->post_date;
58 | if ( empty( $time ) || '0000-00-00 00:00:00' === $time ) {
59 | return false;
60 | }
61 | return date_create_immutable_from_format( 'Y-m-d H:i:s', $time, wp_timezone() );
62 | }
63 | }
64 |
65 | if ( ! function_exists( 'wp_timezone_string' ) ) {
66 | /**
67 | * Retrieves the timezone from site settings as a string.
68 | *
69 | * Uses the `timezone_string` option to get a proper timezone if available,
70 | * otherwise falls back to an offset.
71 | *
72 | * @since 5.3.0 - backported into Parse This
73 | *
74 | * @return string PHP timezone string or a ±HH:MM offset.
75 | */
76 | function wp_timezone_string() {
77 | $timezone_string = get_option( 'timezone_string' );
78 | if ( $timezone_string ) {
79 | return $timezone_string;
80 | }
81 | $offset = (float) get_option( 'gmt_offset' );
82 | $hours = (int) $offset;
83 | $minutes = ( $offset - $hours );
84 | $sign = ( $offset < 0 ) ? '-' : '+';
85 | $abs_hour = abs( $hours );
86 | $abs_mins = abs( $minutes * 60 );
87 | $tz_offset = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
88 | return $tz_offset;
89 | }
90 | }
91 |
92 | if ( ! function_exists( 'wp_timezone' ) ) {
93 | /**
94 | * Retrieves the timezone from site settings as a `DateTimeZone` object.
95 | *
96 | * Timezone can be based on a PHP timezone string or a ±HH:MM offset.
97 | *
98 | * @since 5.3.0 - backported into Parse This
99 | *
100 | * @return DateTimeZone Timezone object.
101 | */
102 | function wp_timezone() {
103 | return new DateTimeZone( wp_timezone_string() );
104 | }
105 | }
106 |
107 |
108 | if ( ! function_exists( 'wp_date' ) ) {
109 | /**
110 | * Retrieves the date, in localized format.
111 | *
112 | * This is a newer function, intended to replace `date_i18n()` without legacy quirks in it.
113 | *
114 | * Note that, unlike `date_i18n()`, this function accepts a true Unix timestamp, not summed
115 | * with timezone offset.
116 | *
117 | * @since 5.3.0 - backported to Parse This
118 | *
119 | * @param string $format PHP date format.
120 | * @param int $timestamp Optional. Unix timestamp. Defaults to current time.
121 | * @param DateTimeZone $timezone Optional. Timezone to output result in. Defaults to timezone
122 | * from site settings.
123 | * @return string|false The date, translated if locale specifies it. False on invalid timestamp input.
124 | */
125 | function wp_date( $format, $timestamp = null, $timezone = null ) {
126 | global $wp_locale;
127 | if ( null === $timestamp ) {
128 | $timestamp = time();
129 | } elseif ( ! is_numeric( $timestamp ) ) {
130 | return false;
131 | }
132 | if ( ! $timezone ) {
133 | $timezone = wp_timezone();
134 | }
135 | $datetime = date_create( '@' . $timestamp );
136 | $datetime->setTimezone( $timezone );
137 | if ( empty( $wp_locale->month ) || empty( $wp_locale->weekday ) ) {
138 | $date = $datetime->format( $format );
139 | } else {
140 | // We need to unpack shorthand `r` format because it has parts that might be localized.
141 | $format = preg_replace( '/(?get_month( $datetime->format( 'm' ) );
145 | $weekday = $wp_locale->get_weekday( $datetime->format( 'w' ) );
146 | for ( $i = 0; $i < $format_length; $i ++ ) {
147 | switch ( $format[ $i ] ) {
148 | case 'D':
149 | $new_format .= backslashit( $wp_locale->get_weekday_abbrev( $weekday ) );
150 | break;
151 | case 'F':
152 | $new_format .= backslashit( $month );
153 | break;
154 | case 'l':
155 | $new_format .= backslashit( $weekday );
156 | break;
157 | case 'M':
158 | $new_format .= backslashit( $wp_locale->get_month_abbrev( $month ) );
159 | break;
160 | case 'a':
161 | $new_format .= backslashit( $wp_locale->get_meridiem( $datetime->format( 'a' ) ) );
162 | break;
163 | case 'A':
164 | $new_format .= backslashit( $wp_locale->get_meridiem( $datetime->format( 'A' ) ) );
165 | break;
166 | case '\\':
167 | $new_format .= $format[ $i ];
168 | // If character follows a slash, we add it without translating.
169 | if ( $i < $format_length ) {
170 | $new_format .= $format[ ++$i ];
171 | }
172 | break;
173 | default:
174 | $new_format .= $format[ $i ];
175 | break;
176 | }
177 | }
178 | $date = $datetime->format( $new_format );
179 | $date = wp_maybe_decline_date( $date );
180 | }
181 | /**
182 | * Filters the date formatted based on the locale.
183 | *
184 | * @since 5.3.0 but backported to Parse This
185 | *
186 | * @param string $date Formatted date string.
187 | * @param string $format Format to display the date.
188 | * @param int $timestamp Unix timestamp.
189 | * @param DateTimeZone $timezone Timezone.
190 | */
191 | $date = apply_filters( 'wp_date', $date, $format, $timestamp, $timezone );
192 | return $date;
193 | }
194 | }
195 |
196 | if ( ! function_exists( 'str_contains' ) ) {
197 | function str_contains( $haystack, $needle ) {
198 | return $needle !== '' && false !== mb_strpos( $haystack, $needle );
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/js/citation.js:
--------------------------------------------------------------------------------
1 | jQuery( document ).ready( function( $ ) {
2 | var elem = document.createElement( 'input' );
3 |
4 | elem.setAttribute( 'type', 'date' );
5 | if ( 'text' === elem.type ) {
6 | $( '#mf2_start_date' ).datepicker({
7 | dateFormat: 'yy-mm-dd'
8 | });
9 | $( '#mf2_end_date' ).datepicker({
10 | dateFormat: 'yy-mm-dd'
11 | });
12 | $( '#cite_published_date' ).datepicker({
13 | dateFormat: 'yy-mm-dd'
14 | });
15 | $( '#cite_updated_date' ).datepicker({
16 | dateFormat: 'yy-mm-dd'
17 | });
18 |
19 | }
20 | elem.setAttribute( 'type', 'time' );
21 | if ( 'text' === elem.type ) {
22 | $( '#mf2_start_time' ).timepicker({
23 | timeFormat: 'H:i:s',
24 | step: 15
25 | });
26 | $( '#mf2_end_time' ).timepicker({
27 | timeFormat: 'H:i:s',
28 | step: 15
29 | });
30 | $( '#cite_published_time' ).timepicker({
31 | timeFormat: 'H:i:s',
32 | step: 15
33 | });
34 | $( '#cite_updated_time' ).timepicker({
35 | timeFormat: 'H:i:s',
36 | step: 15
37 | });
38 |
39 | }
40 | $( '#duration' ).datepair();
41 |
42 | function clearPostProperties() {
43 | var fieldIds = [
44 | 'cite_url',
45 | 'cite_name',
46 | 'cite_summary',
47 | 'cite_tags',
48 | 'cite_media',
49 | 'cite_author_name',
50 | 'cite_author_url',
51 | 'cite_author_photo',
52 | 'cite_featured',
53 | 'cite_publication',
54 | 'mf2_rsvp',
55 | 'mf2_start_time',
56 | 'mf2_start_date',
57 | 'mf2_end_time',
58 | 'mf2_end_date',
59 | 'cite_published_time',
60 | 'cite_published_date',
61 | 'cite_updated_time',
62 | 'cite_updated_date',
63 | 'duration_years',
64 | 'duration_months',
65 | 'duration_days',
66 | 'duration_hours',
67 | 'duration_minutes',
68 | 'duration_seconds'
69 | ];
70 | if ( ! confirm( PKAPI.clear_message ) ) {
71 | return;
72 | }
73 | $.each( fieldIds, function( count, val ) {
74 | document.getElementById( val ).value = '';
75 | });
76 | $( '#kind-media-container' ).addClass( 'hidden' );
77 | $( '#kind-media-container' ).children( 'img' ).hide();
78 | $( '#add-kind-media' ).show();
79 |
80 | }
81 |
82 | function addhttp( url ) {
83 | if ( ! /^(?:f|ht)tps?\:\/\//.test( url ) ) {
84 | url = 'http://' + url;
85 | }
86 | return url;
87 | }
88 |
89 | function showLoadingSpinner() {
90 | $( '#replybox-meta' ).addClass( 'is-loading' );
91 | }
92 |
93 | function hideLoadingSpinner() {
94 | $( '#replybox-meta' ).removeClass( 'is-loading' );
95 | }
96 |
97 | //function used to validate website URL
98 | function checkUrl( url ) {
99 |
100 | //regular expression for URL
101 | var pattern = /^(http|https)?:\/\/[a-zA-Z0-9-\.]+\.[a-z]{2,4}/;
102 |
103 | if ( pattern.test( url ) ) {
104 | return true;
105 | } else {
106 | return false;
107 | }
108 | }
109 |
110 | function getLinkCitation() {
111 | if ( '' === $( '#mf2_url' ).val() ) {
112 | return;
113 | }
114 | $.ajax({
115 | type: 'GET',
116 |
117 | // Here we supply the endpoint url, as opposed to the action in the data object with the admin-ajax method
118 | url: PKAPI.api_url + 'parse/',
119 | beforeSend: function( xhr ) {
120 |
121 | // Here we set a header 'X-WP-Nonce' with the nonce as opposed to the nonce in the data object with admin-ajax
122 | xhr.setRequestHeader( 'X-WP-Nonce', PKAPI.api_nonce );
123 | },
124 | data: {
125 | url: $( '#mf2_url' ).val(),
126 | follow: true
127 | },
128 | success: function( response ) {
129 | var published;
130 | var updated;
131 | if ( 'undefined' === typeof response ) {
132 | alert( 'Error: Unable to Retrieve' );
133 | return;
134 | }
135 | if ( 'message' in response ) {
136 | alert( response.message );
137 | return;
138 | }
139 | if ( 'name' in response ) {
140 | $( '#title' ).val( response.name );
141 | }
142 | if ( 'publication' in response ) {
143 | $( '#mf2_publication' ).val( response.publication ) ;
144 | }
145 | if ( 'published' in response ) {
146 | published = moment.parseZone( response.published );
147 | $( '#mf2_published_date' ).val( published.format( 'YYYY-MM-DD' ) ) ;
148 | $( '#mf2_published_time' ).val( published.format( 'HH:mm:ss' ) ) ;
149 | $( '#mf2_published_offset' ).val( published.format( 'Z' ) );
150 | }
151 | if ( 'updated' in response ) {
152 | updated = moment.parseZone( response.updated );
153 | $( '#mf2_updated_date' ).val( updated.format( 'YYYY-MM-DD' ) ) ;
154 | $( '#mf2_updated_time' ).val( updated.format( 'HH:mm:ss' ) ) ;
155 | $( '#mf2_updated_offset' ).val( updated.format( 'Z' ) );
156 | }
157 | if ( 'summary' in response ) {
158 | $( '#excerpt' ).val( response.summary ) ;
159 | }
160 | if ( 'content' in response ) {
161 | $( '#content' ).val( response.content.text ) ;
162 | }
163 | if ( 'featured' in response ) {
164 | $( '#mf2_featured' ).val( response.featured ) ;
165 | }
166 | if ( ( 'author' in response ) && ( 'string' != typeof response.author ) ) {
167 | if ( 'name' in response.author ) {
168 | if ( 'string' === typeof response.author.name ) {
169 | $( '#mf2_author_name' ).val( response.author.name );
170 | } else {
171 | $( '#mf2_author_name' ).val( response.author.name.join( ';' ) ) ;
172 | }
173 | }
174 | if ( 'photo' in response.author ) {
175 | if ( 'string' === typeof response.author.name ) {
176 | $( '#mf2_author_photo' ).val( response.author.photo );
177 | } else {
178 | $( '#mf2_author_photo' ).val( response.author.photo.join( ';' ) ) ;
179 | }
180 | }
181 | if ( 'url' in response.author ) {
182 | if ( 'string' === typeof response.author.url ) {
183 | $( '#mf2_author_url' ).val( response.author.url );
184 | } else {
185 | $( '#mf2_author_url' ).val( response.author.url.join( ';' ) ) ;
186 | }
187 | }
188 | }
189 | if ( 'category' in response ) {
190 | if ( 'object' === typeof response.category ) {
191 | $( '#new-tag-citation_tags' ).val( response.category.join( ',' ) );
192 | }
193 | }
194 | alert( PKAPI.success_message );
195 | console.log( response );
196 | },
197 | fail: function( response ) {
198 | console.log( response );
199 | alert( response.message );
200 | },
201 | error: function( jqXHR, textStatus, errorThrown ) {
202 | alert( jqXHR.responseJSON.message );
203 | console.log( jqXHR );
204 | },
205 | always: hideLoadingSpinner()
206 | });
207 | }
208 |
209 | jQuery( document )
210 | .on( 'click', '#lookup-citation', function( event ) {
211 | getLinkCitation();
212 | event.preventDefault();
213 | })
214 | });
215 |
--------------------------------------------------------------------------------
/includes/class-kind-plugins.php:
--------------------------------------------------------------------------------
1 | get_kind() ) {
71 | case 'note':
72 | $post_array['type'] = 'Note';
73 | break;
74 | case 'photo':
75 | $post_array['type'] = 'Image';
76 | break;
77 | case 'video':
78 | $post_array['type'] = 'Video';
79 | break;
80 | case 'audio':
81 | $post_array['type'] = 'Audio';
82 | break;
83 | case 'article':
84 | $post_array['type'] = 'Article';
85 | break;
86 | case 'reply':
87 | $post_array['type'] = 'Note';
88 | $post_array['inReplyTo'] = $kind->get_cite( 'url' );
89 | break;
90 | }
91 | }
92 | return $post_array;
93 | }
94 |
95 | public static function hum_local_types( $types ) {
96 | // http://tantek.pbworks.com/w/page/21743973/Whistle#design - Some of the uses are modified based on design considerations noted.
97 | $types[] = 'f'; // Favorited, Likes, etc
98 | $types[] = 'e'; // Events
99 | $types[] = 'g'; // Geo Checkin
100 | $types[] = 'h'; // Link
101 | $types[] = 'm'; // Metric
102 | $types[] = 'q'; // Question
103 | $types[] = 'r'; // Review
104 | $types[] = 'x'; // Experience
105 | $types[] = 'u'; // Status Update
106 | return $types;
107 | }
108 |
109 | public static function hum_type_prefix( $prefix, $post_id ) {
110 | $post_type = get_post_type( $post_id );
111 | if ( 'post' !== $post_type ) {
112 | return $prefix;
113 | }
114 |
115 | $kind = get_post_kind_slug( $post_id );
116 | $shortlink = Kind_Taxonomy::get_kind_info( $kind, 'shortlink' );
117 | if ( ! empty( $shortlink ) ) {
118 | return $shortlink;
119 | }
120 | return $prefix;
121 | }
122 |
123 | /**
124 | * Replaces need for replacing the entire excerpt.
125 | *
126 | * @access public
127 | *
128 | * @param string $post_type Post type slug.
129 | * @param int $post_id Post ID.
130 | * @return string
131 | */
132 | public static function semantic_post_type( $post_type, $post_id ) {
133 | return _x( 'this', 'direct article', 'indieweb-post-kinds' ) . ' ' . strtolower( get_post_kind( $post_id ) );
134 | }
135 |
136 | /**
137 | * Take mf2 properties and set a post kind.
138 | * Implements Post Type Discovery https://www.w3.org/TR/post-type-discovery/
139 | *
140 | * @param array $input Micropub Request in JSON.
141 | * @param array $wp_args Arguments passed to insert or update posts.
142 | */
143 | public static function micropub_set_kind( $input, $wp_args ) {
144 | // Only continue if create or update
145 | if ( ! $wp_args ) {
146 | return;
147 | }
148 | $type = post_type_discovery( mf2_to_jf2( $input ) );
149 | if ( ! empty( $type ) ) {
150 | set_post_kind( $wp_args['ID'], $type );
151 | }
152 | }
153 |
154 | /**
155 | * Set our post formats.
156 | *
157 | * @access public
158 | *
159 | * @param $input
160 | * @param $wp_args
161 | */
162 | public static function post_formats( $input, $wp_args ) {
163 | if ( empty( $wp_args ) || empty( $input ) ) {
164 | return;
165 | }
166 | $kind = get_post_kind_slug( $wp_args['ID'] );
167 | set_post_format( $wp_args['ID'], Kind_Taxonomy::get_kind_info( $kind, 'format' ) );
168 | }
169 |
170 | /**
171 | * Parse our micropub values.
172 | *
173 | * @access public
174 | *
175 | * @param array $input Array of inputs to parse.
176 | * @return mixed
177 | */
178 | public static function micropub_parse( $input ) {
179 | if ( ! $input ) {
180 | return $input;
181 | }
182 | // q indicates a get query
183 | if ( isset( $input['q'] ) ) {
184 | return $input;
185 | }
186 | if ( ! isset( $input['properties'] ) ) {
187 | return $input;
188 | }
189 | $parsed = array( 'bookmark-of', 'like-of', 'favorite-of', 'in-reply-to', 'read-of', 'listen-of', 'watch-of' );
190 | foreach ( $input['properties'] as $property => $value ) {
191 | if ( in_array( $property, $parsed, true ) ) {
192 | if ( wp_is_numeric_array( $value ) ) {
193 | foreach ( $value as $i => $v ) {
194 | if ( wp_http_validate_url( $v ) ) {
195 | $parse = new Parse_This( $v );
196 | $fetch = $parse->fetch();
197 | if ( ! is_wp_error( $fetch ) ) {
198 | $parse->parse();
199 | $jf2 = $parse->get();
200 | // Entries become citations
201 | if ( 'entry' === $jf2['type'] ) {
202 | $jf2['type'] = 'cite';
203 | }
204 | $mf2 = jf2_to_mf2( $jf2 );
205 | $input['properties'][ $property ][ $i ] = $mf2;
206 | } else {
207 | error_log( wp_json_encode( $fetch ) ); // phpcs:ignore
208 | }
209 | }
210 | }
211 | } elseif ( isset( $value['url'] ) && wp_http_validate_url( $value['url'] ) ) {
212 | $parse = new Parse_This( $value['url'] );
213 | $fetch = $parse - fetch();
214 | if ( ! is_wp_error( $fetch ) ) {
215 | $parse->parse();
216 | $input['properties'][ $property ] = array_merge( $value, jf2_to_mf2( $parse->get() ) );
217 | }
218 | }
219 | }
220 | }
221 | return $input;
222 | }
223 | } // End Class Kind_Plugins
224 |
--------------------------------------------------------------------------------
/lib/parse-this/lib/html5/HTML5/Parser/UTF8Utils.php:
--------------------------------------------------------------------------------
1 |
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a
11 | copy of this software and associated documentation files (the
12 | "Software"), to deal in the Software without restriction, including
13 | without limitation the rights to use, copy, modify, merge, publish,
14 | distribute, sublicense, and/or sell copies of the Software, and to
15 | permit persons to whom the Software is furnished to do so, subject to
16 | the following conditions:
17 |
18 | The above copyright notice and this permission notice shall be included
19 | in all copies or substantial portions of the Software.
20 |
21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
26 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
27 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 | */
29 |
30 | use Masterminds\HTML5\Exception;
31 |
32 | class UTF8Utils
33 | {
34 | /**
35 | * The Unicode replacement character.
36 | */
37 | const FFFD = "\xEF\xBF\xBD";
38 |
39 | /**
40 | * Count the number of characters in a string.
41 | * UTF-8 aware. This will try (in order) iconv, MB, libxml, and finally a custom counter.
42 | *
43 | * @param string $string
44 | *
45 | * @return int
46 | */
47 | public static function countChars($string)
48 | {
49 | // Get the length for the string we need.
50 | if (function_exists('mb_strlen')) {
51 | return mb_strlen($string, 'utf-8');
52 | }
53 |
54 | if (function_exists('iconv_strlen')) {
55 | return iconv_strlen($string, 'utf-8');
56 | }
57 |
58 | if (function_exists('utf8_decode')) {
59 | // MPB: Will this work? Won't certain decodes lead to two chars
60 | // extrapolated out of 2-byte chars?
61 | return strlen(utf8_decode($string));
62 | }
63 |
64 | $count = count_chars($string);
65 |
66 | // 0x80 = 0x7F - 0 + 1 (one added to get inclusive range)
67 | // 0x33 = 0xF4 - 0x2C + 1 (one added to get inclusive range)
68 | return array_sum(array_slice($count, 0, 0x80)) + array_sum(array_slice($count, 0xC2, 0x33));
69 | }
70 |
71 | /**
72 | * Convert data from the given encoding to UTF-8.
73 | *
74 | * This has not yet been tested with charactersets other than UTF-8.
75 | * It should work with ISO-8859-1/-13 and standard Latin Win charsets.
76 | *
77 | * @param string $data The data to convert
78 | * @param string $encoding A valid encoding. Examples: http://www.php.net/manual/en/mbstring.supported-encodings.php
79 | *
80 | * @return string
81 | */
82 | public static function convertToUTF8($data, $encoding = 'UTF-8')
83 | {
84 | /*
85 | * From the HTML5 spec: Given an encoding, the bytes in the input stream must be converted
86 | * to Unicode characters for the tokeniser, as described by the rules for that encoding,
87 | * except that the leading U+FEFF BYTE ORDER MARK character, if any, must not be stripped
88 | * by the encoding layer (it is stripped by the rule below). Bytes or sequences of bytes
89 | * in the original byte stream that could not be converted to Unicode characters must be
90 | * converted to U+FFFD REPLACEMENT CHARACTER code points.
91 | */
92 |
93 | // mb_convert_encoding is chosen over iconv because of a bug. The best
94 | // details for the bug are on http://us1.php.net/manual/en/function.iconv.php#108643
95 | // which contains links to the actual but reports as well as work around
96 | // details.
97 | if (function_exists('mb_convert_encoding')) {
98 | // mb library has the following behaviors:
99 | // - UTF-16 surrogates result in false.
100 | // - Overlongs and outside Plane 16 result in empty strings.
101 |
102 | // Before we run mb_convert_encoding we need to tell it what to do with
103 | // characters it does not know. This could be different than the parent
104 | // application executing this library so we store the value, change it
105 | // to our needs, and then change it back when we are done. This feels
106 | // a little excessive and it would be great if there was a better way.
107 | $save = mb_substitute_character();
108 | mb_substitute_character('none');
109 | $data = mb_convert_encoding($data, 'UTF-8', $encoding);
110 | mb_substitute_character($save);
111 | }
112 | // @todo Get iconv running in at least some environments if that is possible.
113 | elseif (function_exists('iconv') && 'auto' !== $encoding) {
114 | // fprintf(STDOUT, "iconv found\n");
115 | // iconv has the following behaviors:
116 | // - Overlong representations are ignored.
117 | // - Beyond Plane 16 is replaced with a lower char.
118 | // - Incomplete sequences generate a warning.
119 | $data = @iconv($encoding, 'UTF-8//IGNORE', $data);
120 | } else {
121 | throw new Exception('Not implemented, please install mbstring or iconv');
122 | }
123 |
124 | /*
125 | * One leading U+FEFF BYTE ORDER MARK character must be ignored if any are present.
126 | */
127 | if ("\xEF\xBB\xBF" === substr($data, 0, 3)) {
128 | $data = substr($data, 3);
129 | }
130 |
131 | return $data;
132 | }
133 |
134 | /**
135 | * Checks for Unicode code points that are not valid in a document.
136 | *
137 | * @param string $data A string to analyze
138 | *
139 | * @return array An array of (string) error messages produced by the scanning
140 | */
141 | public static function checkForIllegalCodepoints($data)
142 | {
143 | // Vestigal error handling.
144 | $errors = array();
145 |
146 | /*
147 | * All U+0000 null characters in the input must be replaced by U+FFFD REPLACEMENT CHARACTERs.
148 | * Any occurrences of such characters is a parse error.
149 | */
150 | for ($i = 0, $count = substr_count($data, "\0"); $i < $count; ++$i) {
151 | $errors[] = 'null-character';
152 | }
153 |
154 | /*
155 | * Any occurrences of any characters in the ranges U+0001 to U+0008, U+000B, U+000E to U+001F, U+007F
156 | * to U+009F, U+D800 to U+DFFF , U+FDD0 to U+FDEF, and characters U+FFFE, U+FFFF, U+1FFFE, U+1FFFF,
157 | * U+2FFFE, U+2FFFF, U+3FFFE, U+3FFFF, U+4FFFE, U+4FFFF, U+5FFFE, U+5FFFF, U+6FFFE, U+6FFFF, U+7FFFE,
158 | * U+7FFFF, U+8FFFE, U+8FFFF, U+9FFFE, U+9FFFF, U+AFFFE, U+AFFFF, U+BFFFE, U+BFFFF, U+CFFFE, U+CFFFF,
159 | * U+DFFFE, U+DFFFF, U+EFFFE, U+EFFFF, U+FFFFE, U+FFFFF, U+10FFFE, and U+10FFFF are parse errors.
160 | * (These are all control characters or permanently undefined Unicode characters.)
161 | */
162 | // Check PCRE is loaded.
163 | $count = preg_match_all(
164 | '/(?:
165 | [\x01-\x08\x0B\x0E-\x1F\x7F] # U+0001 to U+0008, U+000B, U+000E to U+001F and U+007F
166 | |
167 | \xC2[\x80-\x9F] # U+0080 to U+009F
168 | |
169 | \xED(?:\xA0[\x80-\xFF]|[\xA1-\xBE][\x00-\xFF]|\xBF[\x00-\xBF]) # U+D800 to U+DFFFF
170 | |
171 | \xEF\xB7[\x90-\xAF] # U+FDD0 to U+FDEF
172 | |
173 | \xEF\xBF[\xBE\xBF] # U+FFFE and U+FFFF
174 | |
175 | [\xF0-\xF4][\x8F-\xBF]\xBF[\xBE\xBF] # U+nFFFE and U+nFFFF (1 <= n <= 10_{16})
176 | )/x', $data, $matches);
177 | for ($i = 0; $i < $count; ++$i) {
178 | $errors[] = 'invalid-codepoint';
179 | }
180 |
181 | return $errors;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/lib/parse-this/includes/class-parse-this-base.php:
--------------------------------------------------------------------------------
1 | format( DATE_W3C );
40 | }
41 |
42 | public static function validate_email( $email ) {
43 | return filter_var( $email, FILTER_VALIDATE_EMAIL );
44 | }
45 |
46 | /**
47 | *
48 | */
49 | protected static function find_last_updated( $items ) {
50 | $items = self::order_by_date( $items, 'updated' );
51 | $return = new DateTime( $items[0]['updated'], wp_timezone() );
52 | return $return->format( DATE_W3C );
53 | }
54 |
55 | /**
56 | * Utility method to limit an array to 100 values.
57 | * Originally set to 50 but some sites are very detailed in their meta.
58 | *
59 | * @ignore
60 | * @since 4.2.0
61 | *
62 | * @param array $value Array to limit.
63 | * @return array Original array if fewer than 100 values, limited array, empty array otherwise.
64 | */
65 | protected static function limit_array( $value ) {
66 | if ( is_array( $value ) ) {
67 | if ( count( $value ) > 100 ) {
68 | return array_slice( $value, 0, 100 );
69 | }
70 |
71 | return $value;
72 | }
73 |
74 | return array();
75 | }
76 |
77 | /**
78 | * Utility method to limit the length of a given string to 5,000 characters.
79 | *
80 | * @ignore
81 | * @since 4.2.0
82 | *
83 | * @param string $value String to limit.
84 | * @return bool|int|string If boolean or integer, that value. If a string, the original value
85 | * if fewer than 5,000 characters, a truncated version, otherwise an
86 | * empty string.
87 | */
88 | protected static function limit_string( $value ) {
89 | $return = '';
90 | if ( is_numeric( $value ) || is_bool( $value ) ) {
91 | $return = $value;
92 | } elseif ( is_string( $value ) ) {
93 | if ( mb_strlen( $value ) > 5000 ) {
94 | $return = mb_substr( $value, 0, 5000 );
95 | } else {
96 | $return = $value;
97 | }
98 | $return = sanitize_text_field( trim( $return ) );
99 | }
100 |
101 | return $return;
102 | }
103 |
104 | /**
105 | * Utility method to limit a given URL to 2,048 characters.
106 | *
107 | * @ignore
108 | * @since 4.2.0
109 | *
110 | * @param string $url URL to check for length and validity.
111 | * @param string $source_url URL URL to use to resolve relative URLs
112 | * @return string Escaped URL if of valid length (< 2048) and makeup. Empty string otherwise.
113 | */
114 | protected static function limit_url( $url, $source_url ) {
115 | if ( ! is_string( $url ) ) {
116 | return '';
117 | }
118 |
119 | // HTTP 1.1 allows 8000 chars but the "de-facto" standard supported in all current browsers is 2048.
120 | if ( strlen( $url ) > 2048 ) {
121 | return ''; // Return empty rather than a truncated/invalid URL
122 | }
123 |
124 | // Does not look like a URL.
125 | if ( ! filter_var( $url, FILTER_VALIDATE_URL ) ) {
126 | return '';
127 | }
128 |
129 | $url = pt_make_absolute_url( $url, $source_url );
130 |
131 | return esc_url_raw( $url, array( 'http', 'https' ) );
132 | }
133 |
134 | /**
135 | * Utility method to limit image source URLs.
136 | *
137 | * Excluded URLs include share-this type buttons, loaders, spinners, spacers, WordPress interface images,
138 | * tiny buttons or thumbs, mathtag.com or quantserve.com images, or the WordPress.com stats gif.
139 | *
140 | * @param string $src Image source URL.
141 | * @return string If not matched an excluded URL type, the original URL, empty string otherwise.
142 | */
143 | protected static function limit_img( $src, $source_url ) {
144 | $src = self::limit_url( $src, $source_url );
145 |
146 | if ( preg_match( '!/ad[sx]?/!i', $src ) ) {
147 | // Ads
148 | return '';
149 | } elseif ( preg_match( '!(/share-?this[^.]+?\.[a-z0-9]{3,4})(\?.*)?$!i', $src ) ) {
150 | // Share-this type button
151 | return '';
152 | } elseif ( preg_match( '!/(spinner|loading|spacer|blank|rss)\.(gif|jpg|png)!i', $src ) ) {
153 | // Loaders, spinners, spacers
154 | return '';
155 | } elseif ( preg_match( '!/([^./]+[-_])?(spinner|loading|spacer|blank)s?([-_][^./]+)?\.[a-z0-9]{3,4}!i', $src ) ) {
156 | // Fancy loaders, spinners, spacers
157 | return '';
158 | } elseif ( preg_match( '!([^./]+[-_])?thumb[^.]*\.(gif|jpg|png)$!i', $src ) ) {
159 | // Thumbnails, too small, usually irrelevant to context
160 | return '';
161 | } elseif ( false !== stripos( $src, '/wp-includes/' ) ) {
162 | // Classic WordPress interface images
163 | return '';
164 | } elseif ( false !== stripos( $src, '/wp-content/themes' ) ) {
165 | // Anything within a WordPress theme directory
166 | return '';
167 | } elseif ( false !== stripos( $src, '/wp-content/plugins' ) ) {
168 | // Anything within a WordPress plugin directory
169 | return '';
170 | } elseif ( preg_match( '![^\d]\d{1,2}x\d+\.(gif|jpg|png)$!i', $src ) ) {
171 | // Most often tiny buttons/thumbs (< 100px wide)
172 | return '';
173 | } elseif ( preg_match( '!/pixel\.(mathtag|quantserve)\.com!i', $src ) ) {
174 | // See mathtag.com and https://www.quantcast.com/how-we-do-it/iab-standard-measurement/how-we-collect-data/
175 | return '';
176 | } elseif ( preg_match( '!/[gb]\.gif(\?.+)?$!i', $src ) ) {
177 | // WordPress.com stats gif
178 | return '';
179 | }
180 | // Optionally add additional limits
181 | return apply_filters( 'parse_this_img_filters', $src );
182 | }
183 |
184 | /**
185 | * Limit embed source URLs to specific providers.
186 | *
187 | * Not all core oEmbed providers are supported. Supported providers include YouTube, Vimeo,
188 | * Vine, Daily Motion, SoundCloud, and Twitter.
189 | *
190 | * @param string $src Embed source URL.
191 | * @param string $source_url Source URL
192 | * @return string If not from a supported provider, an empty string. Otherwise, a reformatted embed URL.
193 | */
194 | protected static function limit_embed( $src, $source_url ) {
195 | $src = self::limit_url( $src, $source_url );
196 |
197 | if ( empty( $src ) ) {
198 | return '';
199 | }
200 |
201 | if ( preg_match( '!//(m|www)\.youtube\.com/(embed|v)/([^?]+)\?.+$!i', $src, $src_matches ) ) {
202 | // Embedded Youtube videos (www or mobile)
203 | $src = 'https://www.youtube.com/watch?v=' . $src_matches[3];
204 | } elseif ( preg_match( '!//player\.vimeo\.com/video/([\d]+)([?/].*)?$!i', $src, $src_matches ) ) {
205 | // Embedded Vimeo iframe videos
206 | $src = 'https://vimeo.com/' . (int) $src_matches[1];
207 | } elseif ( preg_match( '!//vimeo\.com/moogaloop\.swf\?clip_id=([\d]+)$!i', $src, $src_matches ) ) {
208 | // Embedded Vimeo Flash videos
209 | $src = 'https://vimeo.com/' . (int) $src_matches[1];
210 | } elseif ( preg_match( '!//vine\.co/v/([^/]+)/embed!i', $src, $src_matches ) ) {
211 | // Embedded Vine videos
212 | $src = 'https://vine.co/v/' . $src_matches[1];
213 | } elseif ( preg_match( '!//(www\.)?dailymotion\.com/embed/video/([^/?]+)([/?].+)?!i', $src, $src_matches ) ) {
214 | // Embedded Daily Motion videos
215 | $src = 'https://www.dailymotion.com/video/' . $src_matches[2];
216 | } else {
217 | $oembed = _wp_oembed_get_object();
218 |
219 | if ( ! $oembed->get_provider(
220 | $src,
221 | array(
222 | 'discover' => false,
223 | )
224 | ) ) {
225 | $src = '';
226 | }
227 | }
228 |
229 | return $src;
230 | }
231 |
232 | public static function set( $array, $key, $value ) {
233 | if ( ! isset( $array[ $key ] ) ) {
234 | $array[ $key ] = $value;
235 | } elseif ( is_string( $array[ $key ] ) ) {
236 | $array[ $key ] = array( $array[ $key ], $value );
237 | } elseif ( is_array( $array[ $key ] ) ) {
238 | $array[ $key ][] = $value;
239 | }
240 | return $array;
241 | }
242 | }
243 |
--------------------------------------------------------------------------------