├── .distignore ├── .editorconfig ├── .eslintrc.js ├── css ├── kind.admin.min.css ├── kind.admin.min.css.map ├── kind.css ├── kind.css.map ├── kind.min.css └── kind.min.css.map ├── develop.txt ├── fontawesome-license.txt ├── includes ├── class-kind-config.php ├── class-kind-fields.php ├── class-kind-media-metadata.php ├── class-kind-media-view.php ├── class-kind-menu-widget.php ├── class-kind-metabox.php ├── class-kind-plugins.php ├── class-kind-post-widget.php ├── class-kind-post.php ├── class-kind-taxonomy.php ├── class-kind-view.php ├── class-mf2-post.php ├── class-post-kind.php ├── compat.php ├── kind-functions.php ├── register-kinds.php └── time-functions.php ├── indieweb-post-kinds.php ├── js ├── citation.js ├── clone-media-fragment.js └── kind.js ├── languages └── indieweb-post-kinds.pot ├── lib └── parse-this │ ├── includes │ ├── autoload.php │ ├── class-parse-this-base.php │ ├── class-parse-this-discovery.php │ ├── class-parse-this-html.php │ ├── class-parse-this-instagram.php │ ├── class-parse-this-json.php │ ├── class-parse-this-jsonfeed.php │ ├── class-parse-this-jsonld.php │ ├── class-parse-this-mf2-utils.php │ ├── class-parse-this-mf2.php │ ├── class-parse-this-opml.php │ ├── class-parse-this-restapi.php │ ├── class-parse-this-rss.php │ ├── class-parse-this-twitter.php │ ├── class-parse-this-youtube.php │ ├── class-parse-this.php │ ├── class-rest-parse-this.php │ ├── compat-functions.php │ └── functions.php │ ├── lib │ ├── html5 │ │ ├── HTML5.php │ │ ├── HTML5 │ │ │ ├── Elements.php │ │ │ ├── Entities.php │ │ │ ├── Exception.php │ │ │ ├── InstructionProcessor.php │ │ │ ├── Parser │ │ │ │ ├── CharacterReference.php │ │ │ │ ├── DOMTreeBuilder.php │ │ │ │ ├── EventHandler.php │ │ │ │ ├── FileInputStream.php │ │ │ │ ├── InputStream.php │ │ │ │ ├── ParseError.php │ │ │ │ ├── README.md │ │ │ │ ├── Scanner.php │ │ │ │ ├── StringInputStream.php │ │ │ │ ├── Tokenizer.php │ │ │ │ ├── TreeBuildingRules.php │ │ │ │ └── UTF8Utils.php │ │ │ └── Serializer │ │ │ │ ├── HTML5Entities.php │ │ │ │ ├── OutputRules.php │ │ │ │ ├── README.md │ │ │ │ ├── RulesInterface.php │ │ │ │ └── Traverser.php │ │ ├── README.md │ │ ├── RELEASE.md │ │ ├── UPGRADING.md │ │ └── autoloader.php │ └── mf2 │ │ ├── LICENSE.md │ │ ├── Parser.php │ │ └── README.md │ ├── parse-this.php │ ├── readme.md │ └── readme.txt ├── readme.txt ├── svgs ├── acquisition.svg ├── article.svg ├── audio.svg ├── bookmark.svg ├── checkin.svg ├── craft.svg ├── drink.svg ├── eat.svg ├── event.svg ├── exercise.svg ├── favorite.svg ├── firehose.svg ├── follow.svg ├── issue.svg ├── itinerary.svg ├── jam.svg ├── like.svg ├── listen.svg ├── mood.svg ├── note.svg ├── photo.svg ├── play.svg ├── question.svg ├── quote.svg ├── read.svg ├── recipe.svg ├── reply.svg ├── repost.svg ├── review.svg ├── rsvp.svg ├── sleep.svg ├── tag.svg ├── trip.svg ├── video.svg ├── watch.svg ├── weather.svg └── wish.svg ├── templates ├── feed-atom-comments.php ├── feed-atom.php ├── feed-rss2-comments.php ├── feed-rss2.php ├── reply-author.php ├── reply-details.php ├── reply-metabox.php └── reply-time.php └── views ├── kind-article.php ├── kind-audio.php ├── kind-bookmark.php ├── kind-checkin.php ├── kind-drink.php ├── kind-eat.php ├── kind-event.php ├── kind-favorite.php ├── kind-issue.php ├── kind-itinerary.php ├── kind-jam.php ├── kind-like.php ├── kind-listen.php ├── kind-note.php ├── kind-photo.php ├── kind-read.php ├── kind-rsvp.php ├── kind-video.php ├── kind-watch.php └── kind.php /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "jquery": true, 5 | "es6": true, 6 | }, 7 | "extends": "wordpress", 8 | "rules": {} 9 | }; 10 | -------------------------------------------------------------------------------- /css/kind.admin.min.css: -------------------------------------------------------------------------------- 1 | .svg-icon svg{fill:currentColor;padding:2px;vertical-align:middle;top:-0.0625em}#kind-all svg{width:1rem;height:1rem;color:gray}#kind-menu svg,#kind-posts svg,#tempus-onthisday svg,#tempus-thisweek svg,.kind-titles svg{width:1.5rem;height:1.5rem;color:#fff;mix-blend-mode:difference}#kind-menu ul,#kind-posts ul,#tempus-onthisday ul,#tempus-thisweek ul,.kind-titles ul{list-style-type:none}.response svg{width:2rem;height:2rem;color:#fff;mix-blend-mode:difference}.response figure{margin:auto}#replybox-meta.is-loading .loading{visibility:visible}#replybox-meta label{display:block}#replybox-meta .field-row{overflow:auto;height:100%}#replybox-meta .half{float:left;width:46%;margin:0 1%}#replybox-meta .half:first-of-type{margin-left:0;width:48%}#replybox-meta .half:last-of-type{margin-right:0;width:49%}#replybox-meta .quarter{float:left;margin-left:2%;width:12%}#replybox-meta .eighth{float:left;margin-left:2%;margin-right:1%;width:6%}#replybox-meta input[type=number]{width:100%}#replybox-meta .three-quarters{float:left;width:85%}#replybox-meta .is-loading .loading{visibility:visible}#replybox-meta .loading{visibility:hidden;position:absolute;top:0;bottom:0;left:0;right:0;z-index:10;background-color:#000;background-color:rgba(0,0,0,.4)}#replybox-meta .loading-spinner{margin:0 auto;display:block;position:relative;top:2em}/*# sourceMappingURL=kind.admin.min.css.map */ 2 | -------------------------------------------------------------------------------- /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"} -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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"} -------------------------------------------------------------------------------- /css/kind.min.css: -------------------------------------------------------------------------------- 1 | .svg-icon svg{fill:currentColor;padding:2px;vertical-align:middle;top:-0.0625em}#kind-all svg{width:1rem;height:1rem;color:gray}#kind-menu svg,#kind-posts svg,#tempus-onthisday svg,#tempus-thisweek svg,.kind-titles svg{width:1.5rem;height:1.5rem;color:#fff;mix-blend-mode:difference}#kind-menu ul,#kind-posts ul,#tempus-onthisday ul,#tempus-thisweek ul,.kind-titles ul{list-style-type:none}.response svg{width:2rem;height:2rem;color:#fff;mix-blend-mode:difference}.response figure{margin:auto}.response{margin-bottom:10px;margin-top:10px;margin-left:20px;padding:5px;font-size:16px;border-bottom:inset #d6d6d6}.response .kind-icon{margin-right:3px}.response blockquote{margin-left:25px}.response a:link{text-decoration:none}.response a:hover{text-decoration:none}.response img{padding:0;margin:2px}ul.cites{list-style:none;display:inline}.kind-embeds{text-align:center;position:relative;width:100%;margin-top:5px}.kind-photo-thumbnail{margin-bottom:5px}/*# sourceMappingURL=kind.min.css.map */ 2 | -------------------------------------------------------------------------------- /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"} -------------------------------------------------------------------------------- /develop.txt: -------------------------------------------------------------------------------- 1 | == Functions == 2 | 3 | `get_post_kind_slug($id)` - Return the kind slug for a given post. If `$id` is not specified, use current post. 4 | `get_post_kind($id)` - Return the kind string for a given post. If `$id` is not specified, use current post. 5 | 6 | `has_post_kind($kind, $post)` - Returns true/false if kind is in post. If post is empty, then use current post 7 | 8 | `get_kind_context_class( $class, $classtype )` - Returns the CSS class to be applied to the response/context if the kind is one for which there is context. Classtype defaults to u, other option is usually p. Sets the class to the kind slug and for specially specified slugs, sets appropriate mf2 classes as well. $class specifies any additional classes to be added. 9 | 10 | 11 | 12 | == Filters == 13 | 14 | `get_the_kind` - Filter get_the_kinds 15 | 16 | `the_kinds` - Filter get_the_kinds_list 17 | 18 | `kind_classes` - Filter get_kind_class 19 | 20 | `kind_verb` - Filter get_kind_verbs 21 | 22 | `kind-response-display` - Filters the output being added to the_content or to custom location in theme 23 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /includes/class-kind-media-view.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 | -------------------------------------------------------------------------------- /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 |
52 | 82 |
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 | 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 | -------------------------------------------------------------------------------- /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 ''; 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 | 114 |

115 | 116 | 117 | 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /js/clone-media-fragment.js: -------------------------------------------------------------------------------- 1 | function cloneMediaFragment() { 2 | // Check that the fragment is a Media Fragment (starts with t=) 3 | if(window.location.hash && window.location.hash.match(/^#t=/)) { 4 | // Find any video and audio tags on the page 5 | document.querySelectorAll("video,audio").forEach(function(el){ 6 | // Create a virtual element to use as a URI parser 7 | var parser = document.createElement('a'); 8 | parser.href = el.currentSrc; 9 | // Replace the hash 10 | parser.hash = window.location.hash; 11 | // Set the src of the video/audio tag to the full URL 12 | el.src = parser.href; 13 | }); 14 | } 15 | } 16 | 17 | document.addEventListener("DOMContentLoaded", function() { 18 | cloneMediaFragment(); 19 | // When the media is paused, update the fragment of the page 20 | document.querySelectorAll("video,audio").forEach(function(el) { 21 | el.addEventListener("pause", function(event) { 22 | // Update the media fragment to the current time 23 | // Use replaceState to avoid triggering the "hashchange" listener above 24 | history.replaceState(null, null, "#t=" + Math.round(event.target.currentTime)); 25 | }); 26 | }); 27 | }); 28 | 29 | // If the user changes the hash manually, clone the fragment to the media URLs 30 | window.addEventListener("hashchange", cloneMediaFragment); 31 | -------------------------------------------------------------------------------- /lib/parse-this/includes/autoload.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 | -------------------------------------------------------------------------------- /lib/parse-this/includes/class-parse-this-instagram.php: -------------------------------------------------------------------------------- 1 | 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/includes/class-parse-this-json.php: -------------------------------------------------------------------------------- 1 | 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 | -------------------------------------------------------------------------------- /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/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/includes/class-parse-this-youtube.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 | -------------------------------------------------------------------------------- /lib/parse-this/includes/class-rest-parse-this.php: -------------------------------------------------------------------------------- 1 | 34 |

35 |

36 |

37 | 38 |

39 | 40 |

41 | 46 |


47 |
48 |

49 | 50 |

51 | 52 | 53 | 54 | 57 | 60 | 61 | 62 | 65 | 68 | 69 | 70 | 73 | 76 | 77 | 78 | 81 | 84 | 85 | 86 | 89 | 95 | 96 | 97 | 100 | 103 | 104 | 105 | 106 | 107 | 108 |
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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/parse-this/lib/html5/HTML5/Exception.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 | -------------------------------------------------------------------------------- /lib/parse-this/lib/html5/HTML5/Parser/FileInputStream.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/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/lib/html5/HTML5/Serializer/README.md: -------------------------------------------------------------------------------- 1 | # The Serializer (Writer) Model 2 | 3 | The serializer roughly follows sections _8.1 Writing HTML documents_ and section 4 | _8.3 Serializing HTML fragments_ by converting DOMDocument, DOMDocumentFragment, 5 | and DOMNodeList into HTML5. 6 | 7 | [ HTML5 ] // Interface for saving. 8 | || 9 | [ Traverser ] // Walk the DOM 10 | || 11 | [ Rules ] // Convert DOM elements into strings. 12 | || 13 | [ HTML5 ] // HTML5 document or fragment in text. 14 | 15 | 16 | ## HTML5 Class 17 | 18 | Provides the top level interface for saving. 19 | 20 | ## The Traverser 21 | 22 | Walks the DOM finding each element and passing it off to the output rules to 23 | convert to HTML5. 24 | 25 | ## Output Rules 26 | 27 | The output rules are defined in the RulesInterface which can have multiple 28 | implementations. Currently, the OutputRules is the default implementation that 29 | converts a DOM as is into HTML5. 30 | 31 | ## HTML5 String 32 | 33 | The output of the process it HTML5 as a string or saved to a file. -------------------------------------------------------------------------------- /lib/parse-this/lib/html5/HTML5/Serializer/RulesInterface.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/parse-this/lib/html5/autoloader.php: -------------------------------------------------------------------------------- 1 | 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 | -------------------------------------------------------------------------------- /lib/parse-this/parse-this.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/article.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/audio.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/bookmark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/checkin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/craft.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/drink.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/eat.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/event.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/exercise.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/favorite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/firehose.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/follow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/issue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/itinerary.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/jam.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/like.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/listen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/mood.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/note.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/photo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/play.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/question.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/quote.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/read.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/recipe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/reply.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/repost.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/review.svg: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /svgs/rsvp.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/sleep.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/tag.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/trip.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/video.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/watch.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/weather.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /svgs/wish.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | <?php 32 | if ( is_singular() ) { 33 | /* translators: Comments feed title. %s: Post title */ 34 | printf( ent2ncr( __( 'Comments on %s' ) ), get_the_title_rss() ); 35 | } elseif ( is_search() ) { 36 | /* translators: Comments feed title. 1: Site name, 2: Search query */ 37 | printf( ent2ncr( __( 'Comments for %1$s searching on %2$s' ) ), get_bloginfo_rss( 'name' ), get_search_query() ); 38 | } else { 39 | /* translators: Comments feed title. %s: Site name */ 40 | printf( ent2ncr( __( 'Comments for %s' ) ), get_wp_title_rss() ); 41 | } 42 | ?> 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 | <?php 78 | if ( ! is_singular() ) { 79 | $title = get_the_title( $comment_post->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 | -------------------------------------------------------------------------------- /templates/feed-atom.php: -------------------------------------------------------------------------------- 1 | '; 12 | 13 | /** This action is documented in wp-includes/feed-rss2.php */ 14 | do_action( 'rss_tag_pre', 'atom' ); 15 | ?> 16 | 29 | > 30 | <?php wp_title_rss(); ?> 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 67 | 68 | <![CDATA[<?php echo $title; ?>]]> 69 | 70 | 71 | 72 | 73 | 74 | 75 | ]]> 76 | 77 | ]]> 78 | 79 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /templates/feed-rss2-comments.php: -------------------------------------------------------------------------------- 1 | '; 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 | <?php 37 | if ( is_singular() ) { 38 | /* translators: Comments feed title. %s: Post title */ 39 | printf( ent2ncr( __( 'Comments on: %s' ) ), get_the_title_rss() ); 40 | } elseif ( is_search() ) { 41 | /* translators: Comments feed title. 1: Site name, 2: Search query */ 42 | printf( ent2ncr( __( 'Comments for %1$s searching on %2$s' ) ), get_bloginfo_rss( 'name' ), get_search_query() ); 43 | } else { 44 | /* translators: Comments feed title. %s: Site name */ 45 | printf( ent2ncr( __( 'Comments for %s' ) ), get_wp_title_rss() ); 46 | } 47 | ?> 48 | 49 | 50 | 51 | 52 | 53 | 54 | 58 | 59 | 60 | 64 | 65 | comment_post_ID ); 77 | ?> 78 | 79 | 80 | <?php 81 | if ( ! is_singular() ) { 82 | $title = get_the_title( $comment_post->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 | -------------------------------------------------------------------------------- /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 | <?php wp_title_rss(); ?> 42 | 43 | 44 | 45 | 46 | 47 | 48 | 61 | 62 | 63 | 76 | 77 | 88 | 89 | 90 | 91 | <?php echo $title; ?> 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 | -------------------------------------------------------------------------------- /templates/reply-author.php: -------------------------------------------------------------------------------- 1 |
2 |

3 |
4 |

5 | 9 |

10 |

11 | 15 |

16 |

17 | 21 |

22 |
23 | -------------------------------------------------------------------------------- /templates/reply-details.php: -------------------------------------------------------------------------------- 1 |
2 |

3 | 7 |

8 | 12 |

13 |

14 | 15 | 16 |

17 | 21 |

22 | 26 |

27 | 28 |
29 | -------------------------------------------------------------------------------- /templates/reply-metabox.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 | 33 | 34 |

35 | 39 |

40 |

41 | 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 | get_duration() ); ?> 2 | 3 |
4 |

5 |

6 | 10 | 14 | 18 |

19 |

20 | 24 | 28 | 32 |

33 |

34 |

35 | get_datetime_property( 'start' ), 'start' ); ?> 36 | get_datetime_property( 'end' ), 'end' ); ?> 37 |

38 |
39 | -------------------------------------------------------------------------------- /views/kind-article.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 | -------------------------------------------------------------------------------- /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 | 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-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-eat.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 | -------------------------------------------------------------------------------- /views/kind-event.php: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 |
11 | 15 |
16 | get( 'start' ); 18 | $end = $kind_post->get( 'end' ); 19 | $timestring = '

%1$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 | -------------------------------------------------------------------------------- /views/kind-favorite.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 | 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 | get( 'itinerary', false ); 9 | 10 | foreach( $itineraries as $key => $value ) { 11 | $itineraries [ $key ] = mf2_to_jf2( $value ); 12 | } 13 | 14 | ?> 15 | 16 | 17 |
18 |
19 | 23 |
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 | 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 | -------------------------------------------------------------------------------- /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 | 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 | -------------------------------------------------------------------------------- /views/kind-note.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 | -------------------------------------------------------------------------------- /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-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 | 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 | 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 | 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 |