WP RSS Aggregator - Version 4.7

Version Description

(2015-04-21) = * New: Optionally import only items with titles that don't already exist. * Enhanced: Accessing feeds over HTTPS is now possible. * Enhanced: Added support for multibyte strings in some places. * Enhanced: Increased JS compatibility with other plugins. * Enhanced: Increased UI support for mobile devices. * Fixed bug: Having no mysqli extension no longer causes an error to appear in the debug info. * Fixed bug: Saving an empty license key no longer results in a warning.

Download this release

Release Info

Developer jeangalea
Plugin Icon 128x128 WP RSS Aggregator
Version 4.7
Comparing to
See all releases

Code changes from version 4.6.13 to 4.7

css/admin-3.8.css CHANGED
@@ -1,47 +1,47 @@
1
- #menu-posts-wprss_feed .menu-icon-post div.wp-menu-image:before {
2
- content: "\f303" !important;
3
- }
4
- .welcome-page-tile {
5
- display: inline-block;
6
- padding: 15px 0;
7
- margin: 15px 0;
8
- width: 30%;
9
- text-align: left;
10
- vertical-align: top;
11
- box-sizing: border-box;
12
- -moz-box-sizing: border-box;
13
- }
14
- .welcome-page-tile:first-child {
15
- margin-left: 0;
16
- }
17
- .welcome-page-tile:last-child {
18
- margin-right: 0;
19
- }
20
- .welcome-page-tile > h4 {
21
- margin: 1.4em 0 .6em;
22
- font-size: 1.2em;
23
- }
24
- .welcome-page-tile > p {
25
- margin: 0;
26
- }
27
- .wprss-about-text {
28
- color: #777;
29
- line-height: 1.6em;
30
- margin: 15px 0;
31
- font-size: 1.2em;
32
- }
33
-
34
- #check-out-addons {
35
- margin-top: 15px;
36
- }
37
-
38
- /* WP 3.8+ Settings Page */
39
-
40
- body.wprss_feed_page_wprss-aggregator-settings .wrap h3 {
41
- font-size: 1.5em;
42
- margin-top: 1.8em;
43
- margin-bottom: 0.6em;
44
- }
45
- body.wprss_feed_page_wprss-aggregator-settings .wrap h3 + p {
46
- margin-top: 0.4em;
47
  }
1
+ #menu-posts-wprss_feed .menu-icon-post div.wp-menu-image:before {
2
+ content: "\f303" !important;
3
+ }
4
+ .welcome-page-tile {
5
+ display: inline-block;
6
+ padding: 15px 0;
7
+ margin: 15px 0;
8
+ width: 30%;
9
+ text-align: left;
10
+ vertical-align: top;
11
+ box-sizing: border-box;
12
+ -moz-box-sizing: border-box;
13
+ }
14
+ .welcome-page-tile:first-child {
15
+ margin-left: 0;
16
+ }
17
+ .welcome-page-tile:last-child {
18
+ margin-right: 0;
19
+ }
20
+ .welcome-page-tile > h4 {
21
+ margin: 1.4em 0 .6em;
22
+ font-size: 1.2em;
23
+ }
24
+ .welcome-page-tile > p {
25
+ margin: 0;
26
+ }
27
+ .wprss-about-text {
28
+ color: #777;
29
+ line-height: 1.6em;
30
+ margin: 15px 0;
31
+ font-size: 1.2em;
32
+ }
33
+
34
+ #check-out-addons {
35
+ margin-top: 15px;
36
+ }
37
+
38
+ /* WP 3.8+ Settings Page */
39
+
40
+ body.wprss_feed_page_wprss-aggregator-settings .wrap h3 {
41
+ font-size: 1.5em;
42
+ margin-top: 1.8em;
43
+ margin-bottom: 0.6em;
44
+ }
45
+ body.wprss_feed_page_wprss-aggregator-settings .wrap h3 + p {
46
+ margin-top: 0.4em;
47
  }
css/admin-editor.css CHANGED
@@ -1,106 +1,106 @@
1
- /* EDITOR BUTTON AND DIALOG STYLES */
2
-
3
- #wprss-overlay {
4
- position: fixed;
5
- top: 0;
6
- left: 0;
7
- right: 0;
8
- bottom: 0;
9
- background: rgba(0,0,0,0.6);
10
- z-index: 1000;
11
- }
12
-
13
- #wprss-editor-dialog {
14
- position: absolute;
15
- top: 0;
16
- bottom: 0;
17
- left: 0;
18
- right: 0;
19
- width: 500px;
20
- height: 450px;
21
- margin: auto;
22
- }
23
-
24
- .wprss-dialog-header {
25
- background: #464646;
26
- position: absolute;
27
- top: 0;
28
- left: 0;
29
- right: 0;
30
- height: 30px;
31
- }
32
-
33
- .wprss-dialog-header h1 {
34
- color: #ddd;
35
- text-shadow: rgb(68, 68, 68) 0px -1px 0px;
36
- font-size: 16px;
37
- font-weight: normal;
38
- margin: 7px 0 15px 6px;
39
- }
40
-
41
- .wprss-dialog-header .close-btn {
42
- position: absolute;
43
- top: 4px;
44
- right: 10px;
45
- color: #ccc;
46
- font-size: 18px;
47
- font-weight: bold;
48
- padding: 0px 6px 2px;
49
- border-radius: 20px;
50
- cursor: pointer;
51
- }
52
- .wprss-dialog-header .close-btn:hover {
53
- background: white;
54
- color: black;
55
- }
56
-
57
-
58
- .wprss-dialog-inside {
59
- position: absolute;
60
- top: 30px;
61
- bottom: 0;
62
- left: 0;
63
- right: 0;
64
- overflow: auto;
65
- }
66
-
67
- .wprss-dialog-inside table {
68
- position: absolute;
69
- display: inline-block;
70
- top: 0;
71
- left: 0;
72
- right: 0;
73
- height: auto;
74
- padding: 10px;
75
- box-sizing: border-box;
76
- -moz-box-sizing: border-box;
77
- }
78
-
79
- .wprss-dialog-inside table tr td:first-child {
80
- font-weight: bold;
81
- }
82
-
83
-
84
- #wprss-dialog-sources-container {
85
- margin-top: 10px;
86
- }
87
-
88
- #wprss-dialog-feed-source-list,
89
- #wprss-dialog-exclude-list {
90
- min-width: 100px;
91
- min-height: 100px;
92
- }
93
-
94
- #wprss-dialog-exclude-row td p:first-child {
95
- margin-top: 0;
96
- }
97
-
98
- #wprss-dialog-exclude-label {
99
- vertical-align: top;
100
- }
101
-
102
- #validate-feed-link {
103
- font-size: 0.85em;
104
- text-decoration: none;
105
- margin-left: 3px;
106
  }
1
+ /* EDITOR BUTTON AND DIALOG STYLES */
2
+
3
+ #wprss-overlay {
4
+ position: fixed;
5
+ top: 0;
6
+ left: 0;
7
+ right: 0;
8
+ bottom: 0;
9
+ background: rgba(0,0,0,0.6);
10
+ z-index: 1000;
11
+ }
12
+
13
+ #wprss-editor-dialog {
14
+ position: absolute;
15
+ top: 0;
16
+ bottom: 0;
17
+ left: 0;
18
+ right: 0;
19
+ width: 500px;
20
+ height: 450px;
21
+ margin: auto;
22
+ }
23
+
24
+ .wprss-dialog-header {
25
+ background: #464646;
26
+ position: absolute;
27
+ top: 0;
28
+ left: 0;
29
+ right: 0;
30
+ height: 30px;
31
+ }
32
+
33
+ .wprss-dialog-header h1 {
34
+ color: #ddd;
35
+ text-shadow: rgb(68, 68, 68) 0px -1px 0px;
36
+ font-size: 16px;
37
+ font-weight: normal;
38
+ margin: 7px 0 15px 6px;
39
+ }
40
+
41
+ .wprss-dialog-header .close-btn {
42
+ position: absolute;
43
+ top: 4px;
44
+ right: 10px;
45
+ color: #ccc;
46
+ font-size: 18px;
47
+ font-weight: bold;
48
+ padding: 0px 6px 2px;
49
+ border-radius: 20px;
50
+ cursor: pointer;
51
+ }
52
+ .wprss-dialog-header .close-btn:hover {
53
+ background: white;
54
+ color: black;
55
+ }
56
+
57
+
58
+ .wprss-dialog-inside {
59
+ position: absolute;
60
+ top: 30px;
61
+ bottom: 0;
62
+ left: 0;
63
+ right: 0;
64
+ overflow: auto;
65
+ }
66
+
67
+ .wprss-dialog-inside table {
68
+ position: absolute;
69
+ display: inline-block;
70
+ top: 0;
71
+ left: 0;
72
+ right: 0;
73
+ height: auto;
74
+ padding: 10px;
75
+ box-sizing: border-box;
76
+ -moz-box-sizing: border-box;
77
+ }
78
+
79
+ .wprss-dialog-inside table tr td:first-child {
80
+ font-weight: bold;
81
+ }
82
+
83
+
84
+ #wprss-dialog-sources-container {
85
+ margin-top: 10px;
86
+ }
87
+
88
+ #wprss-dialog-feed-source-list,
89
+ #wprss-dialog-exclude-list {
90
+ min-width: 100px;
91
+ min-height: 100px;
92
+ }
93
+
94
+ #wprss-dialog-exclude-row td p:first-child {
95
+ margin-top: 0;
96
+ }
97
+
98
+ #wprss-dialog-exclude-label {
99
+ vertical-align: top;
100
+ }
101
+
102
+ #validate-feed-link {
103
+ font-size: 0.85em;
104
+ text-decoration: none;
105
+ margin-left: 3px;
106
  }
css/admin-tracking-styles.css CHANGED
@@ -1,42 +1,42 @@
1
- #wprss_tracking_notice {
2
- position: fixed;
3
- display: inline-block;
4
-
5
- top: 45px;
6
- left: 50%;
7
-
8
- width: 300px;
9
- height: auto;
10
- padding: 40px 10px 10px;
11
- margin-left: -150px;
12
-
13
- background: white;
14
- border: 1px solid #ccc;
15
- border-radius: 3px;
16
- box-shadow: 0px 0px 6px rgba(0,0,0,0.2);
17
- z-index: 20000;
18
-
19
- box-sizing: border-box;
20
- -moz-box-sizing: border-box;
21
- -o-box-sizing: border-box;
22
- }
23
-
24
- #wprss_tracking_notice > p {
25
- margin: 0;
26
- }
27
- #wprss_tracking_notice > p:first-child {
28
- position: absolute;
29
- top: 0;
30
- left: 0;
31
- right: 0;
32
- padding: 6px 10px;
33
- font-weight: bold;
34
- background: #008BC2;
35
- color: white;
36
- }
37
-
38
- #wprss_tracking_notice > p:nth-child(2) {
39
- padding-bottom: 5px;
40
- margin-bottom: 12px;
41
- border-bottom: 1px solid #ccc;
42
  }
1
+ #wprss_tracking_notice {
2
+ position: fixed;
3
+ display: inline-block;
4
+
5
+ top: 45px;
6
+ left: 50%;
7
+
8
+ width: 300px;
9
+ height: auto;
10
+ padding: 40px 10px 10px;
11
+ margin-left: -150px;
12
+
13
+ background: white;
14
+ border: 1px solid #ccc;
15
+ border-radius: 3px;
16
+ box-shadow: 0px 0px 6px rgba(0,0,0,0.2);
17
+ z-index: 20000;
18
+
19
+ box-sizing: border-box;
20
+ -moz-box-sizing: border-box;
21
+ -o-box-sizing: border-box;
22
+ }
23
+
24
+ #wprss_tracking_notice > p {
25
+ margin: 0;
26
+ }
27
+ #wprss_tracking_notice > p:first-child {
28
+ position: absolute;
29
+ top: 0;
30
+ left: 0;
31
+ right: 0;
32
+ padding: 6px 10px;
33
+ font-weight: bold;
34
+ background: #008BC2;
35
+ color: white;
36
+ }
37
+
38
+ #wprss_tracking_notice > p:nth-child(2) {
39
+ padding-bottom: 5px;
40
+ margin-bottom: 12px;
41
+ border-bottom: 1px solid #ccc;
42
  }
css/styles.css CHANGED
@@ -1,27 +1,27 @@
1
- li.feed-item { margin-bottom: 10px; }
2
-
3
- .thumbnail-excerpt {
4
- overflow:hidden;
5
- margin-bottom: 5px;
6
- }
7
-
8
- .thumbnail-excerpt img {
9
- max-width:100%; float:left; margin-top: 0.5em; margin-right:10px;
10
- }
11
-
12
- .green {
13
- color: #0BD600;
14
- }
15
-
16
- .nav-links {
17
- overflow: hidden;
18
- margin-bottom: 20px;
19
- }
20
-
21
- div.wprss-feed-meta > span {
22
- font-size: 90%;
23
- clear: both;
24
- }
25
- div.wprss-feed-meta > span:not(:last-child):after {
26
- content: ' | ';
27
  }
1
+ li.feed-item { margin-bottom: 10px; }
2
+
3
+ .thumbnail-excerpt {
4
+ overflow:hidden;
5
+ margin-bottom: 5px;
6
+ }
7
+
8
+ .thumbnail-excerpt img {
9
+ max-width:100%; float:left; margin-top: 0.5em; margin-right:10px;
10
+ }
11
+
12
+ .green {
13
+ color: #0BD600;
14
+ }
15
+
16
+ .nav-links {
17
+ overflow: hidden;
18
+ margin-bottom: 20px;
19
+ }
20
+
21
+ div.wprss-feed-meta > span {
22
+ font-size: 90%;
23
+ clear: both;
24
+ }
25
+ div.wprss-feed-meta > span:not(:last-child):after {
26
+ content: ' | ';
27
  }
includes/OPML.php CHANGED
@@ -1,132 +1,132 @@
1
- <?php
2
-
3
- /**
4
- * The WPRSS_OPML Class represents an OPML document.
5
- * It is constructed using a file, which it parses upon creation.
6
- *
7
- * @since 3.3
8
- * @package WPRSSAggregator
9
- */
10
- class WPRSS_OPML {
11
-
12
-
13
- public $file;
14
- public $head;
15
- public $body;
16
-
17
- /**
18
- * Constructor: Parses the given XML file as an OPML
19
- *
20
- * @since 3.3
21
- * @param $file The file to be parsed into a WPRSS_OPML object
22
- */
23
- public function __construct( $file ) {
24
- // Initialize Properties
25
- $this->file = $file;
26
- $this->head = array();
27
- $this->body = array();
28
- // Begin Parsing
29
- try {
30
- // Wrap file data in a SimpleXMLElement
31
- $xml = simplexml_load_file( $this->file );
32
-
33
- // Check if the head section exists before parsing it
34
- if ( $xml->head )
35
- $this->head = WPRSS_OPML::parse_opml_head( $xml->head->children() );
36
- // Check if the body section exists before parsing it
37
- if ( $xml->body )
38
- $this->body = WPRSS_OPML::parse_opml_body_element( $xml->body->outline );
39
-
40
- } catch (Exception $e) {
41
- // If an exception is caught. Throw an error message
42
- throw new Exception( __( 'An error occured: The file might not be a valid OPML file or is corrrupt. ', WPRSS_TEXT_DOMAIN ), 1);
43
- }
44
- }
45
-
46
-
47
- /**
48
- * Parses the given variable as an OPML head section
49
- *
50
- * @since 3.3
51
- * @param $head The head section of an OPML file ( SimpleXMLElement )
52
- * @return array An associative array containing SimpleXMLElements for each tag in the given head section.
53
- */
54
- private static function parse_opml_head( $head ) {
55
- $parsed_data = array();
56
-
57
- foreach ( $head as $property ) {
58
- $parsed_data[ $property->getName() ] = $property;
59
- }
60
-
61
- return $parsed_data;
62
- }
63
-
64
-
65
- /**
66
- * Iterates through the elements given OPML/XML data and parses
67
- * each element, and recurses where necessary, into an associative
68
- * array that mimics OPML structure.
69
- * @since 1.0
70
- * @return array The Parsed OPML as an associative array
71
- */
72
- private static function parse_opml_body_element( $element ) {
73
- $parsed_data = array();
74
-
75
- // Iterates through each child element in the given element
76
- foreach ( $element as $child_element ) {
77
-
78
- // If child element has type attribute set to 'rss' or has the 'xmlUrl' attribute
79
- if ( $child_element['type'] === 'rss' || isset( $child_element['xmlUrl'] ) ) {
80
- // Parse the child element and add it to the parsed data array
81
- $parsed_data[] = WPRSS_OPML::SimpleXMLElement_to_OPMLElement( $child_element );
82
- }
83
- // Otherwise, if the child element has children 'outline' elements
84
- elseif ( $child_element->outline ) {
85
- // Recurse using this child element as parameter, and add the result to the parsed data array
86
- $parsed_data[ (string) $child_element['text'] ] = WPRSS_OPML::parse_opml_body_element( $child_element->outline );
87
- }
88
-
89
- }
90
-
91
- return $parsed_data;
92
- }
93
-
94
-
95
- /**
96
- * Parses a SimpleXMLElement Object into an OPML element compliant
97
- * associative array.
98
- * @since 1.0
99
- * @return array An array in OPML element format
100
- */
101
- private static function SimpleXMLElement_to_OPMLElement( $element ) {
102
- // Copy attribute data from $element to a new associative array
103
- $result = array(
104
- 'text' => (string) $element['text'],
105
- 'title' => (string) $element['title'],
106
- 'htmlUrl' => (string) $element['htmlUrl'],
107
- 'xmlUrl' => (string) $element['xmlUrl'],
108
- 'description' => (string) $element['description']
109
- );
110
-
111
- // Check for category attribute
112
- if ( isset( $element['category'] ) ) {
113
- // split categories by comma, and trim each category string
114
- $result['categories'] = array_map( 'trim', explode(',', $element['category']) );
115
- }
116
-
117
- /*
118
- * Check for existence of htmlUrl and xmlUrl.
119
- * If not found, use lowercased attribute names.
120
- */
121
- if ( !isset( $element['htmlUrl'] ) )
122
- $result['htmlUrl'] = $element['htmlurl'];
123
- if ( !isset( $element['xmlUrl'] ) )
124
- $result['xmlUrl'] = $element['xmlurl'];
125
-
126
- // Return the result OPML Element
127
- return $result;
128
- }
129
-
130
- }
131
-
132
- ?>
1
+ <?php
2
+
3
+ /**
4
+ * The WPRSS_OPML Class represents an OPML document.
5
+ * It is constructed using a file, which it parses upon creation.
6
+ *
7
+ * @since 3.3
8
+ * @package WPRSSAggregator
9
+ */
10
+ class WPRSS_OPML {
11
+
12
+
13
+ public $file;
14
+ public $head;
15
+ public $body;
16
+
17
+ /**
18
+ * Constructor: Parses the given XML file as an OPML
19
+ *
20
+ * @since 3.3
21
+ * @param $file The file to be parsed into a WPRSS_OPML object
22
+ */
23
+ public function __construct( $file ) {
24
+ // Initialize Properties
25
+ $this->file = $file;
26
+ $this->head = array();
27
+ $this->body = array();
28
+ // Begin Parsing
29
+ try {
30
+ // Wrap file data in a SimpleXMLElement
31
+ $xml = simplexml_load_file( $this->file );
32
+
33
+ // Check if the head section exists before parsing it
34
+ if ( $xml->head )
35
+ $this->head = WPRSS_OPML::parse_opml_head( $xml->head->children() );
36
+ // Check if the body section exists before parsing it
37
+ if ( $xml->body )
38
+ $this->body = WPRSS_OPML::parse_opml_body_element( $xml->body->outline );
39
+
40
+ } catch (Exception $e) {
41
+ // If an exception is caught. Throw an error message
42
+ throw new Exception( __( 'An error occurred: The file might not be a valid OPML file or is corrupt. ', WPRSS_TEXT_DOMAIN ), 1);
43
+ }
44
+ }
45
+
46
+
47
+ /**
48
+ * Parses the given variable as an OPML head section
49
+ *
50
+ * @since 3.3
51
+ * @param $head The head section of an OPML file ( SimpleXMLElement )
52
+ * @return array An associative array containing SimpleXMLElements for each tag in the given head section.
53
+ */
54
+ private static function parse_opml_head( $head ) {
55
+ $parsed_data = array();
56
+
57
+ foreach ( $head as $property ) {
58
+ $parsed_data[ $property->getName() ] = $property;
59
+ }
60
+
61
+ return $parsed_data;
62
+ }
63
+
64
+
65
+ /**
66
+ * Iterates through the elements given OPML/XML data and parses
67
+ * each element, and recurses where necessary, into an associative
68
+ * array that mimics OPML structure.
69
+ * @since 1.0
70
+ * @return array The Parsed OPML as an associative array
71
+ */
72
+ private static function parse_opml_body_element( $element ) {
73
+ $parsed_data = array();
74
+
75
+ // Iterates through each child element in the given element
76
+ foreach ( $element as $child_element ) {
77
+
78
+ // If child element has type attribute set to 'rss' or has the 'xmlUrl' attribute
79
+ if ( $child_element['type'] === 'rss' || isset( $child_element['xmlUrl'] ) ) {
80
+ // Parse the child element and add it to the parsed data array
81
+ $parsed_data[] = WPRSS_OPML::SimpleXMLElement_to_OPMLElement( $child_element );
82
+ }
83
+ // Otherwise, if the child element has children 'outline' elements
84
+ elseif ( $child_element->outline ) {
85
+ // Recurse using this child element as parameter, and add the result to the parsed data array
86
+ $parsed_data[ (string) $child_element['text'] ] = WPRSS_OPML::parse_opml_body_element( $child_element->outline );
87
+ }
88
+
89
+ }
90
+
91
+ return $parsed_data;
92
+ }
93
+
94
+
95
+ /**
96
+ * Parses a SimpleXMLElement Object into an OPML element compliant
97
+ * associative array.
98
+ * @since 1.0
99
+ * @return array An array in OPML element format
100
+ */
101
+ private static function SimpleXMLElement_to_OPMLElement( $element ) {
102
+ // Copy attribute data from $element to a new associative array
103
+ $result = array(
104
+ 'text' => (string) $element['text'],
105
+ 'title' => (string) $element['title'],
106
+ 'htmlUrl' => (string) $element['htmlUrl'],
107
+ 'xmlUrl' => (string) $element['xmlUrl'],
108
+ 'description' => (string) $element['description']
109
+ );
110
+
111
+ // Check for category attribute
112
+ if ( isset( $element['category'] ) ) {
113
+ // split categories by comma, and trim each category string
114
+ $result['categories'] = array_map( 'trim', explode(',', $element['category']) );
115
+ }
116
+
117
+ /*
118
+ * Check for existence of htmlUrl and xmlUrl.
119
+ * If not found, use lowercased attribute names.
120
+ */
121
+ if ( !isset( $element['htmlUrl'] ) )
122
+ $result['htmlUrl'] = $element['htmlurl'];
123
+ if ( !isset( $element['xmlUrl'] ) )
124
+ $result['xmlUrl'] = $element['xmlurl'];
125
+
126
+ // Return the result OPML Element
127
+ return $result;
128
+ }
129
+
130
+ }
131
+
132
+ ?>
includes/admin-addons.php CHANGED
@@ -1,92 +1,92 @@
1
- <?php
2
- /**
3
- * Build the Add-ons page (Code borrowed from the ACF plugin)
4
- *
5
- * @since 4.2
6
- * @link http://www.advancedcustomfields.com/
7
- *
8
- */
9
- function wprss_addons_page_display() {
10
-
11
- $premium = array();
12
- $premium[] = array(
13
- 'title' => __( "Excerpts & Thumbnails", WPRSS_TEXT_DOMAIN ),
14
- 'description' => __( "Adds the ability to display thumbnails and excerpts. Perfect for adding some life and color to your feed item display. For more flexibility Feed to Post is a better option.", WPRSS_TEXT_DOMAIN ),
15
- 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
16
- 'active' => is_plugin_active( 'wp-rss-excerpts-thumbnails/wp-rss-excerpts-thumbnails.php' ),
17
- 'url' => 'http://www.wprssaggregator.com/extension/excerpts-thumbnails/'
18
- );
19
- $premium[] = array(
20
- 'title' => __( "Categories", WPRSS_TEXT_DOMAIN ),
21
- 'description' => __( "Assign categories to your feed sources. Then display a particular category or multiple categories on a post or page via shortcodes.", WPRSS_TEXT_DOMAIN ),
22
- 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
23
- 'active' => is_plugin_active( 'wp-rss-categories/wp-rss-categories.php' ),
24
- 'url' => 'http://www.wprssaggregator.com/extension/categories/'
25
- );
26
- $premium[] = array(
27
- 'title' => __( "Keyword Filtering", WPRSS_TEXT_DOMAIN ),
28
- 'description' => __( "Import feeds that contain specific keywords in either the title or their content. Control what gets imported to your blog. You can use keywords, keyphrases and categories.", WPRSS_TEXT_DOMAIN ),
29
- 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
30
- 'active' => is_plugin_active( 'wp-rss-keyword-filtering/wp-rss-keyword-filtering.php' ),
31
- 'url' => 'http://www.wprssaggregator.com/extension/keyword-filtering/'
32
- );
33
- $premium[] = array(
34
- 'title' => __( "Feed to Post", WPRSS_TEXT_DOMAIN ),
35
- 'description' => __( "Allows you to import feed items into posts or any other custom post type that you have created. Takes WP RSS Aggregator to a whole new level of flexibility.", WPRSS_TEXT_DOMAIN ),
36
- 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
37
- 'active' => is_plugin_active( 'wp-rss-feed-to-post/wp-rss-feed-to-post.php' ),
38
- 'url' => 'http://www.wprssaggregator.com/extension/feed-to-post/'
39
- );
40
- $premium[] = array(
41
- 'title' => __( "Full Text RSS Feeds", WPRSS_TEXT_DOMAIN ),
42
- 'description' => __( "This add-ons provides the connectivity to our Full Text Premium service, which gives you unlimited feed items returned per feed source.", WPRSS_TEXT_DOMAIN ),
43
- 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
44
- 'active' => is_plugin_active( 'wp-rss-full-text-feeds/wp-rss-full-text.php' ),
45
- 'url' => 'http://www.wprssaggregator.com/extension/full-text-rss-feeds/'
46
- );
47
- $premium[] = array(
48
- 'title' => __( "WordAi", WPRSS_TEXT_DOMAIN ),
49
- 'description' => __( "Allows you to spin the content for posts imported by Feed to Post using WordAi. Cleverly rewrite your posts without changing their meaning and maintaining human readability.", WPRSS_TEXT_DOMAIN ),
50
- 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
51
- 'active' => is_plugin_active( 'wp-rss-wordai/wp-rss-wordai.php' ),
52
- 'url' => 'http://www.wprssaggregator.com/extension/wordai/'
53
- );
54
-
55
- ?>
56
- <div class="wrap">
57
- <?php screen_icon( 'wprss-aggregator' ); ?>
58
-
59
- <h2><?php _e( 'Add-Ons', WPRSS_TEXT_DOMAIN ); ?></h2>
60
- <p><?php _e( "The following Add-ons are available to increase the functionality of the WP RSS Aggregator plugin.", WPRSS_TEXT_DOMAIN ); ?><br />
61
- <?php _e( "Each Add-on can be installed as a separate plugin. Note that activating the Feed to Post plugin will deactivate the Categories and Excerpts & Thumbnails add-ons.", WPRSS_TEXT_DOMAIN ); ?></p>
62
-
63
- <div id="add-ons" class="clearfix">
64
-
65
- <div class="add-on-group clearfix">
66
- <?php foreach( $premium as $addon ): ?>
67
- <div class="add-on wp-box <?php if( $addon['active'] ): ?>add-on-active<?php endif; ?>">
68
- <!-- <a target="_blank" href="<?php echo $addon['url']; ?>">
69
- <img src="<?php echo $addon['thumbnail']; ?>" />
70
- </a> -->
71
- <div class="inner">
72
- <h3><a target="_blank" href="<?php echo $addon['url']; ?>"><?php echo $addon['title']; ?></a></h3>
73
- <p><?php echo $addon['description']; ?></p>
74
- </div>
75
- <div class="footer">
76
- <?php if( $addon['active'] ): ?>
77
- <a class="button button-disabled"><span class="wprss-sprite-tick"></span><?php _e( "Installed", WPRSS_TEXT_DOMAIN ); ?></a>
78
- <?php else: ?>
79
- <a target="_blank" href="<?php echo $addon['url']; ?>" class="button"><?php _e( "Purchase & Install", WPRSS_TEXT_DOMAIN ); ?></a>
80
- <?php endif; ?>
81
- </div>
82
- </div>
83
- <?php endforeach; ?>
84
- </div>
85
-
86
- </div>
87
-
88
- </div>
89
-
90
- <?php
91
-
92
  }
1
+ <?php
2
+ /**
3
+ * Build the Add-ons page (Code borrowed from the ACF plugin)
4
+ *
5
+ * @since 4.2
6
+ * @link http://www.advancedcustomfields.com/
7
+ *
8
+ */
9
+ function wprss_addons_page_display() {
10
+
11
+ $premium = array();
12
+ $premium[] = array(
13
+ 'title' => __( "Excerpts & Thumbnails", WPRSS_TEXT_DOMAIN ),
14
+ 'description' => __( "Adds the ability to display thumbnails and excerpts. Perfect for adding some life and color to your feed item display. For more flexibility Feed to Post is a better option.", WPRSS_TEXT_DOMAIN ),
15
+ 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
16
+ 'active' => is_plugin_active( 'wp-rss-excerpts-thumbnails/wp-rss-excerpts-thumbnails.php' ),
17
+ 'url' => 'http://www.wprssaggregator.com/extension/excerpts-thumbnails/'
18
+ );
19
+ $premium[] = array(
20
+ 'title' => __( "Categories", WPRSS_TEXT_DOMAIN ),
21
+ 'description' => __( "Assign categories to your feed sources. Then display a particular category or multiple categories on a post or page via shortcodes.", WPRSS_TEXT_DOMAIN ),
22
+ 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
23
+ 'active' => is_plugin_active( 'wp-rss-categories/wp-rss-categories.php' ),
24
+ 'url' => 'http://www.wprssaggregator.com/extension/categories/'
25
+ );
26
+ $premium[] = array(
27
+ 'title' => __( "Keyword Filtering", WPRSS_TEXT_DOMAIN ),
28
+ 'description' => __( "Import feeds that contain specific keywords in either the title or their content. Control what gets imported to your blog. You can use keywords, keyphrases and categories.", WPRSS_TEXT_DOMAIN ),
29
+ 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
30
+ 'active' => is_plugin_active( 'wp-rss-keyword-filtering/wp-rss-keyword-filtering.php' ),
31
+ 'url' => 'http://www.wprssaggregator.com/extension/keyword-filtering/'
32
+ );
33
+ $premium[] = array(
34
+ 'title' => __( "Feed to Post", WPRSS_TEXT_DOMAIN ),
35
+ 'description' => __( "Allows you to import feed items into posts or any other custom post type that you have created. Takes WP RSS Aggregator to a whole new level of flexibility.", WPRSS_TEXT_DOMAIN ),
36
+ 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
37
+ 'active' => is_plugin_active( 'wp-rss-feed-to-post/wp-rss-feed-to-post.php' ),
38
+ 'url' => 'http://www.wprssaggregator.com/extension/feed-to-post/'
39
+ );
40
+ $premium[] = array(
41
+ 'title' => __( "Full Text RSS Feeds", WPRSS_TEXT_DOMAIN ),
42
+ 'description' => __( "This add-ons provides the connectivity to our Full Text Premium service, which gives you unlimited feed items returned per feed source.", WPRSS_TEXT_DOMAIN ),
43
+ 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
44
+ 'active' => is_plugin_active( 'wp-rss-full-text-feeds/wp-rss-full-text.php' ),
45
+ 'url' => 'http://www.wprssaggregator.com/extension/full-text-rss-feeds/'
46
+ );
47
+ $premium[] = array(
48
+ 'title' => __( "WordAi", WPRSS_TEXT_DOMAIN ),
49
+ 'description' => __( "Allows you to spin the content for posts imported by Feed to Post using WordAi. Cleverly rewrite your posts without changing their meaning and maintaining human readability.", WPRSS_TEXT_DOMAIN ),
50
+ 'thumbnail' => WPRSS_IMG . 'add-ons/wprss.jpg',
51
+ 'active' => is_plugin_active( 'wp-rss-wordai/wp-rss-wordai.php' ),
52
+ 'url' => 'http://www.wprssaggregator.com/extension/wordai/'
53
+ );
54
+
55
+ ?>
56
+ <div class="wrap">
57
+ <?php screen_icon( 'wprss-aggregator' ); ?>
58
+
59
+ <h2><?php _e( 'Add-Ons', WPRSS_TEXT_DOMAIN ); ?></h2>
60
+ <p><?php _e( "The following Add-ons are available to increase the functionality of the WP RSS Aggregator plugin.", WPRSS_TEXT_DOMAIN ); ?><br />
61
+ <?php _e( "Each Add-on can be installed as a separate plugin. Note that activating the Feed to Post plugin will deactivate the Categories and Excerpts & Thumbnails add-ons.", WPRSS_TEXT_DOMAIN ); ?></p>
62
+
63
+ <div id="add-ons" class="clearfix">
64
+
65
+ <div class="add-on-group clearfix">
66
+ <?php foreach( $premium as $addon ): ?>
67
+ <div class="add-on wp-box <?php if( $addon['active'] ): ?>add-on-active<?php endif; ?>">
68
+ <!-- <a target="_blank" href="<?php echo $addon['url']; ?>">
69
+ <img src="<?php echo $addon['thumbnail']; ?>" />
70
+ </a> -->
71
+ <div class="inner">
72
+ <h3><a target="_blank" href="<?php echo $addon['url']; ?>"><?php echo $addon['title']; ?></a></h3>
73
+ <p><?php echo $addon['description']; ?></p>
74
+ </div>
75
+ <div class="footer">
76
+ <?php if( $addon['active'] ): ?>
77
+ <a class="button button-disabled"><span class="wprss-sprite-tick"></span><?php _e( "Installed", WPRSS_TEXT_DOMAIN ); ?></a>
78
+ <?php else: ?>
79
+ <a target="_blank" href="<?php echo $addon['url']; ?>" class="button"><?php _e( "Purchase & Install", WPRSS_TEXT_DOMAIN ); ?></a>
80
+ <?php endif; ?>
81
+ </div>
82
+ </div>
83
+ <?php endforeach; ?>
84
+ </div>
85
+
86
+ </div>
87
+
88
+ </div>
89
+
90
+ <?php
91
+
92
  }
includes/admin-ajax-notice.php CHANGED
@@ -1,141 +1,141 @@
1
- <?php
2
-
3
- /**
4
- * Serves up a notice to leave a review for this plugin
5
- *
6
- * @link http://wp.tutsplus.com/tutorials/creative-coding/a-primer-on-ajax-in-the-wordpress-dashboard-requesting-and-responding/
7
- * @link http://wptheming.com/2011/08/admin-notices-in-wordpress/
8
- *
9
- * @since 3.0
10
- *
11
- */
12
-
13
- add_action( 'admin_notices', 'wprss_display_admin_notice' );
14
- /**
15
- * Renders the administration notice. Also renders a hidden nonce used for security when processing the Ajax request.
16
- *
17
- * @since 3.0
18
- */
19
- function wprss_display_admin_notice() {
20
- // If not an admin, do not show the notification
21
- if ( ! current_user_can( 'manage_options' ) ) return;
22
-
23
- global $pagenow, $typenow;
24
- if ( empty( $typenow ) && !empty( $_GET['post'] ) ) {
25
- $post = get_post( $_GET['post'] );
26
- if ( $post !== NULL && !is_wp_error( $post ) )
27
- $typenow = $post->post_type;
28
- }
29
- $notices_settings = get_option( 'wprss_settings_notices' );
30
-
31
- if ( ( false == $notices_settings ) && ( ( $typenow == 'wprss_feed' ) || ( $typenow == 'wprss_feed_item' ) ) ) {
32
- $html = '<div id="ajax-notification" class="updated">';
33
- $html .= '<p>';
34
- $html .= __( 'Did you know that you can get more RSS features? Excerpts, thumbnails, keyword filtering, importing into posts and more... ', WPRSS_TEXT_DOMAIN );
35
- $html .= __( 'Check out the', WPRSS_TEXT_DOMAIN ) . ' <a target="_blank" href="http://www.wprssaggregator.com/extensions"><strong>' . __( 'extensions', 'WPRSS_TEXT_DOMAIN' ) . '</strong></a> ' . __( 'page.', WPRSS_TEXT_DOMAIN );
36
- $html .= '<a href="javascript:;" id="dismiss-ajax-notification" style="float:right;">' . __( 'Dismiss this notification', WPRSS_TEXT_DOMAIN ) . '</a>';
37
- $html .= '</p>';
38
- $html .= '<span id="ajax-notification-nonce" class="hidden">' . wp_create_nonce( 'ajax-notification-nonce' ) . '</span>';
39
- $html .= '</div>';
40
-
41
- echo $html;
42
- }
43
- }
44
-
45
-
46
- add_action( 'wp_ajax_wprss_hide_admin_notification', 'wprss_hide_admin_notification' );
47
- /**
48
- * JavaScript callback used to hide the administration notice when the 'Dismiss' anchor is clicked on the front end.
49
- *
50
- * @since 3.0
51
- */
52
- function wprss_hide_admin_notification() {
53
-
54
- // First, check the nonce to make sure it matches what we created when displaying the message.
55
- // If not, we won't do anything.
56
- if( wp_verify_nonce( $_REQUEST['nonce'], 'ajax-notification-nonce' ) ) {
57
-
58
- // If the update to the option is successful, send 1 back to the browser;
59
- // Otherwise, send 0.
60
- $general_settings = get_option( 'wprss_settings_notices' );
61
- $general_settings = true;
62
-
63
- if( update_option( 'wprss_settings_notices', $general_settings ) ) {
64
- die( '1' );
65
- } else {
66
- die( '0' );
67
- }
68
- }
69
- }
70
-
71
-
72
-
73
- /**
74
- * Checks if the addon notices option exists in the database, and creates it
75
- * if it does not.
76
- *
77
- * @return The addon notices option
78
- * @since 3.4.2
79
- */
80
- function wprss_check_addon_notice_option() {
81
- $option = get_option( 'wprss_addon_notices' );
82
- if ( $option === FALSE ) {
83
- update_option( 'wprss_addon_notices', array() );
84
- return array();
85
- }
86
- return $option;
87
- }
88
-
89
-
90
-
91
- /**
92
- * This function is called through AJAX to dismiss a particular addon notification.
93
- *
94
- * @since 3.4.2
95
- */
96
- function wprss_dismiss_addon_notice() {
97
- $addon = ( isset( $_POST['addon'] ) === TRUE )? $_POST['addon'] : null;
98
- if ( $addon === null ) {
99
- echo 'false';
100
- die();
101
- }
102
- $notice = ( isset( $_POST['notice'] ) === TRUE )? $_POST['notice'] : null;
103
- if ( $notice === null ){
104
- echo 'false';
105
- die();
106
- }
107
-
108
- $notices = wprss_check_addon_notice_option();
109
- if ( isset( $notices[$addon] ) === FALSE ) {
110
- $notices[$addon] = array();
111
- }
112
- if ( isset( $notices[$addon][$addon] ) === FALSE ) {
113
- $notices[$addon][$notice] = '1';
114
- }
115
- update_option( 'wprss_addon_notices', $notices );
116
- echo 'true';
117
-
118
- die();
119
- }
120
-
121
- add_action( 'wp_ajax_wprss_dismiss_addon_notice', 'wprss_dismiss_addon_notice' );
122
-
123
-
124
-
125
-
126
- /**
127
- * AJAX action for the tracking pointer
128
- *
129
- * @since 3.6
130
- */
131
- function wprss_tracking_ajax_opt() {
132
- if ( isset( $_POST['opted'] ) ){
133
- $opted = $_POST['opted'];
134
- $settings = get_option( 'wprss_settings_general' );
135
- $settings['tracking'] = $opted;
136
- update_option( 'wprss_settings_general', $settings );
137
- }
138
- die();
139
- }
140
-
141
  add_action( 'wp_ajax_wprss_tracking_ajax_opt', 'wprss_tracking_ajax_opt' );
1
+ <?php
2
+
3
+ /**
4
+ * Serves up a notice to leave a review for this plugin
5
+ *
6
+ * @link http://wp.tutsplus.com/tutorials/creative-coding/a-primer-on-ajax-in-the-wordpress-dashboard-requesting-and-responding/
7
+ * @link http://wptheming.com/2011/08/admin-notices-in-wordpress/
8
+ *
9
+ * @since 3.0
10
+ *
11
+ */
12
+
13
+ add_action( 'admin_notices', 'wprss_display_admin_notice' );
14
+ /**
15
+ * Renders the administration notice. Also renders a hidden nonce used for security when processing the Ajax request.
16
+ *
17
+ * @since 3.0
18
+ */
19
+ function wprss_display_admin_notice() {
20
+ // If not an admin, do not show the notification
21
+ if ( ! current_user_can( 'manage_options' ) ) return;
22
+
23
+ global $pagenow, $typenow;
24
+ if ( empty( $typenow ) && !empty( $_GET['post'] ) ) {
25
+ $post = get_post( $_GET['post'] );
26
+ if ( $post !== NULL && !is_wp_error( $post ) )
27
+ $typenow = $post->post_type;
28
+ }
29
+ $notices_settings = get_option( 'wprss_settings_notices' );
30
+
31
+ if ( ( false == $notices_settings ) && ( ( $typenow == 'wprss_feed' ) || ( $typenow == 'wprss_feed_item' ) ) ) {
32
+ $html = '<div id="ajax-notification" class="updated">';
33
+ $html .= '<p>';
34
+ $html .= __( 'Did you know that you can get more RSS features? Excerpts, thumbnails, keyword filtering, importing into posts and more... ', WPRSS_TEXT_DOMAIN );
35
+ $html .= __( 'Check out the', WPRSS_TEXT_DOMAIN ) . ' <a target="_blank" href="http://www.wprssaggregator.com/extensions"><strong>' . __( 'extensions', 'WPRSS_TEXT_DOMAIN' ) . '</strong></a> ' . __( 'page.', WPRSS_TEXT_DOMAIN );
36
+ $html .= '<a href="javascript:;" id="dismiss-ajax-notification" style="float:right;">' . __( 'Dismiss this notification', WPRSS_TEXT_DOMAIN ) . '</a>';
37
+ $html .= '</p>';
38
+ $html .= '<span id="ajax-notification-nonce" class="hidden">' . wp_create_nonce( 'ajax-notification-nonce' ) . '</span>';
39
+ $html .= '</div>';
40
+
41
+ echo $html;
42
+ }
43
+ }
44
+
45
+
46
+ add_action( 'wp_ajax_wprss_hide_admin_notification', 'wprss_hide_admin_notification' );
47
+ /**
48
+ * JavaScript callback used to hide the administration notice when the 'Dismiss' anchor is clicked on the front end.
49
+ *
50
+ * @since 3.0
51
+ */
52
+ function wprss_hide_admin_notification() {
53
+
54
+ // First, check the nonce to make sure it matches what we created when displaying the message.
55
+ // If not, we won't do anything.
56
+ if( wp_verify_nonce( $_REQUEST['nonce'], 'ajax-notification-nonce' ) ) {
57
+
58
+ // If the update to the option is successful, send 1 back to the browser;
59
+ // Otherwise, send 0.
60
+ $general_settings = get_option( 'wprss_settings_notices' );
61
+ $general_settings = true;
62
+
63
+ if( update_option( 'wprss_settings_notices', $general_settings ) ) {
64
+ die( '1' );
65
+ } else {
66
+ die( '0' );
67
+ }
68
+ }
69
+ }
70
+
71
+
72
+
73
+ /**
74
+ * Checks if the addon notices option exists in the database, and creates it
75
+ * if it does not.
76
+ *
77
+ * @return The addon notices option
78
+ * @since 3.4.2
79
+ */
80
+ function wprss_check_addon_notice_option() {
81
+ $option = get_option( 'wprss_addon_notices' );
82
+ if ( $option === FALSE ) {
83
+ update_option( 'wprss_addon_notices', array() );
84
+ return array();
85
+ }
86
+ return $option;
87
+ }
88
+
89
+
90
+
91
+ /**
92
+ * This function is called through AJAX to dismiss a particular addon notification.
93
+ *
94
+ * @since 3.4.2
95
+ */
96
+ function wprss_dismiss_addon_notice() {
97
+ $addon = ( isset( $_POST['addon'] ) === TRUE )? $_POST['addon'] : null;
98
+ if ( $addon === null ) {
99
+ echo 'false';
100
+ die();
101
+ }
102
+ $notice = ( isset( $_POST['notice'] ) === TRUE )? $_POST['notice'] : null;
103
+ if ( $notice === null ){
104
+ echo 'false';
105
+ die();
106
+ }
107
+
108
+ $notices = wprss_check_addon_notice_option();
109
+ if ( isset( $notices[$addon] ) === FALSE ) {
110
+ $notices[$addon] = array();
111
+ }
112
+ if ( isset( $notices[$addon][$addon] ) === FALSE ) {
113
+ $notices[$addon][$notice] = '1';
114
+ }
115
+ update_option( 'wprss_addon_notices', $notices );
116
+ echo 'true';
117
+
118
+ die();
119
+ }
120
+
121
+ add_action( 'wp_ajax_wprss_dismiss_addon_notice', 'wprss_dismiss_addon_notice' );
122
+
123
+
124
+
125
+
126
+ /**
127
+ * AJAX action for the tracking pointer
128
+ *
129
+ * @since 3.6
130
+ */
131
+ function wprss_tracking_ajax_opt() {
132
+ if ( isset( $_POST['opted'] ) ){
133
+ $opted = $_POST['opted'];
134
+ $settings = get_option( 'wprss_settings_general' );
135
+ $settings['tracking'] = $opted;
136
+ update_option( 'wprss_settings_general', $settings );
137
+ }
138
+ die();
139
+ }
140
+
141
  add_action( 'wp_ajax_wprss_tracking_ajax_opt', 'wprss_tracking_ajax_opt' );
includes/admin-dashboard.php CHANGED
@@ -1,99 +1,99 @@
1
- <?php
2
- /**
3
- * Adds a dashboard page for the admin
4
- * It is triggered on plugin activation and upon
5
- * update.
6
- *
7
- * @since 3.3
8
- */
9
-
10
-
11
- // Exit if the page is accessed directly
12
- if ( ! defined( 'ABSPATH' ) ) exit;
13
-
14
-
15
- add_action( 'admin_menu', 'wprss_admin_menu' );
16
- /**
17
- * Adds a dashboard page.
18
- * Usde to add the Welcome Page to the dashboard.
19
- *
20
- * @since 3.3
21
- */
22
- function wprss_admin_menu() {
23
- // Welcome Page
24
- add_dashboard_page(
25
- __( 'Welcome to WP RSS Aggregator', WPRSS_TEXT_DOMAIN ),
26
- __( 'Welcome to WP RSS Aggregator', WPRSS_TEXT_DOMAIN ),
27
- 'manage_options',
28
- 'wprss-welcome',
29
- 'wprss_show_welcome_screen'
30
- );
31
-
32
- }
33
-
34
-
35
- /**
36
- * Callback for the Welcome Dashboard page.
37
- * It merely includes the contents of the admin-welcome.php file,
38
- * which contains the markup for the welcome screen.
39
- *
40
- * @since 3.3
41
- */
42
- function wprss_show_welcome_screen() {
43
- include_once( 'admin-welcome.php' );
44
- }
45
-
46
-
47
- add_action( 'admin_init', 'wprss_welcome' );
48
- /**
49
- * Detects an activation and redirects the user to
50
- * the welcome page.
51
- *
52
- * @since 3.3
53
- */
54
- function wprss_welcome() {
55
- // Bail if no activation redirect
56
- if ( ! get_transient( '_wprss_activation_redirect' ) )
57
- return;
58
-
59
- // Delete the redirect transient
60
- delete_transient( '_wprss_activation_redirect' );
61
-
62
- // Bail if activating from network, or bulk
63
- if ( is_network_admin() || isset( $_GET['activate-multi'] ) )
64
- return;
65
-
66
- wp_safe_redirect( admin_url( 'index.php?page=wprss-welcome' ) );
67
- exit;
68
- }
69
-
70
-
71
- add_action( 'admin_head', 'wprss_admin_head' );
72
- /**
73
- * Removes the dashboard welcome page from the dashboard
74
- * menu, and adds some styles for the welcome page.
75
- *
76
- * @since 3.3
77
- */
78
- function wprss_admin_head() {
79
- remove_submenu_page( 'index.php', 'wprss-welcome' );
80
- ?>
81
- <style type="text/css" media="screen">
82
- /*<![CDATA[*/
83
-
84
- /*.wprss-welcome-table > tbody > tr > td {
85
- line-height: 30px;
86
- }
87
- .wprss-welcome-table > tbody > tr {
88
- font-size: 1.3em;
89
- font-weight: normal;
90
- }
91
- .wprss-welcome-table > thead > tr > th{
92
- font-size: 2em;
93
- border-bottom: 8px solid #999;
94
- }*/
95
-
96
- /*]]>*/
97
- </style>
98
- <?php
99
  }
1
+ <?php
2
+ /**
3
+ * Adds a dashboard page for the admin
4
+ * It is triggered on plugin activation and upon
5
+ * update.
6
+ *
7
+ * @since 3.3
8
+ */
9
+
10
+
11
+ // Exit if the page is accessed directly
12
+ if ( ! defined( 'ABSPATH' ) ) exit;
13
+
14
+
15
+ add_action( 'admin_menu', 'wprss_admin_menu' );
16
+ /**
17
+ * Adds a dashboard page.
18
+ * Usde to add the Welcome Page to the dashboard.
19
+ *
20
+ * @since 3.3
21
+ */
22
+ function wprss_admin_menu() {
23
+ // Welcome Page
24
+ add_dashboard_page(
25
+ __( 'Welcome to WP RSS Aggregator', WPRSS_TEXT_DOMAIN ),
26
+ __( 'Welcome to WP RSS Aggregator', WPRSS_TEXT_DOMAIN ),
27
+ 'manage_options',
28
+ 'wprss-welcome',
29
+ 'wprss_show_welcome_screen'
30
+ );
31
+
32
+ }
33
+
34
+
35
+ /**
36
+ * Callback for the Welcome Dashboard page.
37
+ * It merely includes the contents of the admin-welcome.php file,
38
+ * which contains the markup for the welcome screen.
39
+ *
40
+ * @since 3.3
41
+ */
42
+ function wprss_show_welcome_screen() {
43
+ include_once( 'admin-welcome.php' );
44
+ }
45
+
46
+
47
+ add_action( 'admin_init', 'wprss_welcome' );
48
+ /**
49
+ * Detects an activation and redirects the user to
50
+ * the welcome page.
51
+ *
52
+ * @since 3.3
53
+ */
54
+ function wprss_welcome() {
55
+ // Bail if no activation redirect
56
+ if ( ! get_transient( '_wprss_activation_redirect' ) )
57
+ return;
58
+
59
+ // Delete the redirect transient
60
+ delete_transient( '_wprss_activation_redirect' );
61
+
62
+ // Bail if activating from network, or bulk
63
+ if ( is_network_admin() || isset( $_GET['activate-multi'] ) )
64
+ return;
65
+
66
+ wp_safe_redirect( admin_url( 'index.php?page=wprss-welcome' ) );
67
+ exit;
68
+ }
69
+
70
+
71
+ add_action( 'admin_head', 'wprss_admin_head' );
72
+ /**
73
+ * Removes the dashboard welcome page from the dashboard
74
+ * menu, and adds some styles for the welcome page.
75
+ *
76
+ * @since 3.3
77
+ */
78
+ function wprss_admin_head() {
79
+ remove_submenu_page( 'index.php', 'wprss-welcome' );
80
+ ?>
81
+ <style type="text/css" media="screen">
82
+ /*<![CDATA[*/
83
+
84
+ /*.wprss-welcome-table > tbody > tr > td {
85
+ line-height: 30px;
86
+ }
87
+ .wprss-welcome-table > tbody > tr {
88
+ font-size: 1.3em;
89
+ font-weight: normal;
90
+ }
91
+ .wprss-welcome-table > thead > tr > th{
92
+ font-size: 2em;
93
+ border-bottom: 8px solid #999;
94
+ }*/
95
+
96
+ /*]]>*/
97
+ </style>
98
+ <?php
99
  }
includes/admin-debugging.php CHANGED
@@ -1,312 +1,312 @@
1
- <?php
2
- /**
3
- * Plugin debugging
4
- *
5
- * @package WPRSSAggregator
6
- * @subpackage Includes
7
- * @since 3.0
8
- * @author Jean Galea <info@jeangalea.com>
9
- * @copyright Copyright(c) 2012-2013, Jean Galea
10
- * @link http://www.wpmayor.com
11
- * @license http://www.gnu.org/licenses/gpl.html
12
- */
13
-
14
- /*
15
- //allow redirection, even if my theme starts to send output to the browser
16
- add_action( 'admin_init', 'wprss_do_output_buffer' );
17
- function wprss_do_output_buffer() {
18
- //ob_start();
19
- }*/
20
-
21
-
22
- /**
23
- * Returns the debugging operations array
24
- *
25
- * @since 3.4.6
26
- */
27
- function wprss_get_debug_operations() {
28
- $operations = apply_filters(
29
- 'wprss_debug_operations',
30
- array(
31
- 'update-feeds' => array(
32
- 'nonce' => 'wprss-update-feed-items',
33
- 'run' => 'wprss_fetch_insert_all_feed_items',
34
- 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging&debug_message=1',
35
- 'render' => 'wprss_debug_update_feeds',
36
- ),
37
-
38
- 'reimport-feeds' => array(
39
- 'nonce' => 'wprss-delete-import-feed-items',
40
- 'run' => 'wprss_feed_reset',
41
- 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging&debug_message=2',
42
- 'render' => 'wprss_debug_reimport_feeds',
43
- ),
44
-
45
- )
46
- );
47
-
48
- $operations['error-log'] = apply_filters(
49
- 'wprss_debug_error_log_operation',
50
- array(
51
- 'nonce' => 'wprss-clear-error-log',
52
- 'run' => 'wprss_clear_log',
53
- 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging&debug_message=3',
54
- 'render' => 'wprss_debug_clear_log_button'
55
- )
56
- );
57
-
58
- $operations ['restore-settings'] = apply_filters(
59
- 'wprss_debug_restore_settings_operation',
60
- array(
61
- 'nonce' => 'wprss-restore-settings',
62
- 'run' => 'wprss_restore_settings',
63
- 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging&debug_message=4',
64
- 'render' => 'wprss_debug_restore_settings',
65
- 'pos' => 'bottom'
66
- )
67
- );
68
-
69
- return $operations;
70
- }
71
-
72
-
73
- add_action( 'admin_init', 'wprss_debug_operations' );
74
- /**
75
- * Performs debug operations, depending on the POST request.
76
- *
77
- * @since 3.3
78
- */
79
- function wprss_debug_operations(){
80
-
81
- // Define the debugging operations
82
- $debug_operations = wprss_get_debug_operations();
83
-
84
- // Check which of the operations needs to be run
85
- foreach ( $debug_operations as $id => $operation ) {
86
- // If page loading after having clicked 'Update all fields'
87
- if ( isset( $_POST[ $id ] ) && check_admin_referer( $operation['nonce'] ) ) {
88
- call_user_func( $operation['run'] );
89
- wp_redirect( $operation['redirect'] );
90
- break;
91
- }
92
- }
93
- }
94
-
95
-
96
- /**
97
- * Build the Update Feeds section
98
- *
99
- * @since 3.4.6
100
- */
101
- function wprss_debug_update_feeds() {
102
- ?>
103
- <h3><?php _e( 'Update All Feeds Now', WPRSS_TEXT_DOMAIN ); ?></h3>
104
- <p><?php _e( 'Click the blue button to update all active feed items now. This will check all feed sources for any new feed items.', WPRSS_TEXT_DOMAIN ); ?>
105
- <br><?php _e( 'Existing feed items will not be modified.', WPRSS_TEXT_DOMAIN ); ?>
106
- </p>
107
- <p><?php _e( '<strong>Note:</strong> This might take more than a few seconds if you have many feed sources.', WPRSS_TEXT_DOMAIN ); ?></p>
108
-
109
- <form action="edit.php?post_type=wprss_feed&page=wprss-debugging" method="post">
110
-
111
- <?php wp_nonce_field( 'wprss-update-feed-items' );
112
- submit_button( __( 'Update all feeds', WPRSS_TEXT_DOMAIN ), 'primary', 'update-feeds', true ); ?>
113
-
114
- </form>
115
- <?php
116
- }
117
-
118
-
119
- /**
120
- * Build the Delete and Re-Import Feeds section
121
- *
122
- * @since 3.4.6
123
- */
124
- function wprss_debug_reimport_feeds() {
125
- ?>
126
- <h3><?php _e( 'Delete and Re-import Feeds', WPRSS_TEXT_DOMAIN ); ?></h3>
127
- <p><?php _e( 'Click the red button to delete all imported feed items and re-import them.', WPRSS_TEXT_DOMAIN ); ?></p>
128
- <p><?php _e( '<em><strong>Note:</strong> This is a server-intensive process and should only be used when instructed to by support staff.</em>', WPRSS_TEXT_DOMAIN ); ?></p>
129
-
130
- <form action="edit.php?post_type=wprss_feed&page=wprss-debugging" method="post">
131
-
132
- <?php wp_nonce_field( 'wprss-delete-import-feed-items' );
133
- submit_button( __( 'Delete and Re-import all feeds', WPRSS_TEXT_DOMAIN ), 'button-red', 'reimport-feeds', true ); ?>
134
-
135
- </form>
136
- <?php
137
- }
138
-
139
-
140
- /**
141
- * Render the restore settings button
142
- *
143
- * @since 4.4
144
- */
145
- function wprss_debug_restore_settings() {
146
- ?>
147
- <h3><?php _e( 'Restore Default Settings', WPRSS_TEXT_DOMAIN ); ?></h3>
148
- <p><?php _e( 'Click the red button to reset the plugin settings to default.', WPRSS_TEXT_DOMAIN ); ?></p>
149
- <p><?php _e( '<em><strong>Note:</strong> This cannot be undone. Once the settings have been reset, your old settings cannot be restored.</em>', WPRSS_TEXT_DOMAIN ); ?></p>
150
-
151
- <form action="edit.php?post_type=wprss_feed&page=wprss-debugging" method="post">
152
-
153
- <?php wp_nonce_field( 'wprss-restore-settings' );
154
- submit_button( __( 'Restore Default Settings', WPRSS_TEXT_DOMAIN ), 'button-red', 'restore-settings', true ); ?>
155
-
156
- </form>
157
- <?php
158
- }
159
-
160
-
161
- /**
162
- * Renders the Clear Log button
163
- *
164
- * @since 3.9.6
165
- */
166
- function wprss_debug_clear_log_button() {
167
- ?>
168
- <h3><?php _e( 'Error Log', WPRSS_TEXT_DOMAIN ); ?></h3>
169
-
170
- <textarea readonly="readonly" id="wprss-error-log-textarea"><?php echo wprss_get_log(); ?></textarea>
171
-
172
- <form action="edit.php?post_type=wprss_feed&page=wprss-debugging" method="POST">
173
- <?php wp_nonce_field( 'wprss-clear-error-log' );
174
- submit_button( __( 'Clear log', WPRSS_TEXT_DOMAIN ), 'button-primary', 'error-log', true ); ?>
175
- </form>
176
-
177
- <?php
178
- }
179
-
180
-
181
- /**
182
- * Build the debugging page
183
- *
184
- * @since 3.0
185
- */
186
- function wprss_debugging_page_display() {
187
- $debug_messages = apply_filters(
188
- 'wprss_debug_messages',
189
- array(
190
- '1' => 'wprss_debugging_admin_notice_update_feeds',
191
- '2' => 'wprss_debugging_admin_notice_reimport_feeds',
192
- '3' => 'wprss_debugging_admin_notice_clear_log',
193
- '4' => 'wprss_debugging_admin_notice_reset_settings'
194
- )
195
- );
196
-
197
- ?>
198
-
199
- <div class="wrap">
200
- <?php screen_icon( 'wprss-aggregator' ); ?>
201
-
202
- <h2><?php _e( 'Debugging', WPRSS_TEXT_DOMAIN ); ?></h2>
203
- <?php
204
- if ( isset( $_GET['debug_message'] )) {//&& ( check_admin_referer( 'wprss-delete-import-feed-items' ) || check_admin_referer( 'wprss-update-feed-items' ) ) ) {
205
- $message = $_GET['debug_message'];
206
-
207
- foreach ( $debug_messages as $id => $callback) {
208
- if ( $message == $id ) {
209
- call_user_func( $callback );
210
- break;
211
- }
212
- }
213
- }
214
-
215
- do_action( 'wprss_debugging_before' );
216
-
217
- $bottom = array();
218
- $debug_operations = wprss_get_debug_operations();
219
- foreach( $debug_operations as $id => $data ) {
220
- if ( !isset( $data['render'] ) ) continue;
221
- $pos = isset( $data['pos'] ) ? $data['pos'] : 'normal';
222
- if ( $pos == 'normal' ) {
223
- call_user_func( $data['render'] );
224
- } elseif( $pos == 'bottom' ) {
225
- $bottom[$id] = $data;
226
- }
227
- }
228
-
229
- do_action( 'wprss_debugging_after' );
230
-
231
- wprss_system_info();
232
-
233
- if ( count($bottom) > 0 ) {
234
- foreach( $bottom as $id => $data ) {
235
- if ( !isset( $data['render'] ) ) continue;
236
- call_user_func( $data['render'] );
237
- }
238
- }
239
-
240
- ?>
241
- </div>
242
- <?php
243
- }
244
-
245
-
246
- /**
247
- * Output admin notice that feeds have been updated successfully
248
- *
249
- * @since 3.0
250
- */
251
- function wprss_debugging_admin_notice_update_feeds() {
252
- ?><div class="updated"><p><?php _e( 'Feeds are being updated in the background.', WPRSS_TEXT_DOMAIN ) ?></p></div><?php
253
- }
254
-
255
- /**
256
- * Output admin notice that feeds have been deleted and re-imported successfully
257
- *
258
- * @since 3.0
259
- */
260
- function wprss_debugging_admin_notice_reimport_feeds() {
261
- ?><div class="updated"><p><?php _e( 'Feeds deleted and are being re-imported in the background.', WPRSS_TEXT_DOMAIN ) ?></p></div><?php
262
- }
263
-
264
-
265
- /**
266
- * Output admin notice that log has been cleard
267
- *
268
- * @since 3.9.6
269
- */
270
- function wprss_debugging_admin_notice_clear_log() {
271
- ?><div class="updated"><p><?php _e( 'The error log has been cleared.', WPRSS_TEXT_DOMAIN ) ?></p></div><?php
272
- }
273
-
274
-
275
- /**
276
- * Output admin notice that log has been cleard
277
- *
278
- * @since 4.4
279
- */
280
- function wprss_debugging_admin_notice_reset_settings() {
281
- ?><div class="updated"><p><?php _e( 'The plugin settings have been reset to default.', WPRSS_TEXT_DOMAIN ) ?></p></div><?php
282
- }
283
-
284
-
285
- /**
286
- * Resets the plugin settings to default
287
- *
288
- * @since 4.4
289
- */
290
- function wprss_restore_settings() {
291
- // Action Hook
292
- do_action( 'wprss_before_restore_settings' );
293
-
294
- // Prepare the settings to reset
295
- $settings_to_restore = apply_filters(
296
- 'wprss_settings_to_restore',
297
- array(
298
- 'wprss_settings_general',
299
- 'wprss_settings_notices',
300
- 'wprss_addon_notices',
301
- 'wprss_pwsv',
302
- 'wprss_db_version'
303
- )
304
- );
305
- // Delete the settings
306
- foreach( $settings_to_restore as $setting ) {
307
- delete_option( $setting );
308
- }
309
-
310
- // Action Hook
311
- do_action( 'wprss_after_restore_settings' );
312
- }
1
+ <?php
2
+ /**
3
+ * Plugin debugging
4
+ *
5
+ * @package WPRSSAggregator
6
+ * @subpackage Includes
7
+ * @since 3.0
8
+ * @author Jean Galea <info@jeangalea.com>
9
+ * @copyright Copyright(c) 2012-2013, Jean Galea
10
+ * @link http://www.wpmayor.com
11
+ * @license http://www.gnu.org/licenses/gpl.html
12
+ */
13
+
14
+ /*
15
+ //allow redirection, even if my theme starts to send output to the browser
16
+ add_action( 'admin_init', 'wprss_do_output_buffer' );
17
+ function wprss_do_output_buffer() {
18
+ //ob_start();
19
+ }*/
20
+
21
+
22
+ /**
23
+ * Returns the debugging operations array
24
+ *
25
+ * @since 3.4.6
26
+ */
27
+ function wprss_get_debug_operations() {
28
+ $operations = apply_filters(
29
+ 'wprss_debug_operations',
30
+ array(
31
+ 'update-feeds' => array(
32
+ 'nonce' => 'wprss-update-feed-items',
33
+ 'run' => 'wprss_fetch_insert_all_feed_items',
34
+ 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging&debug_message=1',
35
+ 'render' => 'wprss_debug_update_feeds',
36
+ ),
37
+
38
+ 'reimport-feeds' => array(
39
+ 'nonce' => 'wprss-delete-import-feed-items',
40
+ 'run' => 'wprss_feed_reset',
41
+ 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging&debug_message=2',
42
+ 'render' => 'wprss_debug_reimport_feeds',
43
+ ),
44
+
45
+ )
46
+ );
47
+
48
+ $operations['error-log'] = apply_filters(
49
+ 'wprss_debug_error_log_operation',
50
+ array(
51
+ 'nonce' => 'wprss-clear-error-log',
52
+ 'run' => 'wprss_clear_log',
53
+ 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging&debug_message=3',
54
+ 'render' => 'wprss_debug_clear_log_button'
55
+ )
56
+ );
57
+
58
+ $operations ['restore-settings'] = apply_filters(
59
+ 'wprss_debug_restore_settings_operation',
60
+ array(
61
+ 'nonce' => 'wprss-restore-settings',
62
+ 'run' => 'wprss_restore_settings',
63
+ 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging&debug_message=4',
64
+ 'render' => 'wprss_debug_restore_settings',
65
+ 'pos' => 'bottom'
66
+ )
67
+ );
68
+
69
+ return $operations;
70
+ }
71
+
72
+
73
+ add_action( 'admin_init', 'wprss_debug_operations' );
74
+ /**
75
+ * Performs debug operations, depending on the POST request.
76
+ *
77
+ * @since 3.3
78
+ */
79
+ function wprss_debug_operations(){
80
+
81
+ // Define the debugging operations
82
+ $debug_operations = wprss_get_debug_operations();
83
+
84
+ // Check which of the operations needs to be run
85
+ foreach ( $debug_operations as $id => $operation ) {
86
+ // If page loading after having clicked 'Update all fields'
87
+ if ( isset( $_POST[ $id ] ) && check_admin_referer( $operation['nonce'] ) ) {
88
+ call_user_func( $operation['run'] );
89
+ wp_redirect( $operation['redirect'] );
90
+ break;
91
+ }
92
+ }
93
+ }
94
+
95
+
96
+ /**
97
+ * Build the Update Feeds section
98
+ *
99
+ * @since 3.4.6
100
+ */
101
+ function wprss_debug_update_feeds() {
102
+ ?>
103
+ <h3><?php _e( 'Update All Feeds Now', WPRSS_TEXT_DOMAIN ); ?></h3>
104
+ <p><?php _e( 'Click the blue button to update all active feed items now. This will check all feed sources for any new feed items.', WPRSS_TEXT_DOMAIN ); ?>
105
+ <br><?php _e( 'Existing feed items will not be modified.', WPRSS_TEXT_DOMAIN ); ?>
106
+ </p>
107
+ <p><?php _e( '<strong>Note:</strong> This might take more than a few seconds if you have many feed sources.', WPRSS_TEXT_DOMAIN ); ?></p>
108
+
109
+ <form action="edit.php?post_type=wprss_feed&page=wprss-debugging" method="post">
110
+
111
+ <?php wp_nonce_field( 'wprss-update-feed-items' );
112
+ submit_button( __( 'Update all feeds', WPRSS_TEXT_DOMAIN ), 'primary', 'update-feeds', true ); ?>
113
+
114
+ </form>
115
+ <?php
116
+ }
117
+
118
+
119
+ /**
120
+ * Build the Delete and Re-Import Feeds section
121
+ *
122
+ * @since 3.4.6
123
+ */
124
+ function wprss_debug_reimport_feeds() {
125
+ ?>
126
+ <h3><?php _e( 'Delete and Re-import Feeds', WPRSS_TEXT_DOMAIN ); ?></h3>
127
+ <p><?php _e( 'Click the red button to delete all imported feed items and re-import them.', WPRSS_TEXT_DOMAIN ); ?></p>
128
+ <p><?php _e( '<em><strong>Note:</strong> This is a server-intensive process and should only be used when instructed to by support staff.</em>', WPRSS_TEXT_DOMAIN ); ?></p>
129
+
130
+ <form action="edit.php?post_type=wprss_feed&page=wprss-debugging" method="post">
131
+
132
+ <?php wp_nonce_field( 'wprss-delete-import-feed-items' );
133
+ submit_button( __( 'Delete and Re-import all feeds', WPRSS_TEXT_DOMAIN ), 'button-red', 'reimport-feeds', true ); ?>
134
+
135
+ </form>
136
+ <?php
137
+ }
138
+
139
+
140
+ /**
141
+ * Render the restore settings button
142
+ *
143
+ * @since 4.4
144
+ */
145
+ function wprss_debug_restore_settings() {
146
+ ?>
147
+ <h3><?php _e( 'Restore Default Settings', WPRSS_TEXT_DOMAIN ); ?></h3>
148
+ <p><?php _e( 'Click the red button to reset the plugin settings to default.', WPRSS_TEXT_DOMAIN ); ?></p>
149
+ <p><?php _e( '<em><strong>Note:</strong> This cannot be undone. Once the settings have been reset, your old settings cannot be restored.</em>', WPRSS_TEXT_DOMAIN ); ?></p>
150
+
151
+ <form action="edit.php?post_type=wprss_feed&page=wprss-debugging" method="post">
152
+
153
+ <?php wp_nonce_field( 'wprss-restore-settings' );
154
+ submit_button( __( 'Restore Default Settings', WPRSS_TEXT_DOMAIN ), 'button-red', 'restore-settings', true ); ?>
155
+
156
+ </form>
157
+ <?php
158
+ }
159
+
160
+
161
+ /**
162
+ * Renders the Clear Log button
163
+ *
164
+ * @since 3.9.6
165
+ */
166
+ function wprss_debug_clear_log_button() {
167
+ ?>
168
+ <h3><?php _e( 'Error Log', WPRSS_TEXT_DOMAIN ); ?></h3>
169
+
170
+ <textarea readonly="readonly" id="wprss-error-log-textarea"><?php echo wprss_get_log(); ?></textarea>
171
+
172
+ <form action="edit.php?post_type=wprss_feed&page=wprss-debugging" method="POST">
173
+ <?php wp_nonce_field( 'wprss-clear-error-log' );
174
+ submit_button( __( 'Clear log', WPRSS_TEXT_DOMAIN ), 'button-primary', 'error-log', true ); ?>
175
+ </form>
176
+
177
+ <?php
178
+ }
179
+
180
+
181
+ /**
182
+ * Build the debugging page
183
+ *
184
+ * @since 3.0
185
+ */
186
+ function wprss_debugging_page_display() {
187
+ $debug_messages = apply_filters(
188
+ 'wprss_debug_messages',
189
+ array(
190
+ '1' => 'wprss_debugging_admin_notice_update_feeds',
191
+ '2' => 'wprss_debugging_admin_notice_reimport_feeds',
192
+ '3' => 'wprss_debugging_admin_notice_clear_log',
193
+ '4' => 'wprss_debugging_admin_notice_reset_settings'
194
+ )
195
+ );
196
+
197
+ ?>
198
+
199
+ <div class="wrap">
200
+ <?php screen_icon( 'wprss-aggregator' ); ?>
201
+
202
+ <h2><?php _e( 'Debugging', WPRSS_TEXT_DOMAIN ); ?></h2>
203
+ <?php
204
+ if ( isset( $_GET['debug_message'] )) {//&& ( check_admin_referer( 'wprss-delete-import-feed-items' ) || check_admin_referer( 'wprss-update-feed-items' ) ) ) {
205
+ $message = $_GET['debug_message'];
206
+
207
+ foreach ( $debug_messages as $id => $callback) {
208
+ if ( $message == $id ) {
209
+ call_user_func( $callback );
210
+ break;
211
+ }
212
+ }
213
+ }
214
+
215
+ do_action( 'wprss_debugging_before' );
216
+
217
+ $bottom = array();
218
+ $debug_operations = wprss_get_debug_operations();
219
+ foreach( $debug_operations as $id => $data ) {
220
+ if ( !isset( $data['render'] ) ) continue;
221
+ $pos = isset( $data['pos'] ) ? $data['pos'] : 'normal';
222
+ if ( $pos == 'normal' ) {
223
+ call_user_func( $data['render'] );
224
+ } elseif( $pos == 'bottom' ) {
225
+ $bottom[$id] = $data;
226
+ }
227
+ }
228
+
229
+ do_action( 'wprss_debugging_after' );
230
+
231
+ wprss_system_info();
232
+
233
+ if ( count($bottom) > 0 ) {
234
+ foreach( $bottom as $id => $data ) {
235
+ if ( !isset( $data['render'] ) ) continue;
236
+ call_user_func( $data['render'] );
237
+ }
238
+ }
239
+
240
+ ?>
241
+ </div>
242
+ <?php
243
+ }
244
+
245
+
246
+ /**
247
+ * Output admin notice that feeds have been updated successfully
248
+ *
249
+ * @since 3.0
250
+ */
251
+ function wprss_debugging_admin_notice_update_feeds() {
252
+ ?><div class="updated"><p><?php _e( 'Feeds are being updated in the background.', WPRSS_TEXT_DOMAIN ) ?></p></div><?php
253
+ }
254
+
255
+ /**
256
+ * Output admin notice that feeds have been deleted and re-imported successfully
257
+ *
258
+ * @since 3.0
259
+ */
260
+ function wprss_debugging_admin_notice_reimport_feeds() {
261
+ ?><div class="updated"><p><?php _e( 'Feeds deleted and are being re-imported in the background.', WPRSS_TEXT_DOMAIN ) ?></p></div><?php
262
+ }
263
+
264
+
265
+ /**
266
+ * Output admin notice that log has been cleard
267
+ *
268
+ * @since 3.9.6
269
+ */
270
+ function wprss_debugging_admin_notice_clear_log() {
271
+ ?><div class="updated"><p><?php _e( 'The error log has been cleared.', WPRSS_TEXT_DOMAIN ) ?></p></div><?php
272
+ }
273
+
274
+
275
+ /**
276
+ * Output admin notice that log has been cleard
277
+ *
278
+ * @since 4.4
279
+ */
280
+ function wprss_debugging_admin_notice_reset_settings() {
281
+ ?><div class="updated"><p><?php _e( 'The plugin settings have been reset to default.', WPRSS_TEXT_DOMAIN ) ?></p></div><?php
282
+ }
283
+
284
+
285
+ /**
286
+ * Resets the plugin settings to default
287
+ *
288
+ * @since 4.4
289
+ */
290
+ function wprss_restore_settings() {
291
+ // Action Hook
292
+ do_action( 'wprss_before_restore_settings' );
293
+
294
+ // Prepare the settings to reset
295
+ $settings_to_restore = apply_filters(
296
+ 'wprss_settings_to_restore',
297
+ array(
298
+ 'wprss_settings_general',
299
+ 'wprss_settings_notices',
300
+ 'wprss_addon_notices',
301
+ 'wprss_pwsv',
302
+ 'wprss_db_version'
303
+ )
304
+ );
305
+ // Delete the settings
306
+ foreach( $settings_to_restore as $setting ) {
307
+ delete_option( $setting );
308
+ }
309
+
310
+ // Action Hook
311
+ do_action( 'wprss_after_restore_settings' );
312
+ }
includes/admin-display.php CHANGED
@@ -1,619 +1,618 @@
1
- <?php
2
- /**
3
- * Functions for the admin section, columns and row actions
4
- *
5
- * @package WP RSS Aggregator
6
- */
7
-
8
-
9
- add_filter( 'manage_wprss_feed_posts_columns', 'wprss_set_feed_custom_columns', 20, 1 );
10
- /**
11
- * Set up the custom columns for the wprss_feed list
12
- *
13
- * @since 2.0
14
- */
15
- function wprss_set_feed_custom_columns( $columns ) {
16
-
17
- $columns = array(
18
- 'cb' => '<input type="checkbox" />',
19
- 'errors' => '',
20
- 'title' => __( 'Name', WPRSS_TEXT_DOMAIN ),
21
- );
22
-
23
- $columns = apply_filters( 'wprss_set_feed_custom_columns', $columns );
24
-
25
- // Columns to add when feed is not trashed
26
- if ( !isset( $_GET['post_status'] ) || $_GET['post_status'] !== 'trash' ) {
27
- $columns['state'] = __( 'State', WPRSS_TEXT_DOMAIN );
28
- $columns['updates'] = __( 'Updates', WPRSS_TEXT_DOMAIN );
29
- $columns['feed-count'] = __( apply_filters( 'wprss_feed_items_count_column', 'Imported items' ), WPRSS_TEXT_DOMAIN );
30
- }
31
-
32
- return $columns;
33
- }
34
-
35
-
36
- add_action( "manage_wprss_feed_posts_custom_column", "wprss_show_custom_columns", 10, 2 );
37
- /**
38
- * Show up the custom columns for the wprss_feed list
39
- *
40
- * @since 2.0
41
- */
42
- function wprss_show_custom_columns( $column, $post_id ) {
43
-
44
- switch ( $column ) {
45
- case 'errors':
46
- $errors = get_post_meta( $post_id, 'wprss_error_last_import', true );
47
- $showClass = ( $errors !== '' )? 'wprss-show' : '';
48
- $default_msg = __( "This feed source experienced an error during the last feed fetch or validation check. Re-check the feed source URL or check the Error Log in the Debugging page for more details.", WPRSS_TEXT_DOMAIN );
49
- $msg = strlen( $errors ) > 0 ? $errors : $default_msg;
50
- echo "<i title=\"$msg\" class=\"fa fa-warning fa-fw wprss-feed-error-symbol $showClass\"></i>";
51
- break;
52
- case 'state':
53
- $active = wprss_is_feed_source_active( $post_id );
54
- $text = ( $active )? 'Active' : 'Paused';
55
- $button = ( $active )? 'Pause this feed source' : 'Activate this feed source';
56
- $icon = ( $active )? 'pause' : 'play';
57
- $value = ( $active )? 'paused' : 'active';
58
- $indicator = ( $active )? 'green' : 'grey';
59
-
60
- ?>
61
- <p>
62
- <span class="wprss-indicator-<?php echo $indicator; ?>" title="<?php _e( $text, WPRSS_TEXT_DOMAIN ) ?>">
63
- <i class="fa fa-circle"></i>
64
- </span>
65
- <input type="hidden" name="wprss-redirect" value="1" />
66
- <button type="submit" class='button-secondary' title="<?php _e( $button, WPRSS_TEXT_DOMAIN ) ?>" name="wprss-feed-id" value="<?php echo $post_id; ?>">
67
- <i class='fa fa-<?php echo $icon; ?>'></i>
68
- </button>
69
- </p>
70
- <?php
71
-
72
- break;
73
-
74
- case 'updates':
75
- // Get the update interval
76
- $update_interval = get_post_meta( $post_id, 'wprss_update_interval', TRUE );
77
- // Get the last updated and next update data
78
- $last_update = get_post_meta( $post_id, 'wprss_last_update', TRUE );
79
- $last_update_items = get_post_meta( $post_id, 'wprss_last_update_items', TRUE );
80
- $next_update = wprss_get_next_feed_source_update( $post_id );
81
-
82
- // If using the global interval, get the timestamp of the next global update
83
- if ( $update_interval === wprss_get_default_feed_source_update_interval() || $update_interval === '' ) {
84
- $next_update = wp_next_scheduled( 'wprss_fetch_all_feeds_hook', array() );
85
- }
86
-
87
- // Update the meta field
88
- if ( wprss_is_feed_source_active( $post_id ) ) {
89
- $next_update_text = $next_update === FALSE ? __( 'None', WPRSS_TEXT_DOMAIN ) : human_time_diff( $next_update, time() );
90
- } else {
91
- $next_update_text = __( 'Paused', WPRSS_TEXT_DOMAIN );
92
- }
93
- update_post_meta( $post_id, 'wprss_next_update', $next_update_text );
94
-
95
- ?>
96
-
97
- <p>
98
- <?php _e( 'Next update:', WPRSS_TEXT_DOMAIN ) ?>
99
- <code class="next-update">
100
- <?php echo $next_update_text; ?>
101
- </code>
102
- </p>
103
-
104
- <?php if ( $last_update !== '' ): ?>
105
- <p class="last-update-container">
106
- <?php _e( 'Last updated:', WPRSS_TEXT_DOMAIN ) ?>
107
- <code class="last-update"><?php echo sprintf( __( '%1$s ago', WPRSS_TEXT_DOMAIN ), human_time_diff( $last_update, time() ) ) ?></code>
108
- <?php if ( $last_update_items !== '' ): ?>
109
- <span class="last-update-imported-container"><br/><?php echo sprintf( __( 'Last update imported: <code class="last-update-imported">%1$d</code> items', WPRSS_TEXT_DOMAIN ), $last_update_items ) ?></span>
110
- <?php endif; ?>
111
- </p>
112
- <?php endif;
113
-
114
- break;
115
-
116
- case 'feed-count':
117
- $items = wprss_get_feed_items_for_source( $post_id );
118
- $seconds_for_next_update = wprss_get_next_feed_source_update( $post_id ) - time();
119
- $showClass = ( ( $seconds_for_next_update < 10 && $seconds_for_next_update > 0 ) || wprss_is_feed_source_deleting( $post_id ) )? 'wprss-show' : '';
120
-
121
- ?>
122
- <p>
123
- <span class="items-imported"><?php echo $items->post_count ?></span>
124
- <i class="fa fa-fw fa-refresh fa-spin wprss-updating-feed-icon <?php echo $showClass ?>" title="<?php _e( 'Updating feed source', WPRSS_TEXT_DOMAIN ) ?>"></i>
125
- </p>
126
- <?php
127
-
128
- // Set meta field for items imported
129
- update_post_meta( $post_id, 'wprss_items_imported', $items->post_count );
130
-
131
- break;
132
- }
133
- }
134
-
135
-
136
- add_filter( "manage_edit-wprss_feed_sortable_columns", "wprss_feed_sortable_columns" );
137
- /**
138
- * Make the custom columns sortable for wprss_feed post type
139
- *
140
- * @since 2.0
141
- */
142
- function wprss_feed_sortable_columns() {
143
- $sortable_columns = array(
144
- // meta column id => sortby value used in query
145
- 'title' => 'title',
146
- 'updates' => 'updates',
147
- 'state' => 'state',
148
- 'feed-count' => 'feed-count'
149
- );
150
- return apply_filters( 'wprss_feed_sortable_columns', $sortable_columns );
151
- }
152
-
153
-
154
- add_action( 'pre_get_posts', 'wprss_feed_source_order' );
155
- /**
156
- * Change order of feed sources to alphabetical ascending according to feed name
157
- *
158
- * @since 2.2
159
- */
160
- function wprss_feed_source_order( $query ) {
161
- // Check if the query is being processed in WP Admin, is the main query, and is targetted
162
- // for the wprss_feed CPT. If not, stop
163
- if ( !is_admin() || !$query->is_main_query() || $query->get('post_type') !== 'wprss_feed' ) {
164
- return;
165
- }
166
- // Check if the orderby query variable is set
167
- if ( !( $orderby = $query->get( 'orderby' ) ) ) return;
168
-
169
- // We will be sorting using the meta value (unless sorting by title)
170
- $query->set('orderby', 'meta_value' );
171
- // Get the current order
172
- $order = strtoupper( $query->get( 'order' ) );
173
- // Check if it is valid
174
- $got_order = $order === 'ASC' || $order === 'DESC';
175
-
176
- // Check what we are sorting by
177
- switch ( $orderby ) {
178
- case 'title':
179
- $query->set( 'orderby', 'title' );
180
- break;
181
- case 'state':
182
- $query->set('meta_key', 'wprss_state');
183
- break;
184
- case 'updates':
185
- $query->set('meta_key', 'wprss_next_update');
186
- $query->set('orderby', 'meta_value' );
187
- if ( !$got_order ) $query->set( 'order', 'ASC' );
188
- break;
189
- case 'feed-count':
190
- $query->set('meta_key', 'wprss_items_imported');
191
- $query->set('orderby', 'meta_value_num' );
192
- if ( !$got_order ) $query->set( 'order', 'DESC' );
193
- break;
194
- }
195
-
196
- if ( !$got_order ){
197
- $query->set( 'order', 'ASC' );
198
- }
199
- }
200
-
201
-
202
- add_filter( 'manage_wprss_feed_item_posts_columns', 'wprss_set_feed_item_custom_columns', 20, 1 );
203
- /**
204
- * Set up the custom columns for the wprss_feed source list
205
- *
206
- * @since 2.0
207
- */
208
- function wprss_set_feed_item_custom_columns( $columns ) {
209
-
210
- $columns = array (
211
- 'cb' => '<input type="checkbox" />',
212
- 'title' => __( 'Name', WPRSS_TEXT_DOMAIN ),
213
- 'permalink' => __( 'Permalink', WPRSS_TEXT_DOMAIN ),
214
- 'publishdate' => __( 'Date published', WPRSS_TEXT_DOMAIN ),
215
- 'source' => __( 'Source', WPRSS_TEXT_DOMAIN )
216
- );
217
- return apply_filters( 'wprss_set_feed_item_custom_columns', $columns );
218
- }
219
-
220
-
221
- add_action( "manage_wprss_feed_item_posts_custom_column", "wprss_show_feed_item_custom_columns", 10, 2 );
222
- /**
223
- * Show up the custom columns for the wprss_feed list
224
- *
225
- * @since 2.0
226
- */
227
- function wprss_show_feed_item_custom_columns( $column, $post_id ) {
228
-
229
- switch ( $column ) {
230
- case "permalink":
231
- $url = get_post_meta( $post_id, 'wprss_item_permalink', true);
232
- echo '<a href="' . $url . '">' . $url. '</a>';
233
- break;
234
-
235
- case "publishdate":
236
- $item_date = get_the_time( 'U', get_the_ID() );
237
- $item_date = ( $item_date === '' )? date('U') : $item_date;
238
- $publishdate = date( 'Y-m-d H:i:s', $item_date ) ;
239
- echo $publishdate;
240
- break;
241
-
242
- case "source":
243
- $query = new WP_Query();
244
- $source = '<a href="' . get_edit_post_link( get_post_meta( $post_id, 'wprss_feed_id', true ) ) . '">' . get_the_title( get_post_meta( $post_id, 'wprss_feed_id', true ) ) . '</a>';
245
- echo $source;
246
- break;
247
- }
248
- }
249
-
250
-
251
- add_filter( "manage_edit-wprss_feed_item_sortable_columns", "wprss_feed_item_sortable_columns" );
252
- /**
253
- * Make the custom columns sortable
254
- *
255
- * @since 2.0
256
- */
257
- function wprss_feed_item_sortable_columns() {
258
- $sortable_columns = array(
259
- // meta column id => sortby value used in query
260
- 'publishdate' => 'publishdate',
261
- 'source' => 'source'
262
- );
263
- return apply_filters( 'wprss_feed_item_sortable_columns', $sortable_columns );
264
- }
265
-
266
-
267
- add_action( 'pre_get_posts', 'wprss_feed_item_orderby' );
268
- /**
269
- * Change ordering of posts on wprss_feed_item screen
270
- *
271
- * @since 2.0
272
- */
273
- function wprss_feed_item_orderby( $query ) {
274
- if( ! is_admin() )
275
- return;
276
-
277
- $post_type = $query->get('post_type');
278
-
279
- // If we're on the feed listing admin page
280
- if ( $post_type == 'wprss_feed_item') {
281
- // Set general orderby to date the feed item was published
282
- $query->set('orderby','publishdate');
283
- // If user clicks on the reorder link, implement reordering
284
- $orderby = $query->get( 'orderby');
285
- if( 'publishdate' == $orderby ) {
286
- $query->set( 'order', 'DESC' );
287
- $query->set( 'orderby', 'date' );
288
- }
289
- }
290
- }
291
-
292
-
293
- add_filter( 'post_updated_messages', 'wprss_feed_updated_messages' );
294
- /**
295
- * Change default notification message when new feed is added or updated
296
- *
297
- * @since 2.0
298
- */
299
- function wprss_feed_updated_messages( $messages ) {
300
- global $post, $post_ID;
301
-
302
- $messages[ 'wprss_feed' ] = array(
303
- 0 => '', // Unused. Messages start at index 1.
304
- 1 => __( 'Feed source updated. ', WPRSS_TEXT_DOMAIN ),
305
- 2 => __( 'Custom field updated.', WPRSS_TEXT_DOMAIN ),
306
- 3 => __( 'Custom field deleted.', WPRSS_TEXT_DOMAIN ),
307
- 4 => __( 'Feed source updated.', WPRSS_TEXT_DOMAIN ),
308
- 5 => '',
309
- 6 => __( 'Feed source saved.', WPRSS_TEXT_DOMAIN ),
310
- 7 => __( 'Feed source saved.', WPRSS_TEXT_DOMAIN ),
311
- 8 => __( 'Feed source submitted.', WPRSS_TEXT_DOMAIN ),
312
- 9 => '',
313
- 10 => __( 'Feed source updated.', WPRSS_TEXT_DOMAIN )
314
- );
315
-
316
- return apply_filters( 'wprss_feed_updated_messages', $messages );
317
- }
318
-
319
-
320
- add_filter( 'post_row_actions', 'wprss_remove_row_actions', 10, 2 );
321
- /**
322
- * Remove actions row for imported feed items, we don't want them to be editable or viewable
323
- *
324
- * @since 2.0
325
- */
326
- function wprss_remove_row_actions( $actions, $post )
327
- {
328
-
329
- $page = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
330
- if ( get_post_type($post) === 'wprss_feed_item' ) {
331
- unset( $actions[ 'edit' ] );
332
- unset( $actions[ 'view' ] );
333
- //unset( $actions[ 'trash' ] );
334
- unset( $actions[ 'inline hide-if-no-js' ] );
335
- }
336
- elseif ( get_post_type($post) === 'wprss_feed' ) {
337
- $actions = array_reverse( $actions );
338
- $actions['id'] = '<span class="wprss-row-id">' . sprintf( __( 'ID: %1$s', WPRSS_TEXT_DOMAIN ), $post->ID ) . '</span>';
339
- $actions = array_reverse( $actions );
340
-
341
- unset( $actions[ 'view'] );
342
- unset( $actions[ 'inline hide-if-no-js'] );
343
- if ( get_post_status( $post->ID ) !== 'trash' ) {
344
- $trash = $actions['trash'];
345
- unset( $actions['trash'] );
346
-
347
- $view_items_link = apply_filters(
348
- 'wprss_view_feed_items_row_action_link',
349
- admin_url( 'edit.php?post_type=wprss_feed_item&wprss_feed=' . $post->ID ),
350
- $post->ID
351
- );
352
- $view_items_text = apply_filters( 'wprss_view_feed_items_row_action_text', __( 'View Items', WPRSS_TEXT_DOMAIN ) );
353
- $actions['view-items'] = '<a href="' . $view_items_link . '">' . $view_items_text . '</a>';
354
-
355
- $fetch_items_row_action_text = apply_filters( 'wprss_fetch_items_row_action_text', __( 'Fetch Items', WPRSS_TEXT_DOMAIN ) );
356
- $actions[ 'fetch' ] = '<a href="javascript:;" class="wprss_ajax_action" pid="'. $post->ID .'" purl="'.home_url().'/wp-admin/admin-ajax.php">' . $fetch_items_row_action_text . '</a>';
357
-
358
- $purge_feeds_row_action_text = apply_filters( 'wprss_purge_feeds_row_action_text', __( 'Delete Items', WPRSS_TEXT_DOMAIN ) );
359
- $purge_feeds_row_action_title = apply_filters( 'wprss_purge_feeds_row_action_title', __( 'Delete feed items imported by this feed source', WPRSS_TEXT_DOMAIN ) );
360
- $actions['purge-posts'] = "<a href='".admin_url("edit.php?post_type=wprss_feed&purge-feed-items=" . $post->ID . $page ) . "' title='" . $purge_feeds_row_action_title . "' >" . __( $purge_feeds_row_action_text, WPRSS_TEXT_DOMAIN ) . "</a>";
361
-
362
- $actions['trash'] = $trash;
363
- }
364
- }
365
- return apply_filters( 'wprss_remove_row_actions', $actions );
366
- }
367
-
368
-
369
- add_action( 'admin_init', 'check_delete_for_feed_source' );
370
- /**
371
- * Checks the GET data for the delete per feed source action request
372
- *
373
- * @since 3.5
374
- */
375
- function check_delete_for_feed_source( $source_id = NULL ) {
376
- if ( ! current_user_can( 'delete_feeds' ) ) return;
377
- // then we need to check the GET data for the request
378
- if ( isset( $_GET['purge-feed-items'] ) ) {
379
- $source_id = $_GET['purge-feed-items'];
380
- // Schedule a job that runs this function with the source id parameter
381
- wp_schedule_single_event( time(), 'wprss_delete_feed_items_from_source_hook', array( $source_id ) );
382
- // Set a transient
383
- set_transient( 'wprss_delete_posts_by_source_notif', 'true', 30 );
384
- // Mark feed as deleting its items
385
- update_post_meta( $source_id, 'wprss_feed_is_deleting_items', time() );
386
- // check pagination
387
- $page = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
388
- // Refresh the page without the GET parameter
389
- header( 'Location: ' . admin_url( 'edit.php?post_type=wprss_feed' . $page ) );
390
- exit();
391
- } else {
392
- // Get the notification transient
393
- $transient = get_transient( 'wprss_delete_posts_by_source_notif' );
394
- // If the transient is set and is set to 'true'
395
- if ( $transient !== FALSE && $transient === 'true' ) {
396
- // delete it
397
- delete_transient( 'wprss_delete_posts_by_source_notif' );
398
- // Add an action to show the notification
399
- add_action( 'all_admin_notices', 'wprss_notify_about_deleting_source_feed_items' );
400
- }
401
- }
402
- }
403
-
404
-
405
-
406
- add_action( 'wprss_delete_feed_items_from_source_hook', 'wprss_delete_feed_items_of_feed_source', 10 , 1 );
407
- /**
408
- * Deletes the feed items of the feed source identified by the given ID.
409
- *
410
- * @param $source_id The ID of the feed source
411
- * @since 3.5
412
- */
413
- function wprss_delete_feed_items_of_feed_source( $source_id ) {
414
- $force_delete = apply_filters( 'wprss_force_delete_when_by_source', TRUE );
415
- // WPML fix: removes the current language from the query WHERE and JOIN clauses
416
- global $sitepress;
417
- if ( $sitepress !== NULL ) {
418
- remove_filter( 'posts_join', array( $sitepress,'posts_join_filter') );
419
- remove_filter( 'posts_where', array( $sitepress,'posts_where_filter') );
420
- }
421
- // Run the query
422
- $query = new WP_Query(
423
- array(
424
- 'meta_key' => 'wprss_feed_id',
425
- 'meta_value' => $source_id,
426
- 'post_type' => 'wprss_feed_item',
427
- 'post_status' => 'any',
428
- 'posts_per_page' => -1
429
- )
430
- );
431
- $query = apply_filters( 'wprss_delete_per_source_query', $query, $source_id );
432
- // Delete the results of the query
433
- while( $query->have_posts() ) {
434
- $query->the_post();
435
- wp_delete_post( get_the_ID(), $force_delete );
436
- }
437
- }
438
-
439
-
440
- /**
441
- * Shows a notification that tells the user that feed items for a particular source are being deleted
442
- *
443
- * @since 3.5
444
- */
445
- function wprss_notify_about_deleting_source_feed_items() {
446
- $message = __( apply_filters( 'wprss_notify_about_deleting_source_feed_items_message', 'The feed items for this feed source are being deleted in the background.' ), WPRSS_TEXT_DOMAIN );
447
- echo '<div class="updated"><p>' . $message . '</p></div>';
448
- }
449
-
450
-
451
- add_action( 'wp_ajax_wprss_fetch_feeds_row_action', 'wprss_fetch_feeds_action_hook' );
452
- /**
453
- * The AJAX function for the 'Fetch Feed Items' row action on the
454
- * 'All Feed Sources' page.
455
- *
456
- * @since 3.3
457
- */
458
- function wprss_fetch_feeds_action_hook() {
459
- if ( isset( $_POST['id'] ) && !empty( $_POST['id'] ) ) {
460
- if ( ! current_user_can( 'edit_feed_sources' ) ) die();
461
- $id = $_POST['id'];
462
- update_post_meta( $id, 'wprss_force_next_fetch', '1' );
463
-
464
- // Prepare the schedule args
465
- $schedule_args = array( strval( $id ) );
466
-
467
- // Get the current schedule - do nothing if not scheduled
468
- $next_scheduled = wp_next_scheduled( 'wprss_fetch_single_feed_hook', $schedule_args );
469
- if ( $next_scheduled !== FALSE ) {
470
- // If scheduled, unschedule it
471
- wp_unschedule_event( $next_scheduled, 'wprss_fetch_single_feed_hook', $schedule_args );
472
-
473
- // Get the interval option for the feed source
474
- $interval = get_post_meta( $id, 'wprss_update_interval', TRUE );
475
- // if the feed source uses its own interval
476
- if ( $interval !== '' && $interval !== wprss_get_default_feed_source_update_interval() ) {
477
- // Add meta in feed source. This is used to notify the source that it needs to reschedule it
478
- update_post_meta( $id, 'wprss_reschedule_event', $next_scheduled );
479
- }
480
- }
481
-
482
- // Schedule the event for 5 seconds from now
483
- wp_schedule_single_event( time() + 1, 'wprss_fetch_single_feed_hook', $schedule_args );
484
- wprss_flag_feed_as_updating( $id );
485
- die();
486
- }
487
- }
488
-
489
-
490
- add_filter( 'bulk_actions-edit-wprss_feed_item', 'wprss_custom_feed_item_bulk_actions' );
491
- /**
492
- * Remove bulk action link to edit imported feed items
493
- *
494
- * @since 2.0
495
- */
496
- function wprss_custom_feed_item_bulk_actions( $actions ){
497
- unset( $actions[ 'edit' ] );
498
- return apply_filters( 'wprss_custom_feed_item_bulk_actions', $actions );
499
- }
500
-
501
-
502
- add_action( 'admin_footer-edit.php', 'wprss_remove_a_from_feed_title' );
503
- /**
504
- * Remove hyperlink from imported feed titles in list posts screen
505
- *
506
- * @since 2.0
507
- */
508
- function wprss_remove_a_from_feed_title() {
509
- if ( 'edit-wprss_feed_item' !== get_current_screen()->id )
510
- return;
511
- ?>
512
-
513
- <script type="text/javascript">
514
- jQuery('table.wp-list-table a.row-title').contents().unwrap();
515
- </script>
516
- <?php
517
- }
518
-
519
-
520
- add_filter( 'gettext', 'wprss_change_publish_button_text', 10, 2 );
521
- /**
522
- * Modify 'Publish' button text when adding a new feed source
523
- *
524
- * @since 2.0
525
- */
526
- function wprss_change_publish_button_text( $translation, $text ) {
527
- if ( 'wprss_feed' == get_post_type()) {
528
- if ( $text == 'Publish' )
529
- return __( 'Publish Feed', WPRSS_TEXT_DOMAIN );
530
- }
531
- return apply_filters( 'wprss_change_publish_button_text', $translation );
532
- }
533
-
534
-
535
-
536
- add_action( 'wp_before_admin_bar_render', 'wprss_modify_admin_bar' );
537
- /**
538
- * Removes the old "View Source" menu item from the admin bar and adds a new
539
- * "View items" menu bar item, that opens a new tab, showing the items imported
540
- * from that feed source.
541
- *
542
- * Only shown on the wprss_feed edit page.
543
- *
544
- * @since 4.2
545
- */
546
- function wprss_modify_admin_bar() {
547
- global $wp_admin_bar;
548
- if ( !is_admin() ) return;
549
- $screen = get_current_screen();
550
- // Check if we are in the wprss_feed edit page
551
- if ( $screen->base == 'post' && $screen->post_type == 'wprss_feed' && !empty( $_GET['action'] ) && $_GET['action'] == 'edit' ) {
552
- // Remove the old 'View Source' menu item
553
- $wp_admin_bar->remove_node( 'view' );
554
-
555
- // Prepare the view items link and text
556
- $view_items_link = apply_filters(
557
- 'wprss_view_feed_items_row_action_link',
558
- admin_url( 'edit.php?post_type=wprss_feed_item&wprss_feed=' . get_the_ID() ),
559
- get_the_ID()
560
- );
561
- $view_items_text = apply_filters( 'wprss_view_feed_items_row_action_text', 'View Items' );
562
-
563
- // Prepare the link target
564
- $link_target = 'wprss-view-items-' . get_the_ID();
565
-
566
- // Add the new menu item
567
- $wp_admin_bar->add_node( array(
568
- 'href' => $view_items_link,
569
- 'id' => 'view',
570
- 'title' => __( $view_items_text, WPRSS_TEXT_DOMAIN ),
571
- 'meta' => array(
572
- 'target' => $link_target
573
- )
574
- ));
575
- }
576
- }
577
-
578
-
579
-
580
-
581
- if ( is_admin() ){
582
- add_filter('pre_get_posts', 'wprss_view_feed_items_query');
583
- /**
584
- * Alters the main query in the WordPress admin, when the wprss_feed GET parameter is set.
585
- * The queried items are then filtered down to the items imported by the feed source with
586
- * the ID given in the wprss_feed GET parameter.
587
- *
588
- * @since 4.2
589
- */
590
- function wprss_view_feed_items_query( $query ) {
591
- if ( is_admin() && $query->is_main_query() && !empty($_GET['wprss_feed']) ) {
592
- // Get the ID from the GET param
593
- $id = $_GET['wprss_feed'];
594
- // Get the existing meta query
595
- $mq = $query->get('meta_query');
596
- // If the meta query is not yet set
597
- if ( !is_array($mq) ) {
598
- // initialize it
599
- $mq = array(
600
- 'relation' => 'AND',
601
- );
602
- }
603
- // Add the custom meta query
604
- $mq[] = apply_filters(
605
- 'wprss_view_feed_items_meta_query',
606
- array(
607
- 'key' => 'wprss_feed_id',
608
- 'value' => $id,
609
- 'compare' => '='
610
- ),
611
- $id
612
- );
613
- // Set the new meta query
614
- $query->set('meta_query', $mq);
615
- }
616
- // Return the query
617
- return $query;
618
- }
619
- }
1
+ <?php
2
+ /**
3
+ * Functions for the admin section, columns and row actions
4
+ *
5
+ * @package WP RSS Aggregator
6
+ */
7
+
8
+
9
+ add_filter( 'manage_wprss_feed_posts_columns', 'wprss_set_feed_custom_columns', 20, 1 );
10
+ /**
11
+ * Set up the custom columns for the wprss_feed list
12
+ *
13
+ * @since 2.0
14
+ */
15
+ function wprss_set_feed_custom_columns( $columns ) {
16
+
17
+ $columns = array(
18
+ 'cb' => '<input type="checkbox" />',
19
+ 'errors' => '',
20
+ 'title' => __( 'Name', WPRSS_TEXT_DOMAIN ),
21
+ );
22
+
23
+ $columns = apply_filters( 'wprss_set_feed_custom_columns', $columns );
24
+
25
+ // Columns to add when feed is not trashed
26
+ if ( !isset( $_GET['post_status'] ) || $_GET['post_status'] !== 'trash' ) {
27
+ $columns['state'] = __( 'State', WPRSS_TEXT_DOMAIN );
28
+ $columns['updates'] = __( 'Updates', WPRSS_TEXT_DOMAIN );
29
+ $columns['feed-count'] = __( apply_filters( 'wprss_feed_items_count_column', 'Imported items' ), WPRSS_TEXT_DOMAIN );
30
+ }
31
+
32
+ return $columns;
33
+ }
34
+
35
+
36
+ add_action( "manage_wprss_feed_posts_custom_column", "wprss_show_custom_columns", 10, 2 );
37
+ /**
38
+ * Show up the custom columns for the wprss_feed list
39
+ *
40
+ * @since 2.0
41
+ */
42
+ function wprss_show_custom_columns( $column, $post_id ) {
43
+
44
+ switch ( $column ) {
45
+ case 'errors':
46
+ $errors = get_post_meta( $post_id, 'wprss_error_last_import', true );
47
+ $showClass = ( $errors !== '' )? 'wprss-show' : '';
48
+ $default_msg = __( "This feed source experienced an error during the last feed fetch or validation check. Re-check the feed source URL or check the Error Log in the Debugging page for more details.", WPRSS_TEXT_DOMAIN );
49
+ $msg = strlen( $errors ) > 0 ? $errors : $default_msg;
50
+ echo "<i title=\"$msg\" class=\"fa fa-warning fa-fw wprss-feed-error-symbol $showClass\"></i>";
51
+ break;
52
+ case 'state':
53
+ $active = wprss_is_feed_source_active( $post_id );
54
+ $text = ( $active )? 'Active' : 'Paused';
55
+ $button = ( $active )? 'Pause this feed source' : 'Activate this feed source';
56
+ $icon = ( $active )? 'pause' : 'play';
57
+ $value = ( $active )? 'paused' : 'active';
58
+ $indicator = ( $active )? 'green' : 'grey';
59
+
60
+ ?>
61
+ <p>
62
+ <span class="wprss-indicator-<?php echo $indicator; ?>" title="<?php _e( $text, WPRSS_TEXT_DOMAIN ) ?>">
63
+ <i class="fa fa-circle"></i>
64
+ </span>
65
+ <input type="hidden" name="wprss-redirect" value="1" />
66
+ <button type="submit" class='button-secondary' title="<?php _e( $button, WPRSS_TEXT_DOMAIN ) ?>" name="wprss-feed-id" value="<?php echo $post_id; ?>">
67
+ <i class='fa fa-<?php echo $icon; ?>'></i>
68
+ </button>
69
+ </p>
70
+ <?php
71
+
72
+ break;
73
+
74
+ case 'updates':
75
+ // Get the update interval
76
+ $update_interval = get_post_meta( $post_id, 'wprss_update_interval', TRUE );
77
+ // Get the last updated and next update data
78
+ $last_update = get_post_meta( $post_id, 'wprss_last_update', TRUE );
79
+ $last_update_items = get_post_meta( $post_id, 'wprss_last_update_items', TRUE );
80
+ $next_update = wprss_get_next_feed_source_update( $post_id );
81
+
82
+ // If using the global interval, get the timestamp of the next global update
83
+ if ( $update_interval === wprss_get_default_feed_source_update_interval() || $update_interval === '' ) {
84
+ $next_update = wp_next_scheduled( 'wprss_fetch_all_feeds_hook', array() );
85
+ }
86
+
87
+ // Update the meta field
88
+ if ( wprss_is_feed_source_active( $post_id ) ) {
89
+ $next_update_text = $next_update === FALSE ? __( 'None', WPRSS_TEXT_DOMAIN ) : human_time_diff( $next_update, time() );
90
+ } else {
91
+ $next_update_text = __( 'Paused', WPRSS_TEXT_DOMAIN );
92
+ }
93
+ update_post_meta( $post_id, 'wprss_next_update', $next_update_text );
94
+
95
+ ?>
96
+
97
+ <p>
98
+ <?php _e( 'Next update:', WPRSS_TEXT_DOMAIN ) ?>
99
+ <code class="next-update">
100
+ <?php echo $next_update_text; ?>
101
+ </code>
102
+ </p>
103
+
104
+ <?php if ( $last_update !== '' ): ?>
105
+ <p class="last-update-container">
106
+ <?php _e( 'Last updated:', WPRSS_TEXT_DOMAIN ) ?>
107
+ <code class="last-update"><?php echo sprintf( __( '%1$s ago', WPRSS_TEXT_DOMAIN ), human_time_diff( $last_update, time() ) ) ?></code>
108
+ <?php if ( $last_update_items !== '' ): ?>
109
+ <span class="last-update-imported-container"><br/><?php echo sprintf( __( 'Last update imported: <code class="last-update-imported">%1$d</code> items', WPRSS_TEXT_DOMAIN ), $last_update_items ) ?></span>
110
+ <?php endif; ?>
111
+ </p>
112
+ <?php endif;
113
+
114
+ break;
115
+
116
+ case 'feed-count':
117
+ $items = wprss_get_feed_items_for_source( $post_id );
118
+ $seconds_for_next_update = wprss_get_next_feed_source_update( $post_id ) - time();
119
+ $showClass = ( ( $seconds_for_next_update < 10 && $seconds_for_next_update > 0 ) || wprss_is_feed_source_deleting( $post_id ) )? 'wprss-show' : '';
120
+
121
+ ?>
122
+ <p>
123
+ <span class="items-imported"><?php echo $items->post_count ?></span>
124
+ <i class="fa fa-fw fa-refresh fa-spin wprss-updating-feed-icon <?php echo $showClass ?>" title="<?php _e( 'Updating feed source', WPRSS_TEXT_DOMAIN ) ?>"></i>
125
+ </p>
126
+ <?php
127
+
128
+ // Set meta field for items imported
129
+ update_post_meta( $post_id, 'wprss_items_imported', $items->post_count );
130
+
131
+ break;
132
+ }
133
+ }
134
+
135
+
136
+ add_filter( "manage_edit-wprss_feed_sortable_columns", "wprss_feed_sortable_columns" );
137
+ /**
138
+ * Make the custom columns sortable for wprss_feed post type
139
+ *
140
+ * @since 2.0
141
+ */
142
+ function wprss_feed_sortable_columns() {
143
+ $sortable_columns = array(
144
+ // meta column id => sortby value used in query
145
+ 'title' => 'title',
146
+ 'updates' => 'updates',
147
+ 'state' => 'state',
148
+ 'feed-count' => 'feed-count'
149
+ );
150
+ return apply_filters( 'wprss_feed_sortable_columns', $sortable_columns );
151
+ }
152
+
153
+
154
+ add_action( 'pre_get_posts', 'wprss_feed_source_order' );
155
+ /**
156
+ * Change order of feed sources to alphabetical ascending according to feed name
157
+ *
158
+ * @since 2.2
159
+ */
160
+ function wprss_feed_source_order( $query ) {
161
+ // Check if the query is being processed in WP Admin, is the main query, and is targetted
162
+ // for the wprss_feed CPT. If not, stop
163
+ if ( !is_admin() || !$query->is_main_query() || $query->get('post_type') !== 'wprss_feed' ) {
164
+ return;
165
+ }
166
+ // Check if the orderby query variable is set
167
+ if ( !( $orderby = $query->get( 'orderby' ) ) ) return;
168
+
169
+ // We will be sorting using the meta value (unless sorting by title)
170
+ $query->set('orderby', 'meta_value' );
171
+ // Get the current order
172
+ $order = strtoupper( $query->get( 'order' ) );
173
+ // Check if it is valid
174
+ $got_order = $order === 'ASC' || $order === 'DESC';
175
+
176
+ // Check what we are sorting by
177
+ switch ( $orderby ) {
178
+ case 'title':
179
+ $query->set( 'orderby', 'title' );
180
+ break;
181
+ case 'state':
182
+ $query->set('meta_key', 'wprss_state');
183
+ break;
184
+ case 'updates':
185
+ $query->set('meta_key', 'wprss_next_update');
186
+ $query->set('orderby', 'meta_value' );
187
+ if ( !$got_order ) $query->set( 'order', 'ASC' );
188
+ break;
189
+ case 'feed-count':
190
+ $query->set('meta_key', 'wprss_items_imported');
191
+ $query->set('orderby', 'meta_value_num' );
192
+ if ( !$got_order ) $query->set( 'order', 'DESC' );
193
+ break;
194
+ }
195
+
196
+ if ( !$got_order ){
197
+ $query->set( 'order', 'ASC' );
198
+ }
199
+ }
200
+
201
+
202
+ add_filter( 'manage_wprss_feed_item_posts_columns', 'wprss_set_feed_item_custom_columns', 20, 1 );
203
+ /**
204
+ * Set up the custom columns for the wprss_feed source list
205
+ *
206
+ * @since 2.0
207
+ */
208
+ function wprss_set_feed_item_custom_columns( $columns ) {
209
+
210
+ $columns = array (
211
+ 'cb' => '<input type="checkbox" />',
212
+ 'title' => __( 'Name', WPRSS_TEXT_DOMAIN ),
213
+ 'permalink' => __( 'Permalink', WPRSS_TEXT_DOMAIN ),
214
+ 'publishdate' => __( 'Date published', WPRSS_TEXT_DOMAIN ),
215
+ 'source' => __( 'Source', WPRSS_TEXT_DOMAIN )
216
+ );
217
+ return apply_filters( 'wprss_set_feed_item_custom_columns', $columns );
218
+ }
219
+
220
+
221
+ add_action( "manage_wprss_feed_item_posts_custom_column", "wprss_show_feed_item_custom_columns", 10, 2 );
222
+ /**
223
+ * Show up the custom columns for the wprss_feed list
224
+ *
225
+ * @since 2.0
226
+ */
227
+ function wprss_show_feed_item_custom_columns( $column, $post_id ) {
228
+
229
+ switch ( $column ) {
230
+ case "permalink":
231
+ $url = get_post_meta( $post_id, 'wprss_item_permalink', true);
232
+ echo '<a href="' . $url . '">' . $url. '</a>';
233
+ break;
234
+
235
+ case "publishdate":
236
+ $item_date = get_the_time( 'U', get_the_ID() );
237
+ $item_date = ( $item_date === '' )? date('U') : $item_date;
238
+ $publishdate = date( 'Y-m-d H:i:s', $item_date ) ;
239
+ echo $publishdate;
240
+ break;
241
+
242
+ case "source":
243
+ $query = new WP_Query();
244
+ $source = '<a href="' . get_edit_post_link( get_post_meta( $post_id, 'wprss_feed_id', true ) ) . '">' . get_the_title( get_post_meta( $post_id, 'wprss_feed_id', true ) ) . '</a>';
245
+ echo $source;
246
+ break;
247
+ }
248
+ }
249
+
250
+
251
+ add_filter( "manage_edit-wprss_feed_item_sortable_columns", "wprss_feed_item_sortable_columns" );
252
+ /**
253
+ * Make the custom columns sortable
254
+ *
255
+ * @since 2.0
256
+ */
257
+ function wprss_feed_item_sortable_columns() {
258
+ $sortable_columns = array(
259
+ // meta column id => sortby value used in query
260
+ 'publishdate' => 'publishdate',
261
+ 'source' => 'source'
262
+ );
263
+ return apply_filters( 'wprss_feed_item_sortable_columns', $sortable_columns );
264
+ }
265
+
266
+
267
+ add_action( 'pre_get_posts', 'wprss_feed_item_orderby' );
268
+ /**
269
+ * Change ordering of posts on wprss_feed_item screen
270
+ *
271
+ * @since 2.0
272
+ */
273
+ function wprss_feed_item_orderby( $query ) {
274
+ if( ! is_admin() )
275
+ return;
276
+
277
+ $post_type = $query->get('post_type');
278
+
279
+ // If we're on the feed listing admin page
280
+ if ( $post_type == 'wprss_feed_item') {
281
+ // Set general orderby to date the feed item was published
282
+ $query->set('orderby','publishdate');
283
+ // If user clicks on the reorder link, implement reordering
284
+ $orderby = $query->get( 'orderby');
285
+ if( 'publishdate' == $orderby ) {
286
+ $query->set( 'order', 'DESC' );
287
+ $query->set( 'orderby', 'date' );
288
+ }
289
+ }
290
+ }
291
+
292
+
293
+ add_filter( 'post_updated_messages', 'wprss_feed_updated_messages' );
294
+ /**
295
+ * Change default notification message when new feed is added or updated
296
+ *
297
+ * @since 2.0
298
+ */
299
+ function wprss_feed_updated_messages( $messages ) {
300
+ global $post, $post_ID;
301
+
302
+ $messages[ 'wprss_feed' ] = array(
303
+ 0 => '', // Unused. Messages start at index 1.
304
+ 1 => __( 'Feed source updated. ', WPRSS_TEXT_DOMAIN ),
305
+ 2 => __( 'Custom field updated.', WPRSS_TEXT_DOMAIN ),
306
+ 3 => __( 'Custom field deleted.', WPRSS_TEXT_DOMAIN ),
307
+ 4 => __( 'Feed source updated.', WPRSS_TEXT_DOMAIN ),
308
+ 5 => '',
309
+ 6 => __( 'Feed source saved.', WPRSS_TEXT_DOMAIN ),
310
+ 7 => __( 'Feed source saved.', WPRSS_TEXT_DOMAIN ),
311
+ 8 => __( 'Feed source submitted.', WPRSS_TEXT_DOMAIN ),
312
+ 9 => '',
313
+ 10 => __( 'Feed source updated.', WPRSS_TEXT_DOMAIN )
314
+ );
315
+
316
+ return apply_filters( 'wprss_feed_updated_messages', $messages );
317
+ }
318
+
319
+
320
+ add_filter( 'post_row_actions', 'wprss_remove_row_actions', 10, 2 );
321
+ /**
322
+ * Remove actions row for imported feed items, we don't want them to be editable or viewable
323
+ *
324
+ * @since 2.0
325
+ */
326
+ function wprss_remove_row_actions( $actions, $post )
327
+ {
328
+
329
+ $page = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
330
+ if ( get_post_type($post) === 'wprss_feed_item' ) {
331
+ unset( $actions[ 'edit' ] );
332
+ unset( $actions[ 'view' ] );
333
+ //unset( $actions[ 'trash' ] );
334
+ unset( $actions[ 'inline hide-if-no-js' ] );
335
+ }
336
+ elseif ( get_post_type($post) === 'wprss_feed' ) {
337
+ $actions = array_reverse( $actions );
338
+ $actions['id'] = '<span class="wprss-row-id">' . sprintf( __( 'ID: %1$s', WPRSS_TEXT_DOMAIN ), $post->ID ) . '</span>';
339
+ $actions = array_reverse( $actions );
340
+
341
+ unset( $actions[ 'view'] );
342
+ unset( $actions[ 'inline hide-if-no-js'] );
343
+ if ( get_post_status( $post->ID ) !== 'trash' ) {
344
+ $trash = $actions['trash'];
345
+ unset( $actions['trash'] );
346
+
347
+ $view_items_link = apply_filters(
348
+ 'wprss_view_feed_items_row_action_link',
349
+ admin_url( 'edit.php?post_type=wprss_feed_item&wprss_feed=' . $post->ID ),
350
+ $post->ID
351
+ );
352
+ $view_items_text = apply_filters( 'wprss_view_feed_items_row_action_text', __( 'View Items', WPRSS_TEXT_DOMAIN ) );
353
+ $actions['view-items'] = '<a href="' . $view_items_link . '">' . $view_items_text . '</a>';
354
+
355
+ $fetch_items_row_action_text = apply_filters( 'wprss_fetch_items_row_action_text', __( 'Fetch Items', WPRSS_TEXT_DOMAIN ) );
356
+ $actions[ 'fetch' ] = '<a href="javascript:;" class="wprss_ajax_action" pid="'. $post->ID .'" purl="'.home_url().'/wp-admin/admin-ajax.php">' . $fetch_items_row_action_text . '</a>';
357
+
358
+ $purge_feeds_row_action_text = apply_filters( 'wprss_purge_feeds_row_action_text', __( 'Delete Items', WPRSS_TEXT_DOMAIN ) );
359
+ $purge_feeds_row_action_title = apply_filters( 'wprss_purge_feeds_row_action_title', __( 'Delete feed items imported by this feed source', WPRSS_TEXT_DOMAIN ) );
360
+ $actions['purge-posts'] = "<a href='".admin_url("edit.php?post_type=wprss_feed&purge-feed-items=" . $post->ID . $page ) . "' title='" . $purge_feeds_row_action_title . "' >" . __( $purge_feeds_row_action_text, WPRSS_TEXT_DOMAIN ) . "</a>";
361
+
362
+ $actions['trash'] = $trash;
363
+ }
364
+ }
365
+ return apply_filters( 'wprss_remove_row_actions', $actions );
366
+ }
367
+
368
+
369
+ add_action( 'admin_init', 'check_delete_for_feed_source' );
370
+ /**
371
+ * Checks the GET data for the delete per feed source action request
372
+ *
373
+ * @since 3.5
374
+ */
375
+ function check_delete_for_feed_source( $source_id = NULL ) {
376
+ if ( ! current_user_can( 'delete_feeds' ) ) return;
377
+ // then we need to check the GET data for the request
378
+ if ( isset( $_GET['purge-feed-items'] ) ) {
379
+ $source_id = $_GET['purge-feed-items'];
380
+ // Schedule a job that runs this function with the source id parameter
381
+ wp_schedule_single_event( time(), 'wprss_delete_feed_items_from_source_hook', array( $source_id ) );
382
+ // Set a transient
383
+ set_transient( 'wprss_delete_posts_by_source_notif', 'true', 30 );
384
+ // Mark feed as deleting its items
385
+ update_post_meta( $source_id, 'wprss_feed_is_deleting_items', time() );
386
+ // check pagination
387
+ $page = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
388
+ // Refresh the page without the GET parameter
389
+ header( 'Location: ' . admin_url( 'edit.php?post_type=wprss_feed' . $page ) );
390
+ exit();
391
+ } else {
392
+ // Get the notification transient
393
+ $transient = get_transient( 'wprss_delete_posts_by_source_notif' );
394
+ // If the transient is set and is set to 'true'
395
+ if ( $transient !== FALSE && $transient === 'true' ) {
396
+ // delete it
397
+ delete_transient( 'wprss_delete_posts_by_source_notif' );
398
+ // Add an action to show the notification
399
+ add_action( 'all_admin_notices', 'wprss_notify_about_deleting_source_feed_items' );
400
+ }
401
+ }
402
+ }
403
+
404
+
405
+
406
+ add_action( 'wprss_delete_feed_items_from_source_hook', 'wprss_delete_feed_items_of_feed_source', 10 , 1 );
407
+ /**
408
+ * Deletes the feed items of the feed source identified by the given ID.
409
+ *
410
+ * @param $source_id The ID of the feed source
411
+ * @since 3.5
412
+ */
413
+ function wprss_delete_feed_items_of_feed_source( $source_id ) {
414
+ $force_delete = apply_filters( 'wprss_force_delete_when_by_source', TRUE );
415
+ // WPML fix: removes the current language from the query WHERE and JOIN clauses
416
+ global $sitepress;
417
+ if ( $sitepress !== NULL ) {
418
+ remove_filter( 'posts_join', array( $sitepress,'posts_join_filter') );
419
+ remove_filter( 'posts_where', array( $sitepress,'posts_where_filter') );
420
+ }
421
+ // Run the query
422
+ $query = new WP_Query(
423
+ array(
424
+ 'meta_key' => 'wprss_feed_id',
425
+ 'meta_value' => $source_id,
426
+ 'post_type' => 'wprss_feed_item',
427
+ 'post_status' => 'any',
428
+ 'posts_per_page' => -1
429
+ )
430
+ );
431
+ $query = apply_filters( 'wprss_delete_per_source_query', $query, $source_id );
432
+ // Delete the results of the query
433
+ while( $query->have_posts() ) {
434
+ $query->the_post();
435
+ wp_delete_post( get_the_ID(), $force_delete );
436
+ }
437
+ }
438
+
439
+
440
+ /**
441
+ * Shows a notification that tells the user that feed items for a particular source are being deleted
442
+ *
443
+ * @since 3.5
444
+ */
445
+ function wprss_notify_about_deleting_source_feed_items() {
446
+ $message = __( apply_filters( 'wprss_notify_about_deleting_source_feed_items_message', 'The feed items for this feed source are being deleted in the background.' ), WPRSS_TEXT_DOMAIN );
447
+ echo '<div class="updated"><p>' . $message . '</p></div>';
448
+ }
449
+
450
+
451
+ add_action( 'wp_ajax_wprss_fetch_feeds_row_action', 'wprss_fetch_feeds_action_hook' );
452
+ /**
453
+ * The AJAX function for the 'Fetch Feed Items' row action on the
454
+ * 'All Feed Sources' page.
455
+ *
456
+ * @since 3.3
457
+ */
458
+ function wprss_fetch_feeds_action_hook() {
459
+ if ( isset( $_POST['id'] ) && !empty( $_POST['id'] ) ) {
460
+ if ( ! current_user_can( 'edit_feed_sources' ) ) die();
461
+ $id = $_POST['id'];
462
+ update_post_meta( $id, 'wprss_force_next_fetch', '1' );
463
+
464
+ // Prepare the schedule args
465
+ $schedule_args = array( strval( $id ) );
466
+
467
+ // Get the current schedule - do nothing if not scheduled
468
+ $next_scheduled = wp_next_scheduled( 'wprss_fetch_single_feed_hook', $schedule_args );
469
+ if ( $next_scheduled !== FALSE ) {
470
+ // If scheduled, unschedule it
471
+ wp_unschedule_event( $next_scheduled, 'wprss_fetch_single_feed_hook', $schedule_args );
472
+
473
+ // Get the interval option for the feed source
474
+ $interval = get_post_meta( $id, 'wprss_update_interval', TRUE );
475
+ // if the feed source uses its own interval
476
+ if ( $interval !== '' && $interval !== wprss_get_default_feed_source_update_interval() ) {
477
+ // Add meta in feed source. This is used to notify the source that it needs to reschedule it
478
+ update_post_meta( $id, 'wprss_reschedule_event', $next_scheduled );
479
+ }
480
+ }
481
+
482
+ // Schedule the event for 5 seconds from now
483
+ wp_schedule_single_event( time() + 1, 'wprss_fetch_single_feed_hook', $schedule_args );
484
+ wprss_flag_feed_as_updating( $id );
485
+ die();
486
+ }
487
+ }
488
+
489
+
490
+ add_filter( 'bulk_actions-edit-wprss_feed_item', 'wprss_custom_feed_item_bulk_actions' );
491
+ /**
492
+ * Allow filtering bulk actions for feed items
493
+ *
494
+ * @since 2.0
495
+ */
496
+ function wprss_custom_feed_item_bulk_actions( $actions ){
497
+ return apply_filters( 'wprss_custom_feed_item_bulk_actions', $actions );
498
+ }
499
+
500
+
501
+ add_action( 'admin_footer-edit.php', 'wprss_remove_a_from_feed_title' );
502
+ /**
503
+ * Remove hyperlink from imported feed titles in list posts screen
504
+ *
505
+ * @since 2.0
506
+ */
507
+ function wprss_remove_a_from_feed_title() {
508
+ if ( 'edit-wprss_feed_item' !== get_current_screen()->id )
509
+ return;
510
+ ?>
511
+
512
+ <script type="text/javascript">
513
+ jQuery('table.wp-list-table a.row-title').contents().unwrap();
514
+ </script>
515
+ <?php
516
+ }
517
+
518
+
519
+ add_filter( 'gettext', 'wprss_change_publish_button_text', 10, 2 );
520
+ /**
521
+ * Modify 'Publish' button text when adding a new feed source
522
+ *
523
+ * @since 2.0
524
+ */
525
+ function wprss_change_publish_button_text( $translation, $text ) {
526
+ if ( 'wprss_feed' == get_post_type()) {
527
+ if ( $text == 'Publish' )
528
+ return __( 'Publish Feed', WPRSS_TEXT_DOMAIN );
529
+ }
530
+ return apply_filters( 'wprss_change_publish_button_text', $translation );
531
+ }
532
+
533
+
534
+
535
+ add_action( 'wp_before_admin_bar_render', 'wprss_modify_admin_bar' );
536
+ /**
537
+ * Removes the old "View Source" menu item from the admin bar and adds a new
538
+ * "View items" menu bar item, that opens a new tab, showing the items imported
539
+ * from that feed source.
540
+ *
541
+ * Only shown on the wprss_feed edit page.
542
+ *
543
+ * @since 4.2
544
+ */
545
+ function wprss_modify_admin_bar() {
546
+ global $wp_admin_bar;
547
+ if ( !is_admin() ) return;
548
+ $screen = get_current_screen();
549
+ // Check if we are in the wprss_feed edit page
550
+ if ( $screen->base == 'post' && $screen->post_type == 'wprss_feed' && !empty( $_GET['action'] ) && $_GET['action'] == 'edit' ) {
551
+ // Remove the old 'View Source' menu item
552
+ $wp_admin_bar->remove_node( 'view' );
553
+
554
+ // Prepare the view items link and text
555
+ $view_items_link = apply_filters(
556
+ 'wprss_view_feed_items_row_action_link',
557
+ admin_url( 'edit.php?post_type=wprss_feed_item&wprss_feed=' . get_the_ID() ),
558
+ get_the_ID()
559
+ );
560
+ $view_items_text = apply_filters( 'wprss_view_feed_items_row_action_text', 'View Items' );
561
+
562
+ // Prepare the link target
563
+ $link_target = 'wprss-view-items-' . get_the_ID();
564
+
565
+ // Add the new menu item
566
+ $wp_admin_bar->add_node( array(
567
+ 'href' => $view_items_link,
568
+ 'id' => 'view',
569
+ 'title' => __( $view_items_text, WPRSS_TEXT_DOMAIN ),
570
+ 'meta' => array(
571
+ 'target' => $link_target
572
+ )
573
+ ));
574
+ }
575
+ }
576
+
577
+
578
+
579
+
580
+ if ( is_admin() ){
581
+ add_filter('pre_get_posts', 'wprss_view_feed_items_query');
582
+ /**
583
+ * Alters the main query in the WordPress admin, when the wprss_feed GET parameter is set.
584
+ * The queried items are then filtered down to the items imported by the feed source with
585
+ * the ID given in the wprss_feed GET parameter.
586
+ *
587
+ * @since 4.2
588
+ */
589
+ function wprss_view_feed_items_query( $query ) {
590
+ if ( is_admin() && $query->is_main_query() && !empty($_GET['wprss_feed']) ) {
591
+ // Get the ID from the GET param
592
+ $id = $_GET['wprss_feed'];
593
+ // Get the existing meta query
594
+ $mq = $query->get('meta_query');
595
+ // If the meta query is not yet set
596
+ if ( !is_array($mq) ) {
597
+ // initialize it
598
+ $mq = array(
599
+ 'relation' => 'AND',
600
+ );
601
+ }
602
+ // Add the custom meta query
603
+ $mq[] = apply_filters(
604
+ 'wprss_view_feed_items_meta_query',
605
+ array(
606
+ 'key' => 'wprss_feed_id',
607
+ 'value' => $id,
608
+ 'compare' => '='
609
+ ),
610
+ $id
611
+ );
612
+ // Set the new meta query
613
+ $query->set('meta_query', $mq);
614
+ }
615
+ // Return the query
616
+ return $query;
617
+ }
618
+ }
 
includes/admin-editor.php CHANGED
@@ -1,147 +1,147 @@
1
- <?php
2
- /**
3
- * This file contains code related to the custom button added to Wordpress' TinyMCE editor.
4
- *
5
- * @since 3.5
6
- */
7
-
8
-
9
-
10
- add_action( 'admin_init', 'wprss_add_editor_button' );
11
- /**
12
- * Adds the WPRSS button to WordPress' editor
13
- *
14
- * @since 3.5
15
- */
16
- function wprss_add_editor_button() {
17
- if ( ! current_user_can( 'edit_posts' ) && !current_user_can( 'edit_pages' ) )
18
- return;
19
- if ( get_user_option( 'rich_editing' ) == 'true') {
20
- add_filter( 'mce_external_plugins', 'wprss_register_tinymce_plugin' );
21
- add_filter( 'mce_buttons', 'wprss_register_tinymce_button' );
22
- }
23
- }
24
-
25
-
26
- /**
27
- * Adds a separator and the wprss button to the buttons array.
28
- *
29
- * @since 3.5
30
- */
31
- function wprss_register_tinymce_button( $buttons ) {
32
- array_push( $buttons, "|", "wprss" );
33
- return $buttons;
34
- }
35
-
36
-
37
- /**
38
- * Adds the button action JS file to TinyMCE's plugin list
39
- *
40
- * @todo add filter to skip showing the editor button
41
- * @since 3.5
42
- */
43
- function wprss_register_tinymce_plugin($plugin_array) {
44
- // add filter here
45
- $plugin_array['wprss'] = WPRSS_JS . 'editor.js';
46
- return $plugin_array;
47
- }
48
-
49
-
50
- add_filter( 'tiny_mce_version', 'wprss_tinymce_version');
51
- /**
52
- * Intercepts TinyMCE's version check and increments its version by 3.
53
- *
54
- * This is a hack used to work around TinyMCE's caching, that might prevent the
55
- * new wprss button from appearing on the editor.
56
- *
57
- * @since 3.5
58
- */
59
- function wprss_tinymce_version($ver) {
60
- $ver += 3;
61
- return $ver;
62
- }
63
-
64
-
65
-
66
-
67
-
68
-
69
- add_action( 'wp_ajax_wprss_editor_dialog', 'wprss_return_dialog_contents' );
70
- /**
71
- *
72
- *
73
- */
74
- function wprss_return_dialog_contents() {
75
- $feed_sources = get_posts( array(
76
- 'post_type' => 'wprss_feed',
77
- 'post_status' => 'publish',
78
- 'posts_per_page' => -1,
79
- 'no_found_rows' => true
80
- ));
81
- $feed_sources_select = '<select id="wprss-dialog-feed-source-list" multiple>';
82
- $feed_sources_exclude_select = '<select id="wprss-dialog-exclude-list" multiple>';
83
- $feed_sources_both_select = '';
84
- foreach ( $feed_sources as $source ) {
85
- $feed_sources_both_select .= '<option value="' . $source->ID . '" >' . $source->post_title . '</option>';
86
- }
87
- $feed_sources_both_select .= '</select><p>' . __( 'Hold Ctrl or Mac Command key when clicking to select more than one feed source.' , WPRSS_TEXT_DOMAIN ) . '</p>';
88
-
89
- $feed_sources_select .= $feed_sources_both_select;
90
- $feed_sources_exclude_select .= $feed_sources_both_select;
91
-
92
- ?>
93
- <table cellspacing="20">
94
- <tbody>
95
-
96
- <tr>
97
- <td id="wprss-dialog-all-sources-label"><?php _e( 'Feed Sources', WPRSS_TEXT_DOMAIN ) ?></td>
98
- <td>
99
- <input id="wprss-dialog-all-sources" type="checkbox" checked> <label for="wprss-dialog-all-sources"><?php _e( 'All feed sources', WPRSS_TEXT_DOMAIN ) ?></label>
100
- <div id="wprss-dialog-sources-container" style="display:none">
101
- <p><?php _e( 'Choose the feed source to display:', WPRSS_TEXT_DOMAIN ) ?></p>
102
- <?php echo $feed_sources_select; ?>
103
- </div>
104
- <script>
105
- jQuery('#wprss-dialog-all-sources').click( function(){
106
- if ( jQuery(this).is(':checked') ) {
107
- jQuery( '#wprss-dialog-sources-container' ).hide();
108
- jQuery( '#wprss-dialog-exclude-row' ).show();
109
- jQuery( '#wprss-dialog-all-sources-label' ).css('vertical-align', 'middle');
110
- } else {
111
- jQuery( '#wprss-dialog-sources-container' ).show();
112
- jQuery( '#wprss-dialog-exclude-row' ).hide();
113
- jQuery( '#wprss-dialog-all-sources-label' ).css('vertical-align', 'top');
114
- }
115
- });
116
- jQuery('#wprss-dialog-submit').click( wprss_dialog_submit );
117
- </script>
118
- </td>
119
- </tr>
120
-
121
- <tr id="wprss-dialog-exclude-row">
122
- <td id="wprss-dialog-exclude-label"><?php _e( 'Exclude:', WPRSS_TEXT_DOMAIN ) ?></td>
123
- <td>
124
- <p><?php _e( 'Choose the feed sources to exclude:', WPRSS_TEXT_DOMAIN ) ?></p>
125
- <?php echo $feed_sources_exclude_select; ?>
126
- </td>
127
- </tr>
128
-
129
- <tr>
130
- <td><?php _e( 'Feed Limit:', WPRSS_TEXT_DOMAIN ) ?></td>
131
- <td> <input id="wprss-dialog-feed-limit" type="number" class="wprss-number-roller" placeholder="<?php _e( 'Ignore', WPRSS_TEXT_DOMAIN ) ?> " min="0" /> </td>
132
- </tr>
133
-
134
- <?php do_action( 'wprss_return_dialog_contents' ); ?>
135
-
136
- <tr>
137
- <td></td>
138
- <td>
139
- <button id="wprss-dialog-submit"><?php _e( 'Add shortcode', WPRSS_TEXT_DOMAIN ) ?></button>
140
- </td>
141
- </tr>
142
-
143
- </tbody>
144
- </table>
145
- <?php
146
- die();
147
  }
1
+ <?php
2
+ /**
3
+ * This file contains code related to the custom button added to Wordpress' TinyMCE editor.
4
+ *
5
+ * @since 3.5
6
+ */
7
+
8
+
9
+
10
+ add_action( 'admin_init', 'wprss_add_editor_button' );
11
+ /**
12
+ * Adds the WPRSS button to WordPress' editor
13
+ *
14
+ * @since 3.5
15
+ */
16
+ function wprss_add_editor_button() {
17
+ if ( ! current_user_can( 'edit_posts' ) && !current_user_can( 'edit_pages' ) )
18
+ return;
19
+ if ( get_user_option( 'rich_editing' ) == 'true') {
20
+ add_filter( 'mce_external_plugins', 'wprss_register_tinymce_plugin' );
21
+ add_filter( 'mce_buttons', 'wprss_register_tinymce_button' );
22
+ }
23
+ }
24
+
25
+
26
+ /**
27
+ * Adds a separator and the wprss button to the buttons array.
28
+ *
29
+ * @since 3.5
30
+ */
31
+ function wprss_register_tinymce_button( $buttons ) {
32
+ array_push( $buttons, "|", "wprss" );
33
+ return $buttons;
34
+ }
35
+
36
+
37
+ /**
38
+ * Adds the button action JS file to TinyMCE's plugin list
39
+ *
40
+ * @todo add filter to skip showing the editor button
41
+ * @since 3.5
42
+ */
43
+ function wprss_register_tinymce_plugin($plugin_array) {
44
+ // add filter here
45
+ $plugin_array['wprss'] = WPRSS_JS . 'editor.js';
46
+ return $plugin_array;
47
+ }
48
+
49
+
50
+ add_filter( 'tiny_mce_version', 'wprss_tinymce_version');
51
+ /**
52
+ * Intercepts TinyMCE's version check and increments its version by 3.
53
+ *
54
+ * This is a hack used to work around TinyMCE's caching, that might prevent the
55
+ * new wprss button from appearing on the editor.
56
+ *
57
+ * @since 3.5
58
+ */
59
+ function wprss_tinymce_version($ver) {
60
+ $ver += 3;
61
+ return $ver;
62
+ }
63
+
64
+
65
+
66
+
67
+
68
+
69
+ add_action( 'wp_ajax_wprss_editor_dialog', 'wprss_return_dialog_contents' );
70
+ /**
71
+ *
72
+ *
73
+ */
74
+ function wprss_return_dialog_contents() {
75
+ $feed_sources = get_posts( array(
76
+ 'post_type' => 'wprss_feed',
77
+ 'post_status' => 'publish',
78
+ 'posts_per_page' => -1,
79
+ 'no_found_rows' => true
80
+ ));
81
+ $feed_sources_select = '<select id="wprss-dialog-feed-source-list" multiple>';
82
+ $feed_sources_exclude_select = '<select id="wprss-dialog-exclude-list" multiple>';
83
+ $feed_sources_both_select = '';
84
+ foreach ( $feed_sources as $source ) {
85
+ $feed_sources_both_select .= '<option value="' . $source->ID . '" >' . $source->post_title . '</option>';
86
+ }
87
+ $feed_sources_both_select .= '</select><p>' . __( 'Hold Ctrl or Mac Command key when clicking to select more than one feed source.' , WPRSS_TEXT_DOMAIN ) . '</p>';
88
+
89
+ $feed_sources_select .= $feed_sources_both_select;
90
+ $feed_sources_exclude_select .= $feed_sources_both_select;
91
+
92
+ ?>
93
+ <table cellspacing="20">
94
+ <tbody>
95
+
96
+ <tr>
97
+ <td id="wprss-dialog-all-sources-label"><?php _e( 'Feed Sources', WPRSS_TEXT_DOMAIN ) ?></td>
98
+ <td>
99
+ <input id="wprss-dialog-all-sources" type="checkbox" checked> <label for="wprss-dialog-all-sources"><?php _e( 'All feed sources', WPRSS_TEXT_DOMAIN ) ?></label>
100
+ <div id="wprss-dialog-sources-container" style="display:none">
101
+ <p><?php _e( 'Choose the feed source to display:', WPRSS_TEXT_DOMAIN ) ?></p>
102
+ <?php echo $feed_sources_select; ?>
103
+ </div>
104
+ <script>
105
+ jQuery('#wprss-dialog-all-sources').click( function(){
106
+ if ( jQuery(this).is(':checked') ) {
107
+ jQuery( '#wprss-dialog-sources-container' ).hide();
108
+ jQuery( '#wprss-dialog-exclude-row' ).show();
109
+ jQuery( '#wprss-dialog-all-sources-label' ).css('vertical-align', 'middle');
110
+ } else {
111
+ jQuery( '#wprss-dialog-sources-container' ).show();
112
+ jQuery( '#wprss-dialog-exclude-row' ).hide();
113
+ jQuery( '#wprss-dialog-all-sources-label' ).css('vertical-align', 'top');
114
+ }
115
+ });
116
+ jQuery('#wprss-dialog-submit').click( wprss_dialog_submit );
117
+ </script>
118
+ </td>
119
+ </tr>
120
+
121
+ <tr id="wprss-dialog-exclude-row">
122
+ <td id="wprss-dialog-exclude-label"><?php _e( 'Exclude:', WPRSS_TEXT_DOMAIN ) ?></td>
123
+ <td>
124
+ <p><?php _e( 'Choose the feed sources to exclude:', WPRSS_TEXT_DOMAIN ) ?></p>
125
+ <?php echo $feed_sources_exclude_select; ?>
126
+ </td>
127
+ </tr>
128
+
129
+ <tr>
130
+ <td><?php _e( 'Feed Limit:', WPRSS_TEXT_DOMAIN ) ?></td>
131
+ <td> <input id="wprss-dialog-feed-limit" type="number" class="wprss-number-roller" placeholder="<?php _e( 'Ignore', WPRSS_TEXT_DOMAIN ) ?> " min="0" /> </td>
132
+ </tr>
133
+
134
+ <?php do_action( 'wprss_return_dialog_contents' ); ?>
135
+
136
+ <tr>
137
+ <td></td>
138
+ <td>
139
+ <button id="wprss-dialog-submit"><?php _e( 'Add shortcode', WPRSS_TEXT_DOMAIN ) ?></button>
140
+ </td>
141
+ </tr>
142
+
143
+ </tbody>
144
+ </table>
145
+ <?php
146
+ die();
147
  }
includes/admin-heartbeat.php CHANGED
@@ -1,78 +1,78 @@
1
- <?php
2
-
3
- add_action( 'wp_ajax_wprss_feed_source_table_ajax', 'wprss_feed_source_updates');
4
- /**
5
- *
6
- */
7
- function wprss_feed_source_updates() {
8
- $response = array();
9
-
10
- if ( ! current_user_can( 'edit_feed_sources' ) ) return $response;
11
-
12
- if ( empty($_POST['wprss_heartbeat']) ) return $response;
13
-
14
- // Get the wprss heartbeat data and extract the data
15
- $wprss_heartbeat = $_POST['wprss_heartbeat'];
16
- extract( $wprss_heartbeat );
17
-
18
- // Perform the action specified by the heartbeat data
19
- switch( $action ) {
20
- /* FEED SOURCE UPDATING STATUS
21
- * Used to determine whether or not to show the updating icon in the feed source table.
22
- */
23
- case 'feed_sources':
24
- // Prepare array of IDs for feed sources currently updating
25
- $feed_sources_data = array();
26
- // Iterate all feed sources
27
- foreach ( $params as $feed_id ) {
28
- $feed_sources_data[$feed_id] = array();
29
- $feed_source_data = &$feed_sources_data[$feed_id];
30
-
31
- // Check if the feed source is updating
32
- $seconds_for_next_update = wprss_get_next_feed_source_update( $feed_id ) - time();
33
- $feed_source_data['updating'] = ( $seconds_for_next_update < 2 && $seconds_for_next_update > 0 ) || wprss_is_feed_source_updating( $feed_id ) || wprss_is_feed_source_deleting( $feed_id );
34
-
35
- // Add the number of imported items
36
- $items = wprss_get_feed_items_for_source( $feed_id );
37
- $feed_source_data['items'] = $items->post_count;
38
- // Update the meta field
39
- update_post_meta( $feed_id, 'wprss_items_imported', $items->post_count );
40
-
41
- // Add the next update time
42
- $next_update = wprss_get_next_feed_source_update( $feed_id );
43
- $update_interval = get_post_meta( $feed_id, 'wprss_update_interval', TRUE );
44
- // If using the global interval, get the timestamp of the next global update
45
- if ( $update_interval === wprss_get_default_feed_source_update_interval() || $update_interval === '' ) {
46
- $next_update = wp_next_scheduled( 'wprss_fetch_all_feeds_hook', array() );
47
- }
48
- // Set the text appropriately
49
- if ( ! wprss_is_feed_source_active( $feed_id ) ) {
50
- $feed_source_data['next-update'] = __( 'Paused', WPRSS_TEXT_DOMAIN );
51
- }
52
- elseif( $next_update === FALSE ) {
53
- $feed_source_data['next-update'] = __( 'None', WPRSS_TEXT_DOMAIN );
54
- }
55
- else {
56
- $feed_source_data['next-update'] = human_time_diff( $next_update, time() );
57
- }
58
- // Update the meta field
59
- update_post_meta( $feed_id, 'wprss_next_update', $feed_source_data['next-update'] );
60
-
61
- // Add the last update information
62
- $last_update = get_post_meta( $feed_id, 'wprss_last_update', TRUE );
63
- $last_update_items = get_post_meta( $feed_id, 'wprss_last_update_items', TRUE );
64
-
65
- $feed_source_data['last-update'] = ( $last_update === '' )? '' : human_time_diff( $last_update, time() );
66
- $feed_source_data['last-update-imported'] = $last_update_items;
67
-
68
- // Add any error info
69
- $errors = get_post_meta( $feed_id, 'wprss_error_last_import', true );
70
- $feed_source_data['errors'] = $errors;
71
- }
72
- // Send back all the IDs
73
- $response['wprss_feed_sources_data'] = $feed_sources_data;
74
- break;
75
- }
76
- // Return the response
77
- die( json_encode($response) );
78
  }
1
+ <?php
2
+
3
+ add_action( 'wp_ajax_wprss_feed_source_table_ajax', 'wprss_feed_source_updates');
4
+ /**
5
+ *
6
+ */
7
+ function wprss_feed_source_updates() {
8
+ $response = array();
9
+
10
+ if ( ! current_user_can( 'edit_feed_sources' ) ) return $response;
11
+
12
+ if ( empty($_POST['wprss_heartbeat']) ) return $response;
13
+
14
+ // Get the wprss heartbeat data and extract the data
15
+ $wprss_heartbeat = $_POST['wprss_heartbeat'];
16
+ extract( $wprss_heartbeat );
17
+
18
+ // Perform the action specified by the heartbeat data
19
+ switch( $action ) {
20
+ /* FEED SOURCE UPDATING STATUS
21
+ * Used to determine whether or not to show the updating icon in the feed source table.
22
+ */
23
+ case 'feed_sources':
24
+ // Prepare array of IDs for feed sources currently updating
25
+ $feed_sources_data = array();
26
+ // Iterate all feed sources
27
+ foreach ( $params as $feed_id ) {
28
+ $feed_sources_data[$feed_id] = array();
29
+ $feed_source_data = &$feed_sources_data[$feed_id];
30
+
31
+ // Check if the feed source is updating
32
+ $seconds_for_next_update = wprss_get_next_feed_source_update( $feed_id ) - time();
33
+ $feed_source_data['updating'] = ( $seconds_for_next_update < 2 && $seconds_for_next_update > 0 ) || wprss_is_feed_source_updating( $feed_id ) || wprss_is_feed_source_deleting( $feed_id );
34
+
35
+ // Add the number of imported items
36
+ $items = wprss_get_feed_items_for_source( $feed_id );
37
+ $feed_source_data['items'] = $items->post_count;
38
+ // Update the meta field
39
+ update_post_meta( $feed_id, 'wprss_items_imported', $items->post_count );
40
+
41
+ // Add the next update time
42
+ $next_update = wprss_get_next_feed_source_update( $feed_id );
43
+ $update_interval = get_post_meta( $feed_id, 'wprss_update_interval', TRUE );
44
+ // If using the global interval, get the timestamp of the next global update
45
+ if ( $update_interval === wprss_get_default_feed_source_update_interval() || $update_interval === '' ) {
46
+ $next_update = wp_next_scheduled( 'wprss_fetch_all_feeds_hook', array() );
47
+ }
48
+ // Set the text appropriately
49
+ if ( ! wprss_is_feed_source_active( $feed_id ) ) {
50
+ $feed_source_data['next-update'] = __( 'Paused', WPRSS_TEXT_DOMAIN );
51
+ }
52
+ elseif( $next_update === FALSE ) {
53
+ $feed_source_data['next-update'] = __( 'None', WPRSS_TEXT_DOMAIN );
54
+ }
55
+ else {
56
+ $feed_source_data['next-update'] = human_time_diff( $next_update, time() );
57
+ }
58
+ // Update the meta field
59
+ update_post_meta( $feed_id, 'wprss_next_update', $feed_source_data['next-update'] );
60
+
61
+ // Add the last update information
62
+ $last_update = get_post_meta( $feed_id, 'wprss_last_update', TRUE );
63
+ $last_update_items = get_post_meta( $feed_id, 'wprss_last_update_items', TRUE );
64
+
65
+ $feed_source_data['last-update'] = ( $last_update === '' )? '' : human_time_diff( $last_update, time() );
66
+ $feed_source_data['last-update-imported'] = $last_update_items;
67
+
68
+ // Add any error info
69
+ $errors = get_post_meta( $feed_id, 'wprss_error_last_import', true );
70
+ $feed_source_data['errors'] = $errors;
71
+ }
72
+ // Send back all the IDs
73
+ $response['wprss_feed_sources_data'] = $feed_sources_data;
74
+ break;
75
+ }
76
+ // Return the response
77
+ die( json_encode($response) );
78
  }
includes/admin-help-metaboxes.php CHANGED
@@ -1,71 +1,76 @@
1
- <?php
2
-
3
- if( class_exists('WPRSS_Help') ) {
4
- $help = WPRSS_Help::get_instance();
5
-
6
- // Feed source setting fields
7
- $prefix = 'field_';
8
- $tooltips = array(
9
- /* -----------------------------
10
- * Feed Source Details Metabox
11
- * -----------------------------
12
- */
13
- // Feed Source URL
14
- 'wprss_url' => __('The URL of the feed source. In most cases, the URL of the site will also work, but for best results we recommend trying to find the URL of the RSS feed.
15
-
16
- Also include the <code>http://</code> prefix in the URL.', WPRSS_TEXT_DOMAIN),
17
- // Feed limit
18
- 'wprss_limit' => __('The maximum number of imported items from this feed to keep stored.
19
-
20
- When new items are imported and the limit is exceeded, the oldest feed items will be deleted to make room for new ones.
21
-
22
- If you already have items imported from this feed source, setting this option now may delete some of your items, in order to comply with the limit.', WPRSS_TEXT_DOMAIN),
23
- // Link to Enclosure
24
- 'wprss_enclosure' => __('Tick this box to make feed items link to the URL in the enclosure tag, rather than link to the original article.
25
-
26
- Enclosure tags are RSS tags that may be included with a feed items. These tags typically contain links to images, audio, videos, attachment files or even flash content.
27
-
28
- If you are not sure leave this setting blank.', WPRSS_TEXT_DOMAIN),
29
-
30
- /* -------------------------
31
- * Feed Processing Metabox
32
- * -------------------------
33
- */
34
- // Feed State
35
- 'wprss_state' => __('State of the feed, active or paused.
36
-
37
- If active, the feed source will fetch items periodically, according to the settings below.
38
-
39
- If paused, the feed source will not fetch feed items periodically.', WPRSS_TEXT_DOMAIN),
40
- // Activate Feed: [date]
41
- 'wprss_activate_feed' => __('You can set a time, in UTC, in the future when the feed source will become active, if it is paused.
42
-
43
- Leave blank to activate immediately.', WPRSS_TEXT_DOMAIN),
44
- // Pause Feed: [date]
45
- 'wprss_pause_feed' => __('You can set a time, in UTC, in the future when the feed source will become paused, if it is active.
46
-
47
- Leave blank to never pause.', WPRSS_TEXT_DOMAIN),
48
- // Update Interval
49
- 'wprss_update_interval' => __('How frequently the feed source should check for new items and fetch if needed.
50
-
51
- If left as <em>Default</em>, the interval in the global settings is used.', WPRSS_TEXT_DOMAIN),
52
- // Delete items older than: [date]
53
- 'wprss_age_limit' => __('The maximum age allowed for feed items. Very useful if you are only concerned with, say, last week\'s news.
54
-
55
- Items already imported will be deleted if they eventually exceed this age limit.
56
-
57
- Also, items in the RSS feed that are already older than this age will not be imported at all.
58
-
59
- Leaving empty to use the <em>Limit feed items by age</em> option in the general settings.', WPRSS_TEXT_DOMAIN),
60
-
61
- /* ----------------------
62
- * Feed Preview Metabox
63
- * ----------------------
64
- */
65
- // Force Feed
66
- 'wprss_force_feed' => __('Use this option if you are seeing an <q>Invalid feed URL</q> error in the Feed Preview above, but you are sure that the URL is correct.
67
-
68
- Note, however, that this will disable auto-discovery, meaning that the given URL must be an RSS feed URL. Using the site\'s URL will not work.', WPRSS_TEXT_DOMAIN)
69
- );
70
- $help->add_tooltips( $tooltips, $prefix );
 
 
 
 
 
71
  }
1
+ <?php
2
+
3
+ if( class_exists('WPRSS_Help') ) {
4
+ $help = WPRSS_Help::get_instance();
5
+
6
+ // Feed source setting fields
7
+ $prefix = 'field_';
8
+ $tooltips = array(
9
+ /* -----------------------------
10
+ * Feed Source Details Metabox
11
+ * -----------------------------
12
+ */
13
+ // Feed Source URL
14
+ 'wprss_url' => __('The URL of the feed source. In most cases, the URL of the site will also work, but for best results we recommend trying to find the URL of the RSS feed.
15
+
16
+ Also include the <code>http://</code> prefix in the URL.', WPRSS_TEXT_DOMAIN),
17
+ // Feed limit
18
+ 'wprss_limit' => __('The maximum number of imported items from this feed to keep stored.
19
+
20
+ When new items are imported and the limit is exceeded, the oldest feed items will be deleted to make room for new ones.
21
+
22
+ If you already have items imported from this feed source, setting this option now may delete some of your items, in order to comply with the limit.', WPRSS_TEXT_DOMAIN),
23
+ // Link to Enclosure
24
+ 'wprss_enclosure' => __('Tick this box to make feed items link to the URL in the enclosure tag, rather than link to the original article.
25
+
26
+ Enclosure tags are RSS tags that may be included with a feed items. These tags typically contain links to images, audio, videos, attachment files or even flash content.
27
+
28
+ If you are not sure leave this setting blank.', WPRSS_TEXT_DOMAIN),
29
+
30
+ 'wprss_unique_titles' => __('Whether to allow multiple feed items to have the same title. When checked, if a feed item has the same title as a previously-imported feed item, it will not be imported.
31
+
32
+ This can be useful in cases where permalinks change, or where multiple permalinks refer to the same item.', WPRSS_TEXT_DOMAIN),
33
+
34
+
35
+ /* -------------------------
36
+ * Feed Processing Metabox
37
+ * -------------------------
38
+ */
39
+ // Feed State
40
+ 'wprss_state' => __('State of the feed, active or paused.
41
+
42
+ If active, the feed source will fetch items periodically, according to the settings below.
43
+
44
+ If paused, the feed source will not fetch feed items periodically.', WPRSS_TEXT_DOMAIN),
45
+ // Activate Feed: [date]
46
+ 'wprss_activate_feed' => __('You can set a time, in UTC, in the future when the feed source will become active, if it is paused.
47
+
48
+ Leave blank to activate immediately.', WPRSS_TEXT_DOMAIN),
49
+ // Pause Feed: [date]
50
+ 'wprss_pause_feed' => __('You can set a time, in UTC, in the future when the feed source will become paused, if it is active.
51
+
52
+ Leave blank to never pause.', WPRSS_TEXT_DOMAIN),
53
+ // Update Interval
54
+ 'wprss_update_interval' => __('How frequently the feed source should check for new items and fetch if needed.
55
+
56
+ If left as <em>Default</em>, the interval in the global settings is used.', WPRSS_TEXT_DOMAIN),
57
+ // Delete items older than: [date]
58
+ 'wprss_age_limit' => __('The maximum age allowed for feed items. Very useful if you are only concerned with, say, last week\'s news.
59
+
60
+ Items already imported will be deleted if they eventually exceed this age limit.
61
+
62
+ Also, items in the RSS feed that are already older than this age will not be imported at all.
63
+
64
+ Leaving empty to use the <em>Limit feed items by age</em> option in the general settings.', WPRSS_TEXT_DOMAIN),
65
+
66
+ /* ----------------------
67
+ * Feed Preview Metabox
68
+ * ----------------------
69
+ */
70
+ // Force Feed
71
+ 'wprss_force_feed' => __('Use this option if you are seeing an <q>Invalid feed URL</q> error in the Feed Preview above, but you are sure that the URL is correct.
72
+
73
+ Note, however, that this will disable auto-discovery, meaning that the given URL must be an RSS feed URL. Using the site\'s URL will not work.', WPRSS_TEXT_DOMAIN)
74
+ );
75
+ $help->add_tooltips( $tooltips, $prefix );
76
  }
includes/admin-help-settings.php CHANGED
@@ -1,116 +1,137 @@
1
- <?php
2
-
3
- if( class_exists('WPRSS_Help') ) {
4
- $help = WPRSS_Help::get_instance();
5
-
6
- // Feed source setting fields
7
- $prefix = 'setting-';
8
- $tooltips = array(
9
- /* -----------------
10
- * General Section
11
- * -----------------
12
- */ // Limit feed items by age
13
- 'limit-feed-items-by-age' => __( 'The maximum age allowed for feed items.
14
- <hr/>
15
-
16
- Items already imported will be deleted if they eventually exceed this age limit.
17
-
18
- Also, items in the RSS feed that are already older than this age will not be imported at all.
19
- <hr/>
20
-
21
- <em>Leave empty for no limit.</em>', WPRSS_TEXT_DOMAIN),
22
- // Limit feed items per feed
23
- 'limit-feed-items-imported' => __('The maximum number of imported items to keep stored, for feed sources that do not have their own limit.
24
- <hr/>
25
-
26
- When new items are imported and the limit for a feed source is exceeded, the oldest feed items for that feed source will be deleted to make room for the new ones.
27
-
28
- If you already have items imported from this feed source, setting this option now may delete some of your items, in order to comply with the limit.
29
- <hr/>
30
-
31
- <em>Use 0 or leave empty for no limit.</em>', WPRSS_TEXT_DOMAIN),
32
- // Feed processing interval
33
- 'cron-interval' => __('How frequently should the feed sources (that do not have their own update interval) check for updates and fetch items accordingly.
34
-
35
- It is recommended to not have more than 20 feed sources that use this global update interval. Having too many feed sources updating precisely at the same time can cause the WP Cron System to crash.', WPRSS_TEXT_DOMAIN),
36
- // Custom Feed URL
37
- 'custom-feed-url' => __('The URL of the custom feed, located at <code>http://yoursite.com/[custom feed url]</code>.
38
- <hr/>
39
-
40
- WP RSS Aggregator allows you to create a custom RSS feed, that contains all of your imported feed items. This setting allows you to change the URL of this custom feed.
41
-
42
- <hr/>
43
-
44
- <strong>Note:</strong> You may be required to refresh you Permalinks after you change this setting, by going to <em>Settings <i class="fa fa-angle-right"></i> Permalinks</e> and clicking <em>Save</em>.', WPRSS_TEXT_DOMAIN),
45
- // Custom Feed Title
46
- 'custom-feed-title' => __('The title of the custom feed.
47
-
48
- This title will be included in the RSS source of the custom feed, in a <code>&lt;title&gt;</code> tag.', WPRSS_TEXT_DOMAIN),
49
- // Custom Feed Limit
50
- 'custom-feed-limit' => __('The maximum number of feed items in the custom feed.', WPRSS_TEXT_DOMAIN),
51
-
52
- /* --------------------------
53
- * General Display Settings
54
- * --------------------------
55
- */ // Link titles
56
- 'link-enable' => __('Check this box to make the feed item titles link to the original article.', WPRSS_TEXT_DOMAIN),
57
- // Title Maximum length
58
- 'title-limit' => __('Set the maximum number of characters to show for feed item titles.
59
- <hr/>
60
-
61
- <em>Leave empty for no limit.</em>', WPRSS_TEXT_DOMAIN),
62
- // Show Authors
63
- 'authors-enable' => __('Check this box to show the author for each feed item, if it is available.', WPRSS_TEXT_DOMAIN),
64
- // Video Links
65
- 'video-links' => __('For feed items from YouTube, Vimeo or Dailymotion, you can choose whether you want to have the items link to the original page link, or a link to the embedded video player only.', WPRSS_TEXT_DOMAIN),
66
- // Pagination Type
67
- 'pagination' => __('The type of pagination to use when showing feed items on multiple pages.
68
-
69
- The first shows two links, "Older" and "Newer", which allow you to navigate through the pages.
70
-
71
- The second shows links for all the pages, together with links for the next and previous pages.', WPRSS_TEXT_DOMAIN),
72
- // Feed Limit
73
- 'feed-limit' => __('The maximum number of feed items to display when using the shortcode.
74
-
75
- This enables pagination if set to a number smaller than the number of items to be displayed.', WPRSS_TEXT_DOMAIN),
76
- // Open Links Behaviour
77
- 'open-dd' => __('Choose how you want links to be opened. This applies to the feed item title and the source link.', WPRSS_TEXT_DOMAIN),
78
- // Set links as no follow
79
- 'follow-dd' => __('Enable this option to set all links displayed as "NoFollow".
80
- <hr/>
81
-
82
- "Nofollow" provides a way to tell search engines to <em>not</em> follow certain links, such as links to feed items in this case.', WPRSS_TEXT_DOMAIN),
83
-
84
- /* -------------------------
85
- * Source Display Settings
86
- * -------------------------
87
- */ // Source Enabled
88
- 'source-enable' => __('Enable this option to show the feed source name for each feed item.', WPRSS_TEXT_DOMAIN),
89
- // Text preceding source
90
- 'text-preceding-source' => __('Enter the text that you want to show before the source name. A space is automatically added between this text and the feed source name.', WPRSS_TEXT_DOMAIN),
91
- // Source Link
92
- 'source-link' => __('Enable this option to link the feed source name to the RSS feed\'s source site.', WPRSS_TEXT_DOMAIN),
93
-
94
- /* -------------------------
95
- * Date Display Settings
96
- * -------------------------
97
- */ // Source Enabled
98
- 'date-enable' => __('Enable this to show the feed item\'s date.', WPRSS_TEXT_DOMAIN),
99
- // Text preceding date
100
- 'text-preceding-date' => __('Enter the text that you want to show before the feed item date. A space is automatically added between this text and the date.', WPRSS_TEXT_DOMAIN),
101
- // Date Format
102
- 'date-format' => __('The format to use for the feed item dates, as a PHP date format.', WPRSS_TEXT_DOMAIN),
103
- // Time Ago Format Enable
104
- 'time-ago-format-enable' => __('Enable this option to show the elapsed time from the feed item\'s date and time to the present time.
105
- <em>Eg. 2 hours ago</em>', WPRSS_TEXT_DOMAIN),
106
-
107
- /* --------
108
- * Styles
109
- * --------
110
- */ // Styles Disable
111
- 'styles-disable' => __('Check this box to disable all plugin styles used for displaying feed items.
112
-
113
- This will allow you to provide your own custom CSS styles for displaying the feed items.', WPRSS_TEXT_DOMAIN)
114
- );
115
- $help->add_tooltips( $tooltips, $prefix );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  }
1
+ <?php
2
+
3
+ if( class_exists('WPRSS_Help') ) {
4
+ $help = WPRSS_Help::get_instance();
5
+
6
+ // Feed source setting fields
7
+ $prefix = 'setting-';
8
+ $tooltips = array(
9
+ /* -----------------
10
+ * General Section
11
+ * -----------------
12
+ */ // Limit feed items by age
13
+ 'limit-feed-items-by-age' => __( 'The maximum age allowed for feed items.
14
+ <hr/>
15
+
16
+ Items already imported will be deleted if they eventually exceed this age limit.
17
+
18
+ Also, items in the RSS feed that are already older than this age will not be imported at all.
19
+ <hr/>
20
+
21
+ <em>Leave empty for no limit.</em>', WPRSS_TEXT_DOMAIN),
22
+ // Limit feed items per feed
23
+ 'limit-feed-items-imported' => __('The maximum number of imported items to keep stored, for feed sources that do not have their own limit.
24
+ <hr/>
25
+
26
+ When new items are imported and the limit for a feed source is exceeded, the oldest feed items for that feed source will be deleted to make room for the new ones.
27
+
28
+ If you already have items imported from this feed source, setting this option now may delete some of your items, in order to comply with the limit.
29
+ <hr/>
30
+
31
+ <em>Use 0 or leave empty for no limit.</em>', WPRSS_TEXT_DOMAIN),
32
+ // Feed processing interval
33
+ 'cron-interval' => __('How frequently should the feed sources (that do not have their own update interval) check for updates and fetch items accordingly.
34
+
35
+ It is recommended to not have more than 20 feed sources that use this global update interval. Having too many feed sources updating precisely at the same time can cause the WP Cron System to crash.', WPRSS_TEXT_DOMAIN),
36
+ // Unique titles only
37
+ 'unique-titles' => __('Whether to allow multiple feed items to have the same title. When checked, if a feed item has the same title as a previously-imported feed item from any feed source, it will not be imported.
38
+
39
+ This can be useful in cases where permalinks change, or where multiple permalinks refer to the same item.
40
+
41
+ Since this feature requires checking every post title, WordPress installs with a significant amount of posts may notice a slight slowdown of the post import process.', WPRSS_TEXT_DOMAIN),
42
+ // Custom Feed URL
43
+ 'custom-feed-url' => __('The URL of the custom feed, located at <code>http://yoursite.com/[custom feed url]</code>.
44
+ <hr/>
45
+
46
+ WP RSS Aggregator allows you to create a custom RSS feed, that contains all of your imported feed items. This setting allows you to change the URL of this custom feed.
47
+
48
+ <hr/>
49
+
50
+ <strong>Note:</strong> You may be required to refresh you Permalinks after you change this setting, by going to <em>Settings <i class="fa fa-angle-right"></i> Permalinks</e> and clicking <em>Save</em>.', WPRSS_TEXT_DOMAIN),
51
+ // Custom Feed Title
52
+ 'custom-feed-title' => __('The title of the custom feed.
53
+
54
+ This title will be included in the RSS source of the custom feed, in a <code>&lt;title&gt;</code> tag.', WPRSS_TEXT_DOMAIN),
55
+ // Custom Feed Limit
56
+ 'custom-feed-limit' => __('The maximum number of feed items in the custom feed.', WPRSS_TEXT_DOMAIN),
57
+
58
+ /* --------------------------
59
+ * General Display Settings
60
+ * --------------------------
61
+ */ // Link titles
62
+ 'link-enable' => __('Check this box to make the feed item titles link to the original article.', WPRSS_TEXT_DOMAIN),
63
+ // Title Maximum length
64
+ 'title-limit' => __('Set the maximum number of characters to show for feed item titles.
65
+ <hr/>
66
+
67
+ <em>Leave empty for no limit.</em>', WPRSS_TEXT_DOMAIN),
68
+ // Show Authors
69
+ 'authors-enable' => __('Check this box to show the author for each feed item, if it is available.', WPRSS_TEXT_DOMAIN),
70
+ // Video Links
71
+ 'video-links' => __('For feed items from YouTube, Vimeo or Dailymotion, you can choose whether you want to have the items link to the original page link, or a link to the embedded video player only.', WPRSS_TEXT_DOMAIN),
72
+ // Pagination Type
73
+ 'pagination' => __('The type of pagination to use when showing feed items on multiple pages.
74
+
75
+ The first shows two links, "Older" and "Newer", which allow you to navigate through the pages.
76
+
77
+ The second shows links for all the pages, together with links for the next and previous pages.', WPRSS_TEXT_DOMAIN),
78
+ // Feed Limit
79
+ 'feed-limit' => __('The maximum number of feed items to display when using the shortcode.
80
+
81
+ This enables pagination if set to a number smaller than the number of items to be displayed.', WPRSS_TEXT_DOMAIN),
82
+ // Open Links Behaviour
83
+ 'open-dd' => __('Choose how you want links to be opened. This applies to the feed item title and the source link.', WPRSS_TEXT_DOMAIN),
84
+ // Set links as no follow
85
+ 'follow-dd' => __('Enable this option to set all links displayed as "NoFollow".
86
+ <hr/>
87
+
88
+ "Nofollow" provides a way to tell search engines to <em>not</em> follow certain links, such as links to feed items in this case.', WPRSS_TEXT_DOMAIN),
89
+
90
+ /* -------------------------
91
+ * Source Display Settings
92
+ * -------------------------
93
+ */ // Source Enabled
94
+ 'source-enable' => __('Enable this option to show the feed source name for each feed item.', WPRSS_TEXT_DOMAIN),
95
+ // Text preceding source
96
+ 'text-preceding-source' => __('Enter the text that you want to show before the source name. A space is automatically added between this text and the feed source name.', WPRSS_TEXT_DOMAIN),
97
+ // Source Link
98
+ 'source-link' => __('Enable this option to link the feed source name to the RSS feed\'s source site.', WPRSS_TEXT_DOMAIN),
99
+
100
+ /* -------------------------
101
+ * Date Display Settings
102
+ * -------------------------
103
+ */ // Source Enabled
104
+ 'date-enable' => __('Enable this to show the feed item\'s date.', WPRSS_TEXT_DOMAIN),
105
+ // Text preceding date
106
+ 'text-preceding-date' => __('Enter the text that you want to show before the feed item date. A space is automatically added between this text and the date.', WPRSS_TEXT_DOMAIN),
107
+ // Date Format
108
+ 'date-format' => __('The format to use for the feed item dates, as a PHP date format.', WPRSS_TEXT_DOMAIN),
109
+ // Time Ago Format Enable
110
+ 'time-ago-format-enable' => __('Enable this option to show the elapsed time from the feed item\'s date and time to the present time.
111
+ <em>Eg. 2 hours ago</em>', WPRSS_TEXT_DOMAIN),
112
+
113
+ /* --------
114
+ * Styles
115
+ * --------
116
+ */ // Styles Disable
117
+ 'styles-disable' => __('Check this box to disable all plugin styles used for displaying feed items.
118
+
119
+ This will allow you to provide your own custom CSS styles for displaying the feed items.', WPRSS_TEXT_DOMAIN),
120
+
121
+ /*
122
+ * -------
123
+ * Other
124
+ * -------
125
+ */ // Certificate Path
126
+ 'certificate-path' => __( 'Path to the file containing one or more certificates.
127
+
128
+ These will be used to verify certificates over secure connection, such as when fetching a remote resource over HTTPS.
129
+
130
+ Relative path will be relative to the WordPress root.
131
+
132
+ <strong>Default:</strong> path to certificate file bundled with WordPress.'
133
+ , WPRSS_TEXT_DOMAIN )
134
+
135
+ );
136
+ $help->add_tooltips( $tooltips, $prefix );
137
  }
includes/admin-help.php CHANGED
@@ -1,1107 +1,1107 @@
1
- <?php
2
- /**
3
- * Build the Help page
4
- *
5
- * @since 4.2
6
- */
7
- function wprss_help_page_display() {
8
- ?>
9
-
10
- <div class="wrap">
11
- <?php screen_icon( 'wprss-aggregator' ); ?>
12
-
13
- <h2><?php _e( 'Help & Support', WPRSS_TEXT_DOMAIN ); ?></h2>
14
- <h3><?php _e( 'Documentation', WPRSS_TEXT_DOMAIN ) ?></h3>
15
- <?php echo wpautop( __('In the <a href="http://www.wprssaggregator.com/documentation/">documentation area</a> on the WP RSS Aggregator website you will find comprehensive details on how to use the core plugin and all the add-ons.
16
-
17
- There are also some videos to help you make a quick start to setting up and enjoying this plugin.', WPRSS_TEXT_DOMAIN) ) ?>
18
- <h3><?php _e( 'Frequently Asked Questions (FAQ)', WPRSS_TEXT_DOMAIN ) ?></h3>
19
- <?php echo wpautop( __('If after going through the documentation you still have questions, please take a look at the <a href="http://www.wprssaggregator.com/faq/">FAQ page</a> on the site, we set this up purposely to answer the most commonly asked questions by our users.', WPRSS_TEXT_DOMAIN) ) ?>
20
-
21
- <?php
22
- if ( wprss_is_premium_user() ) {
23
- wprss_premium_help_display();
24
- } else {
25
- wprss_free_help_display();
26
- }
27
- ?>
28
- </div>
29
- <?php
30
- }
31
-
32
-
33
- /**
34
- * Print the premium help section with inline support form.
35
- *
36
- * @since 4.7
37
- */
38
- function wprss_premium_help_display() {
39
- // Get the first valid license.
40
- $addon = '';
41
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
42
- foreach ( $statuses as $key => $value ) {
43
- // If we're looking at a license status key...
44
- if ( strpos($key, '_license_status') !== FALSE ) {
45
- // ...and the license is valid...
46
- if ($value === 'valid') {
47
- $addon = substr( $key, 0, strpos( $key, '_license_status' ) );
48
- break;
49
- }
50
- }
51
- }
52
-
53
- // If we didn't find an add-on with a valid license, show the free help text.
54
- if ( $addon === '' ) {
55
- wprss_free_help_display();
56
- return;
57
- }
58
-
59
- // Get the full license info so we can prefill the name and email
60
- $license = wprss_edd_check_license($addon, NULL, 'ALL');
61
- $customer_name = is_object($license) ? $license->customer_name : '';
62
- $customer_email = is_object($license) ? $license->customer_email : '';
63
-
64
- echo '<h3>' . __( 'Email Support', WPRSS_TEXT_DOMAIN ) . '</h3>';
65
- echo wpautop( __( "If you still can't find an answer to your query after reading the documentation and going through the FAQ, just fill out the support request form below. We'll be happy to help you out.", WPRSS_TEXT_DOMAIN ) );
66
-
67
- ?>
68
-
69
- <form method="post">
70
- <table>
71
- <tr>
72
- <td><strong><?php _e('From: ', WPRSS_TEXT_DOMAIN); ?></strong></td>
73
- <td><input type='text' name='support-name' value="<?php echo esc_attr($customer_name); ?>" placeholder='<?php echo esc_attr('Name', WPRSS_TEXT_DOMAIN); ?>' style='width:100%;'></td>
74
- <td><input type='text' name='support-email' value="<?php echo esc_attr($customer_email); ?>" placeholder='<?php echo esc_attr('Email', WPRSS_TEXT_DOMAIN); ?>' style='width:100%;'></td>
75
- </tr>
76
- <tr>
77
- <td colspan="3" style="text-align:right;"><small><?php _e('Replies will be sent to this email address.'); ?></small></td>
78
- </tr>
79
- <tr>
80
- <td colspan="3"><input type='text' name='support-subject' placeholder='<?php echo esc_attr('Subject', WPRSS_TEXT_DOMAIN); ?>' style='width:100%;'></td>
81
- </tr>
82
- <tr>
83
- <td colspan="3"><textarea name='support-message' rows='10' cols='80' placeholder='<?php echo esc_attr('Message', WPRSS_TEXT_DOMAIN); ?>'></textarea></td>
84
- </tr>
85
- <tr>
86
- <td colspan="3"><strong><?php _e('Attachments', WPRSS_TEXT_DOMAIN);?>: </strong></td>
87
- </tr>
88
- <tr>
89
- <td colspan="3"><input type='checkbox' name='support-include-log' value='checked' checked><?php _e('WP RSS Aggregator log file', WPRSS_TEXT_DOMAIN); ?></td>
90
- </tr>
91
- <tr>
92
- <td colspan="3"><input type='checkbox' name='support-include-sys' value='checked' checked><?php _e('WordPress information', WPRSS_TEXT_DOMAIN); ?></td>
93
- </tr>
94
- </table>
95
- </form>
96
- <div style='line-height:2.3em;'>
97
- <button id='send-message-btn' class='button button-primary'><?php _e('Send Message', WPRSS_TEXT_DOMAIN); ?></button>
98
- <span id='support-error'></span>
99
- </div>
100
-
101
- <?php
102
- }
103
-
104
- /**
105
- * Print the free help section with link to forums.
106
- *
107
- * @since 4.7
108
- */
109
- function wprss_free_help_display() {
110
- echo '<h3>' . __( 'Support Forums', WPRSS_TEXT_DOMAIN ) . '</h3>';
111
- echo wpautop( __( "Users of the free version of WP RSS Aggregator can ask questions on the " . '<a href="http://wordpress.org/support/plugin/wp-rss-aggregator">support forum</a>.', WPRSS_TEXT_DOMAIN ) );
112
- }
113
-
114
-
115
- add_action( 'wp_ajax_wprss_ajax_send_premium_support', 'wprss_ajax_send_premium_support' );
116
- /**
117
- * Handles the AJAX request to send the support form. Returns a JSON status.
118
- *
119
- * @since 4.7
120
- */
121
- function wprss_ajax_send_premium_support() {
122
- $ret = array();
123
-
124
- // Validate the form fields that were submitted and send any errors.
125
- $error = wprss_validate_support_request();
126
- if ($error !== FALSE) {
127
- $ret['error'] = $error;
128
- echo json_encode($ret);
129
- die();
130
- }
131
-
132
- // Create the email content.
133
- $subject = sanitize_text_field($_GET['support-subject']);
134
- $message = wprss_create_support_message();
135
- $headers = wprss_create_support_headers();
136
-
137
- // Send the email.
138
- $sent = wp_mail( "support@wprssaggregator.com", $subject, $message, $headers );
139
-
140
- // NB, the retval is a best-guess about email sending. According to the WP Codex it
141
- // doesn't mean the user received the email, it "only means that the method used
142
- // was able to process the request without any errors."
143
- if ($sent === FALSE) {
144
- $ret['error'] = sprintf(__('There was an error sending the form. Please use the <a href="%s" target="_blank">contact form on our site.</a>'), esc_attr('http://www.wprssaggregator.com/contact/'));
145
- $ret['message'] = $message;
146
- } else {
147
- $ret['status'] = 'OK';
148
- }
149
-
150
- echo json_encode($ret);
151
- die();
152
- }
153
-
154
-
155
- /**
156
- * Ensures that all support form fields have been filled out. Returns TRUE
157
- *
158
- * @since 4.7
159
- * @return FALSE when all fields are valid, or a string containing an error they aren't.
160
- */
161
- function wprss_validate_support_request() {
162
- $fields = array(
163
- 'support-name',
164
- 'support-email',
165
- 'support-subject',
166
- 'support-message'
167
- );
168
-
169
- // Ensure that each required field is present and filled out.
170
- foreach ($fields as $field) {
171
- if (!isset($_GET[$field]) || $_GET[$field] === "") {
172
-
173
- return sprintf(
174
- __('Please fill out all the fields in the form, including the <strong>%s</strong> field.', WPRSS_TEXT_DOMAIN),
175
- ucfirst(substr($field, strpos($field, '-') + 1))
176
- );
177
- }
178
- }
179
-
180
- // Ensure the email is of a valid format.
181
- if (is_email($_GET['support-email']) === FALSE) {
182
- return __('Please enter a valid email address.', WPRSS_TEXT_DOMAIN);
183
- }
184
-
185
- return FALSE;
186
- }
187
-
188
-
189
- /**
190
- * Creates and returns the support request email's message body.
191
- *
192
- * @since 4.7
193
- */
194
- function wprss_create_support_message() {
195
- // Get the WP RSS Aggregator log.
196
- $log = 'Customer did not send log';
197
- if ($_GET['support-include-log'] === 'true') {
198
- $log = wprss_get_log();
199
- }
200
-
201
- // Get the system information.
202
- $sys_info = 'Customer did not send system information';
203
- if ($_GET['support-include-sys'] === 'true') {
204
- ob_start();
205
- wprss_print_system_info();
206
- $sys_info = ob_get_contents();
207
- ob_end_clean();
208
- }
209
-
210
- // Get the license keys.
211
- $keys = json_encode( get_option( 'wprss_settings_license_keys', array() ), JSON_PRETTY_PRINT );
212
-
213
- // Get the message they entered.
214
- $message = sanitize_text_field($_GET['support-message']);
215
-
216
- // Remove any generated system data that may be present from previous form submission attempts.
217
- $idx = strpos($message, "----------------------------------------------");
218
- if ($idx !== FALSE) {
219
- $message = substr($message, 0, $idx);
220
- }
221
-
222
- // Append the generated system data.
223
- $message .= "\n\n----------------------------------------------\n";
224
- $message .= "\nLicense Information:\n" . $keys;
225
- $message .= "\n\n\nError Log:\n" . $log;
226
- $message .= "\n\n\nSystem Information:\n" . $sys_info . "\n";
227
-
228
- $message = apply_filters( 'wprss_support_message', $message );
229
-
230
- return $message;
231
- }
232
-
233
-
234
- /**
235
- * Creates and returns the support request email's headers.
236
- *
237
- * @since 4.7
238
- */
239
- function wprss_create_support_headers() {
240
- $headers = "From: no-reply@wprssaggregator.com\r\n";
241
- $headers .= "Reply-to: " . sanitize_text_field($_GET['support-name']) . " <" . sanitize_email($_GET['support-email']) . ">\r\n";
242
-
243
- $headers = apply_filters( 'wprss_support_headers', $headers );
244
-
245
- return $headers;
246
- }
247
-
248
-
249
- /**
250
- * Encapsulates features for providing inline help in the admin interface.
251
- *
252
- * The following filters are introduced:
253
- *
254
- * - `wprss_help_default_options` - The default options to be extended.
255
- *
256
- * 1. The array of options
257
- *
258
- * - `wprss_help_template_path` - The path of template retrieved by WPRSS_Help::get_template().
259
- *
260
- * 1. The path to the template.
261
- * 2. The array of variables passed.
262
- *
263
- * - `wprss_help_template_vars` - The variables for the template, received by WPRSS_Help::get_template().
264
- *
265
- * 1. The variables array.
266
- * 2. The path to the template, filtered by `wprss_help_template_path`.
267
- *
268
- * - `wprss_help_tooltip_options` - Options that are in effect when adding tooltips with WPRSS_Help::add_tooltip().
269
- * - `wprss_help_tooltip_handle_html_options` - Options that are in effect when retrieving tooltip handle HTML with WPRSS_Help::wprss_help_tooltip_handle_html_options.
270
- *
271
- *
272
- * Also, the following options are available:
273
- *
274
- * - `tooltip_id_prefix` - The HTML element ID prefix that will be used for tooltips.
275
- * - `tooltip_handle_text` - The text that will appear inside the handle HTML elements.
276
- * - `tooltip_handle_class` - The CSS class that will be assigned to tooltip handles.
277
- * - `tooltip_content_class` - The CSS class that will be assigned to tooltip content HTML elements.
278
- * - `enqueue_tooltip_content` - Whether or not content is to be enqueued, instead of being output directly.
279
- *
280
- * 1. The absolute path to the core plugin directory
281
- */
282
- class WPRSS_Help {
283
-
284
- static $_instance;
285
-
286
- protected $_options;
287
- protected $_enqueued_tooltip_content = array();
288
- protected $_tooltips = array();
289
-
290
- const OPTION_NAME = 'wprss_settings_help';
291
- const CODE_PREFIX = 'wprss_help_';
292
- const OVERRIDE_DEFAULT_PREFIX = '!';
293
- const TEXT_DOMAIN = WPRSS_TEXT_DOMAIN;
294
- const HASHING_CONCATENATOR = '|';
295
- const OPTIONS_FILTER_SUFFIX = '_options';
296
-
297
- const TOOLTIP_DATA_KEY_ID = 'id';
298
- const TOOLTIP_DATA_KEY_TEXT = 'text';
299
- const TOOLTIP_DATA_KEY_OPTIONS = 'options';
300
-
301
- /**
302
- * Retrieve the singleton instance
303
- *
304
- * @return WPRSS_Help
305
- */
306
- public static function get_instance() {
307
- if ( is_null( self::$_instance ) ) {
308
- $class_name = __CLASS__; // Late static bindings not allowed
309
- self::$_instance = new $class_name();
310
- }
311
-
312
- return self::$_instance;
313
- }
314
-
315
-
316
- public static function init() {
317
- // Actions
318
- add_action( 'admin_enqueue_scripts', array( self::get_instance(), '_admin_enqueue_scripts' ) );
319
- add_action( 'admin_footer', array( self::get_instance(), '_admin_footer' ) );
320
- }
321
-
322
-
323
- /**
324
- * Filters used:
325
- *
326
- * - `wprss_help_default_options`
327
- *
328
- * @param array $options Options that will overwrite defaults.
329
- */
330
- public function __construct( $options = array() ) {
331
- $defaults = apply_filters( 'wprss_help_default_options', array(
332
- 'tooltip_id_prefix' => 'wprss-tooltip-',
333
- 'tooltip_handle_text' => '',
334
- 'tooltip_handle_class' => 'wprss-tooltip-handle', // Used in logic to identify handle elements
335
- 'tooltip_handle_class_extra' => 'fa fa-question-circle', // Not used in logic
336
- 'tooltip_content_class' => 'wprss-tooltip-content',
337
- 'tooltip_class' => 'wprss-ui-tooltip', // Overrides default jQuery UI class
338
- 'is_enqueue_tooltip_content' => '0',
339
- 'tooltip_handle_template' => '%1$s/help-tooltip-handle.php',
340
- 'tooltip_content_template' => '%1$s/help-tooltip-content.php',
341
- 'admin_footer_js_template' => '%1$s/help-footer-js.php',
342
- 'tooltip_not_found_handle_html' => '',
343
- 'text_domain' => self::TEXT_DOMAIN
344
- ));
345
- $db_options = $this->get_options_db();
346
- $this->_set_options( $this->array_merge_recursive_distinct( $db_options, $defaults ) );
347
-
348
- $this->_construct();
349
- }
350
-
351
-
352
- /**
353
- * Used for parameter-less extension of constructor logic
354
- */
355
- protected function _construct() {
356
-
357
- }
358
-
359
-
360
- /**
361
- * Return an option value, or the whole array of internal options.
362
- * These options are a product of the defaults, the database, and anything
363
- * set later on, applied on top of eachother and overwriting in that order.
364
- *
365
- * @param null|string $key The key of the option to return.
366
- * @param null|mixed $default What to return if options with the specified key not found.
367
- * @return array|mixed|null The option value, or an array of options.
368
- */
369
- public function get_options( $key = null, $default = null ) {
370
- $options = $this->_options;
371
-
372
- if ( is_null( $key ) ) {
373
- return $options;
374
- }
375
-
376
- if( is_array( $key ) ) {
377
- return $this->array_merge_recursive_distinct( $options, $key );
378
- }
379
-
380
- return isset( $options[ $key ] ) ? $options[ $key ] : $default;
381
- }
382
-
383
-
384
- /**
385
- * Set the value of an internal option or options.
386
- * Existing options will be overwritten. New options will be added.
387
- * Database options will not be modified.
388
- *
389
- * @param string|array $key The key of the option to set, or an array of options.
390
- * @param null|mixed $value The value of the option to set.
391
- * @return WPRSS_Help This instance.
392
- */
393
- public function set_options( $key, $value = null ) {
394
- if ( is_array( $key ) ) {
395
- foreach ( $key as $_key => $_value ) {
396
- $this->_set_options( $_key, $_value );
397
- }
398
-
399
- return $this;
400
- }
401
-
402
- $this->_set_options( $key, $value );
403
- }
404
-
405
-
406
- /**
407
- * Set an option value, or all options.
408
- * In latter case completely overrides the whole options array.
409
- *
410
- * @param string|array $key The key of the option to set, or the whole options array.
411
- * @param null|mixed $value Value of the option to set.
412
- * @return WPRSS_Help This instance.
413
- */
414
- protected function _set_options( $key, $value = null ) {
415
- if ( is_array( $key ) ) {
416
- $this->_options = $key;
417
- return $this;
418
- }
419
-
420
- $this->_options[ $key ] = $value;
421
- return $this;
422
- }
423
-
424
-
425
- /**
426
- * Returns a WPRSS_Help option or options from the database.
427
- *
428
- * @param string $key The key of the option to return.
429
- * @param null|mixed $default What to return if option identified by $key is not found.
430
- * @return null|array|mixed The options or option value.
431
- */
432
- public function get_options_db( $key = null, $default = null ) {
433
- $options = (array) get_option( self::OPTION_NAME, array() );
434
-
435
- if ( is_null( $key ) ) {
436
- return $options;
437
- }
438
-
439
- return isset( $options[ $key ] ) ? $options[ $key ] : $default;
440
- }
441
-
442
-
443
- /**
444
- * Get content of a template.
445
- *
446
- * Filters used
447
- *
448
- * - `wprss_help_template_path`
449
- * - `wprss_help_template_vars`
450
- *
451
- * @param string $path Full path to the template
452
- * @param array $vars This will be passed to the template
453
- */
454
- public function get_template( $path, $vars = array() ) {
455
- $vars = (array) $vars;
456
-
457
- // Entry points
458
- $path = apply_filters( 'wprss_help_template_path', $path, $vars );
459
- $vars = apply_filters( 'wprss_help_template_vars', $vars, $path );
460
-
461
- ob_start();
462
- include($path);
463
- $content = ob_get_contents();
464
- ob_end_clean();
465
-
466
- return $content;
467
- }
468
-
469
-
470
- /**
471
- * This is called during the `admin_enqueue_scripts` action, and will
472
- * enqueue scripts needed for the backend.
473
- *
474
- * Filters used:
475
- *
476
- * - `wprss_help_admin_scripts`
477
- *
478
- * @return WPRSS_Help This instance.
479
- */
480
- public function _admin_enqueue_scripts() {
481
- $scripts = $this->apply_filters( 'admin_scripts', array(
482
- 'jquery-ui-tooltip' => array()
483
- ));
484
-
485
- foreach ( $scripts as $_handle => $_args ) {
486
- // Allows numeric array with handles as values
487
- if ( is_numeric( $_handle ) ) {
488
- $_handle = $_args;
489
- }
490
-
491
- // Allows specifying null as value to simply enqueue handle
492
- if ( empty( $_args ) ){
493
- $_args = array();
494
- }
495
-
496
- array_unshift( $_args, $_handle );
497
- call_user_func_array( 'wp_enqueue_script', $_args );
498
- }
499
-
500
- return $this;
501
- }
502
-
503
-
504
- public function _admin_footer() {
505
- $html = '';
506
- $html .= $this->get_enqueued_tooltip_content_html() . "\n";
507
- $html .= $this->get_admin_footer_js_html();
508
- $html = $this->apply_filters( 'admin_footer', $html );
509
-
510
- echo $html;
511
- }
512
-
513
-
514
- public function is_overrides_default_prefix( $string ) {
515
- return strpos( $string, self::OVERRIDE_DEFAULT_PREFIX ) === 0;
516
- }
517
-
518
-
519
- /**
520
- * @return string This class's text domain
521
- */
522
- public function get_text_domain() {
523
- return self::TEXT_DOMAIN;
524
- }
525
-
526
- /**
527
- * Format this string, replacing placeholders with values, and translate it
528
- * in the class's text domain.
529
- *
530
- * @see sprintf()
531
- * @param string $string The string to translate.
532
- * @param mixed $argN,.. Additional arguments.
533
- */
534
- public function __( $string, $argN = null ) {
535
- $args = func_get_args();
536
- $args[0] = $string = __( $string, $this->get_text_domain() );
537
-
538
- $string = call_user_func_array( 'sprintf', $args );
539
-
540
- return $string;
541
- }
542
-
543
- /**
544
- * Hashes all the given values into a single hash.
545
- * Accepts an infinite number of parameters, all of which will be first
546
- * glued together by a separator, then hashed.
547
- * Non-scalar values will be serialized.
548
- *
549
- * @param mixed $value The value to hash.
550
- * @param mixed $argN Other values to hash.
551
- * @return string The hash.
552
- */
553
- public function get_hash( $value ) {
554
- $args = func_get_args();
555
- $glue = self::HASHING_CONCATENATOR;
556
-
557
- $blob = '';
558
- foreach ( $args as $_idx => $_arg ) {
559
- $blob .= is_scalar( $_arg ) ? $_arg : serialize( $_arg );
560
- $blob .= $glue;
561
- }
562
-
563
- $blob = substr( $blob, 0, -1 );
564
-
565
- return sha1( $blob );
566
- }
567
-
568
- /**
569
- * Get the class code prefix, or the specified prefixed with it.
570
- *
571
- * @param string $string A string to prefix.
572
- * @return string The code prefix or the prefixed string.
573
- */
574
- public function get_code_prefix( $string = '' ) {
575
- return self::CODE_PREFIX . (string)$string;
576
- }
577
-
578
- /**
579
- * Optionally prefix a string with the class code prefix, unless it
580
- * contains the "!" character in the very beginning, in which case it will
581
- * simply be removed.
582
- *
583
- * @param string $string The string to consider for prefixing.
584
- * @return string The prefixed or clean string.
585
- */
586
- public function prefix( $string ) {
587
- return $this->is_overrides_default_prefix( $string )
588
- ? substr( $string, 1 )
589
- : $this->get_code_prefix( $string );
590
- }
591
-
592
- /**
593
- * Applies filters, but prefixes the filter name with 'wprss_help_',
594
- * unless '!' is specified as the first character of the filter.
595
- *
596
- * @param string $filter_name Name or "tag" of the filter.
597
- * @param mixed $subject The value to apply filters to.
598
- * @param mixed $argN,.. Additional filter arguments
599
- * @return mixed Result of filtering
600
- */
601
- public function apply_filters( $filter_name, $subject, $argN = null ) {
602
- $args = func_get_args();
603
-
604
- $args[0] = $filter_name = $this->prefix( $filter_name );
605
-
606
- return call_user_func_array( 'apply_filters', $args );
607
- }
608
-
609
-
610
- /**
611
- * Applies a filters with the specified name to the options that were
612
- * applied on top of defaults.
613
- * The name will be prefixed with the class prefix 'wprss_help_', and
614
- * suffixed with '_options'.
615
- *
616
- * @param string $filter_name Name of the filter to apply to the options
617
- * @param array $options The options to filter
618
- * @param mixed $filter_argN,.. Other filter arguments to be passed to filter
619
- */
620
- public function apply_options_filters( $filter_name, $options = array(), $filter_argN = null ) {
621
- $args = func_get_args();
622
-
623
- // Adding sufix
624
- $args[0] = $filter_name .= self::OPTIONS_FILTER_SUFFIX;
625
-
626
- // Applying defaults
627
- $args[1] = $options = $this->get_options( $options );
628
-
629
- // Entry point. Order of args is already correct.
630
- $options = call_user_func_array( array( $this, 'apply_filters' ), $args );
631
-
632
- return $options;
633
- }
634
-
635
-
636
- /**
637
- * Parses the tooltip handle template path for placeholders.
638
- *
639
- * Filters used:
640
- *
641
- * - `wprss_help_admin_footer_js_html_template`
642
- *
643
- * @param null|string $path Optional path to parse and retrieve. Default: value of the 'admin_footer_js_template' option.
644
- * @return string Path to the template.
645
- */
646
- public function get_admin_footer_js_html_template( $path = null ) {
647
- // Default is from options
648
- if ( is_null( $path ) ) {
649
- $path = $this->get_options( 'admin_footer_js_template' );
650
- }
651
-
652
- // Entry point
653
- $path = $this->apply_filters( 'admin_footer_js_html_template', $path );
654
-
655
- return $this->parse_path( $path );
656
- }
657
-
658
-
659
- /**
660
- * Get the HTML of the JavaScript for the footer in Admin Panel.
661
- *
662
- * Filters used:
663
- *
664
- * - `wprss_help_admin_footer_js_html`
665
- *
666
- * @param array $options Any additional options to be used with defaults.
667
- * @return string The HTML.
668
- */
669
- public function get_admin_footer_js_html( $options = array() ) {
670
- $options = $this->apply_options_filters( 'admin_footer_js_html', $options);
671
-
672
- $templatePath = $this->get_admin_footer_js_html_template( $options['admin_footer_js_template'] );
673
-
674
- return $this->get_template($templatePath, $options);
675
- }
676
-
677
-
678
- /**
679
- * Parses the tooltip handle template path for placeholders.
680
- *
681
- * Filters used:
682
- *
683
- * - `wprss_help_tooltip_handle_html_template`
684
- *
685
- * @param null|string $path Optional path to parse and retrieve. Default: value of the 'tooltip_handle_template' option.
686
- * @return string Path to the template.
687
- */
688
- public function get_tooltip_handle_html_template( $path = null ) {
689
- // Default is from options
690
- if ( is_null( $path ) ) {
691
- $path = $this->get_options( 'tooltip_handle_template' );
692
- }
693
-
694
- // Entry point
695
- $path = $this->apply_filters( 'tooltip_handle_html_template', $path );
696
-
697
- return $this->parse_path( $path );
698
- }
699
-
700
-
701
- /**
702
- * Get the HTML of the tooltip handle.
703
- *
704
- * Filters used:
705
- *
706
- * - `wprss_help_tooltip_handle_html_options`
707
- *
708
- * @param string $text Content of the tooltip text.
709
- * @param string $id ID of the tooltip.
710
- * @param array $options Any additional options to be used with defaults.
711
- * @return string The HTML.
712
- */
713
- public function get_tooltip_handle_html( $text, $id, $options = array() ) {
714
- $options = $this->apply_options_filters( 'tooltip_handle_html', $options, $text, $id);
715
-
716
- // Add template varialbes
717
- $options['tooltip_id'] = $id;
718
- $options['tooltip_text'] = $text;
719
-
720
- $templatePath = $this->get_tooltip_handle_html_template( $options['tooltip_handle_template'] );
721
-
722
- return $this->get_template($templatePath, $options);
723
- }
724
-
725
-
726
- /**
727
- * Parses the tooltip content template path for placeholders.
728
- *
729
- * Filters used:
730
- *
731
- * - `wprss_help_tooltip_content_html_template`
732
- *
733
- * @param null|string $path Optional path to parse and retrieve. Default: value of the 'tooltip_handle_template' option.
734
- * @return string Path to the template.
735
- */
736
- public function get_tooltip_content_html_template( $path = null ) {
737
- // Default is from options
738
- if ( is_null( $path ) ) {
739
- $path = $this->get_options( 'tooltip_content_template' );
740
- }
741
-
742
- // Entry point
743
- $path = $this->apply_filters( 'tooltip_content_html_template', $path );
744
-
745
- return $this->parse_path( $path );
746
- }
747
-
748
-
749
- /**
750
- * Get the HTML of the tooltip content.
751
- *
752
- * Filters used:
753
- *
754
- * - `wprss_help_tooltip_content_html_options`
755
- *
756
- * @param string $text Content of the tooltip text.
757
- * @param string $id ID of the tooltip.
758
- * @param array $options Any additional options to be used with defaults.
759
- * @return string The HTML.
760
- */
761
- public function get_tooltip_content_html( $text, $id, $options = array() ) {
762
- $options = $this->apply_options_filters( 'tooltip_content_html', $options, $text, $id );
763
-
764
- // Add template varialbes
765
- $options['tooltip_id'] = $id;
766
- $options['tooltip_text'] = $text;
767
-
768
- $templatePath = $this->get_tooltip_content_html_template( $options['tooltip_content_template'] );
769
-
770
- return $this->get_template( $templatePath, $options );
771
- }
772
-
773
-
774
- /**
775
- * Add tooltip and get tooltip HTML.
776
- * If $text is null, just get the HTML of tooltip with specified ID.
777
- * The `is_enqueue_tooltip_content` option determines whether to enqueue
778
- * the content, instead of outputting it after the handle.
779
- *
780
- * @param string $id ID for this tooltip
781
- * @param string|null $text Text of this tooltip. If null, tooltip will not be added, but only retrieved.
782
- * @param array|bool $options The options for this operation, or a boolean indicating whether or not content is to be enqueued
783
- * @return string The tooltip handle and, optionally, content.
784
- */
785
- public function tooltip( $id, $text = null, $options = array() ) {
786
- $this->add_tooltip( $id, $text, $options );
787
- return $this->do_tooltip( $id );
788
- }
789
-
790
-
791
- /**
792
- * Add tooltips in a batch, with optionally prefixed ID.
793
- *
794
- * @param array $tooltips An array where key is tooltip ID and value is tooltip text.
795
- * @param string $prefix A prefix to add to all tooltip IDs.
796
- * @param array $options Arra of options for all the tooltips to add.
797
- * @return \WPRSS_Help
798
- */
799
- public function add_tooltips( $tooltips, $prefix = null, $options = array() ) {
800
- $prefix = (string) $prefix;
801
- if ( !is_array($options) ) $options = array();
802
-
803
- foreach ( $tooltips as $_id => $_text ) {
804
- $this->add_tooltip( $prefix . $_id, $_text, $options );
805
- }
806
-
807
- return $this;
808
- }
809
-
810
-
811
- /**
812
- * Add a tooltip for later display.
813
- * Text and options will be replaced by existing text and options, if they
814
- * are empty, and a tooltip with the same ID is already registered.
815
- *
816
- * @param string $id The ID of this tooltip
817
- * @param string $text Text for this tooltip
818
- * @param array $options Options for this tooltip.
819
- * @return WPRSS_Help This instance.
820
- */
821
- public function add_tooltip( $id, $text = null, $options = array() ) {
822
- if ( $tooltip = $this->get_tooltip( $id ) ) {
823
- if ( is_null( $text ) ) $text = isset( $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] : $text;
824
- if ( empty( $options ) ) $options = isset( $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] : $options;
825
- }
826
-
827
- $this->set_tooltip( $id, $text, $options );
828
-
829
- return $this;
830
- }
831
-
832
-
833
- /**
834
- * Set a tooltip, existing or not.
835
- *
836
- * @param string $id The ID of this tooltip
837
- * @param string $text Text for this tooltip
838
- * @param array $options Options for this tooltip.
839
- * @return WPRSS_Help This instance.
840
- */
841
- public function set_tooltip( $id, $text = null, $options = array() ) {
842
- $this->_tooltips[ $id ] = array(
843
- self::TOOLTIP_DATA_KEY_ID => $id,
844
- self::TOOLTIP_DATA_KEY_TEXT => $text,
845
- self::TOOLTIP_DATA_KEY_OPTIONS => $options
846
- );
847
-
848
- return $this;
849
- }
850
-
851
-
852
- /**
853
- * Retrieve one tooltip, or an array containing all tooltips.
854
- *
855
- * @param string|null $id The ID of the tooltip to retrieve.
856
- * @param mixed|null $default What to return if tooltip with specified ID not found.
857
- * @return array An array that contains the following indexes: 'id', 'text', 'options'. See {@link add_tooltip()} for details.
858
- */
859
- public function get_tooltip( $id = null, $default = null ) {
860
- if ( is_null( $id ) ) {
861
- return $this->_tooltips;
862
- }
863
-
864
- return $this->has_tooltip( $id ) ? $this->_tooltips[ $id ] : $default;
865
- }
866
-
867
-
868
- /**
869
- * Check whether a tooltip with the specified ID exists.
870
- *
871
- * @param string $id ID of the tooltip to check for.
872
- * @return boolean True if a tooltip with the specified ID exists; false otherwise.
873
- */
874
- public function has_tooltip( $id ) {
875
- return isset( $this->_tooltips[ $id ] );
876
- }
877
-
878
- /**
879
- * Get registered tooltip HTML.
880
- *
881
- * Filters used:
882
- *
883
- * - `wprss_help_tooltip_options` - Filters options used for tooltip
884
- *
885
- * @param string $id ID for this tooltip
886
- * @param string $text Text of this tooltip
887
- * @param array|bool $options The options for this operation, or a boolean indicating whether or not content is to be enqueued
888
- * @return string The tooltip handle and, optionally, content.
889
- */
890
- public function do_tooltip( $id ) {
891
- $options = $this->get_options();
892
-
893
- if ( !($tooltip = $this->get_tooltip( $id )) || !isset($tooltip[ self::TOOLTIP_DATA_KEY_TEXT ]) || !$tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) {
894
- return isset( $options['tooltip_not_found_handle_html'] )
895
- ? $options['tooltip_not_found_handle_html']
896
- : null;
897
- }
898
-
899
- $options = isset( $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] : null;
900
- $text = isset( $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] : null;
901
-
902
- if ( !is_array( $options ) ) {
903
- $options = array( 'is_enqueue_tooltip_content' => $options );
904
- }
905
-
906
- // Entry point
907
- $options = $this->apply_options_filters( 'tooltip', $options, $id, $text );
908
-
909
- // Get handle HTML
910
- $output = $this->get_tooltip_handle_html( $text, $id, $options );
911
-
912
- if ( $this->evaluate_boolean( $options['is_enqueue_tooltip_content'] ) ) {
913
- $this->enqueue_tooltip_content($text, $id, $options);
914
- }
915
- else {
916
- $output .= $this->get_tooltip_content_html( $text, $id, $options );
917
- }
918
-
919
- return $output;
920
- }
921
-
922
-
923
- /**
924
- * Enqueue tooltip content to be displayed in another part of the page.
925
- *
926
- * @param string $text The text of the tooltip content to enqueue.
927
- * @param string $id ID of the tooltip, the content of which to enqueue.
928
- * @param array $options This tooltip's options.
929
- * @return \WP_Error|\WPRSS_Help This instance, or error if enqueue method is invalid.
930
- */
931
- public function enqueue_tooltip_content( $text, $id, $options = array() ) {
932
- $queue_method = $this->apply_filters( 'enqueue_tooltip_content_method', array( $this, '_enqueue_tooltip_content' ), $options, $id, $text );
933
-
934
- // "Error handling" WP style
935
- if ( !is_callable( $queue_method ) ) {
936
- return new WP_Error( $this->prefix( 'invalid_queue_method' ), $this->__( 'Could not enqueue tooltip content: the queue method is not a valid callable.' ), array(
937
- 'queue_method' => $queue_method,
938
- 'text' => $text,
939
- 'id' => $id,
940
- 'options' => $options
941
- ));
942
- }
943
-
944
- call_user_func_array( $queue_method, array( $text, $id, $options ) );
945
-
946
- return $this;
947
- }
948
-
949
-
950
- public function _enqueue_tooltip_content( $text, $id, $options = array() ) {
951
- $hash = $this->get_hash( $text, $id, $options );
952
- $this->_enqueued_tooltip_content[ $hash ] = array(
953
- self::TOOLTIP_DATA_KEY_TEXT => $text,
954
- self::TOOLTIP_DATA_KEY_ID => $id,
955
- self::TOOLTIP_DATA_KEY_OPTIONS => $options
956
- );
957
-
958
- return $this;
959
- }
960
-
961
-
962
- public function get_enqueued_tooltip_content() {
963
- return $this->_enqueued_tooltip_content;
964
- }
965
-
966
-
967
- public function get_enqueued_tooltip_content_html() {
968
- $output = '';
969
- foreach ( $this->get_enqueued_tooltip_content() as $_hash => $_vars ) {
970
- $options = is_array( $_vars[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $_vars[ self::TOOLTIP_DATA_KEY_OPTIONS ] : array();
971
- $output = $this->get_tooltip_content_html( $_vars[ self::TOOLTIP_DATA_KEY_ID ], $_vars[ self::TOOLTIP_DATA_KEY_ID ], $options );
972
- }
973
-
974
- echo $output;
975
- }
976
-
977
-
978
- /**
979
- * Check whether or not the given value is false.
980
- * False values are all {@link empty()} values, and also strings 'false' and 'no'.
981
- *
982
- * @param mixed $value The value to check.
983
- * @return boolean Whether or not the value is considered to be false.
984
- */
985
- public function evaluate_boolean( $value ) {
986
- return (empty( $value ) || strtolower( $value ) === 'false' || strtolower( $value ) === 'no')
987
- ? false
988
- : true;
989
- }
990
-
991
-
992
- /**
993
- * Merge two arrays in an intuitive way.
994
- * Input arrays remain unchanged.
995
- *
996
- * @see http://php.net/manual/en/function.array-merge-recursive.php#92195
997
- * @param array $array1 The array to merge.
998
- * @param array $array2 The array to merge into.
999
- * @return array The merged array.
1000
- */
1001
- public function array_merge_recursive_distinct( array &$array1, array &$array2 ) {
1002
- $merged = $array1;
1003
-
1004
- foreach ( $array2 as $key => &$value ) {
1005
- if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
1006
- $merged[ $key ] = array_merge_recursive_distinct( $merged[ $key ], $value );
1007
- } else {
1008
- $merged[ $key ] = $value;
1009
- }
1010
- }
1011
-
1012
- return $merged;
1013
- }
1014
-
1015
-
1016
- /**
1017
- * Converts an array to a numeric array.
1018
- * If $map is empty, assumes that the array keys are already in order.
1019
- * If $map is a number, assumes it's the amount of elements to return.
1020
- * If $map is an array, assumes it is the map of intended numeric indexes to their value in the input array.
1021
- *
1022
- * @param array $array The array to convert to a numeric array
1023
- * @param false|null|array $map The map of the array indexes, or number of array elements to slice, or nothing.
1024
- * @return array The resulting numeric array.
1025
- */
1026
- public function array_to_numeric( $array, $map = null ) {
1027
- $result = array();
1028
-
1029
- // If map is not an array, assume it's an indicator
1030
- if ( !is_array( $map ) ) {
1031
- $array = array_values( $array );
1032
- }
1033
-
1034
- // If map is empty, assume keys are in order
1035
- if ( empty( $map ) ) {
1036
- return $array;
1037
- }
1038
-
1039
- // If map is a number, assume it's the amount of elements to return
1040
- if ( is_numeric( $map ) ) {
1041
- $map = intval( $map );
1042
- return array_slice( $array, 0, $map );
1043
- }
1044
-
1045
- foreach( $map as $_idx => $_key ) {
1046
- $result[ $_idx ] = $array[ $_key ];
1047
- }
1048
-
1049
- return $result;
1050
- }
1051
-
1052
-
1053
- /**
1054
- * Parses the template and replaces placeholders with their values.
1055
- * This function uses {@see sprintf()} to format the template string using
1056
- * the values provided in $data.
1057
- * It is also possible for $data to be an associative array of key-value pairs.
1058
- * To achieve the same result, a map can be provided, mapping data keys to
1059
- * their placeholder positions.
1060
- * If no map is provided,
1061
- *
1062
- * @param string $string The template string.
1063
- * @param array $data The key-value pairs of template data.
1064
- * @param false|null|array $map {@see array_to_numeric()} The template value map.
1065
- * @return string The parsed and modified template.
1066
- */
1067
- public function parse_template( $string, $data, $map = null ) {
1068
- $data = $this->array_to_numeric( $data, $map );
1069
- array_unshift( $data, $string );
1070
- return call_user_func_array( 'sprintf', $data );
1071
- }
1072
-
1073
-
1074
- /**
1075
- * Parses a path template specifically with WPRSS_Help path placeholders.
1076
- *
1077
- * Filters used (in order):
1078
- *
1079
- * 1. `parse_path_data_default`;
1080
- * 2. `parse_path_data`;
1081
- * 3. `parse_path_map`;
1082
- * 4. `parse_path_path`.
1083
- *
1084
- * @see WPRSS_Help::parse_template()
1085
- * @param string $path The path to parse.
1086
- * @param null|array $data Any additional data. Will be merged with defaults.
1087
- * @param null|array $map The map for parsing.
1088
- * @return string The path with placeholders replaced
1089
- */
1090
- public function parse_path( $path, $data = null, $map = null ) {
1091
- if( is_null( $data ) ) {
1092
- $data = array();
1093
- }
1094
-
1095
- $defaults = $this->apply_filters( 'parse_path_data_default', array(
1096
- 'wprss_templates_dir' => wprss_get_templates_dir()
1097
- ));
1098
- $data = $this->array_merge_recursive_distinct( $data, $defaults );
1099
- $data = $this->apply_filters( 'parse_path_data', $data, $path, $map );
1100
- $map = $this->apply_filters( 'parse_path_map', $map, $data, $path );
1101
- $path = $this->apply_filters( 'parse_path_path', $path, $data, $map );
1102
-
1103
- return $this->parse_template( $path, $data, $map );
1104
- }
1105
- }
1106
-
1107
- WPRSS_Help::init();
1
+ <?php
2
+ /**
3
+ * Build the Help page
4
+ *
5
+ * @since 4.2
6
+ */
7
+ function wprss_help_page_display() {
8
+ ?>
9
+
10
+ <div class="wrap">
11
+ <?php screen_icon( 'wprss-aggregator' ); ?>
12
+
13
+ <h2><?php _e( 'Help & Support', WPRSS_TEXT_DOMAIN ); ?></h2>
14
+ <h3><?php _e( 'Documentation', WPRSS_TEXT_DOMAIN ) ?></h3>
15
+ <?php echo wpautop( __('In the <a href="http://www.wprssaggregator.com/documentation/">documentation area</a> on the WP RSS Aggregator website you will find comprehensive details on how to use the core plugin and all the add-ons.
16
+
17
+ There are also some videos to help you make a quick start to setting up and enjoying this plugin.', WPRSS_TEXT_DOMAIN) ) ?>
18
+ <h3><?php _e( 'Frequently Asked Questions (FAQ)', WPRSS_TEXT_DOMAIN ) ?></h3>
19
+ <?php echo wpautop( __('If after going through the documentation you still have questions, please take a look at the <a href="http://www.wprssaggregator.com/faq/">FAQ page</a> on the site, we set this up purposely to answer the most commonly asked questions by our users.', WPRSS_TEXT_DOMAIN) ) ?>
20
+
21
+ <?php
22
+ if ( wprss_is_premium_user() ) {
23
+ wprss_premium_help_display();
24
+ } else {
25
+ wprss_free_help_display();
26
+ }
27
+ ?>
28
+ </div>
29
+ <?php
30
+ }
31
+
32
+
33
+ /**
34
+ * Print the premium help section with inline support form.
35
+ *
36
+ * @since 4.7
37
+ */
38
+ function wprss_premium_help_display() {
39
+ // Get the first valid license.
40
+ $addon = '';
41
+ $statuses = get_option( 'wprss_settings_license_statuses', array() );
42
+ foreach ( $statuses as $key => $value ) {
43
+ // If we're looking at a license status key...
44
+ if ( strpos($key, '_license_status') !== FALSE ) {
45
+ // ...and the license is valid...
46
+ if ($value === 'valid') {
47
+ $addon = substr( $key, 0, strpos( $key, '_license_status' ) );
48
+ break;
49
+ }
50
+ }
51
+ }
52
+
53
+ // If we didn't find an add-on with a valid license, show the free help text.
54
+ if ( $addon === '' ) {
55
+ wprss_free_help_display();
56
+ return;
57
+ }
58
+
59
+ // Get the full license info so we can prefill the name and email
60
+ $license = wprss_edd_check_license($addon, NULL, 'ALL');
61
+ $customer_name = is_object($license) ? $license->customer_name : '';
62
+ $customer_email = is_object($license) ? $license->customer_email : '';
63
+
64
+ echo '<h3>' . __( 'Email Support', WPRSS_TEXT_DOMAIN ) . '</h3>';
65
+ echo wpautop( __( "If you still can't find an answer to your query after reading the documentation and going through the FAQ, just fill out the support request form below. We'll be happy to help you out.", WPRSS_TEXT_DOMAIN ) );
66
+
67
+ ?>
68
+
69
+ <form method="post">
70
+ <table>
71
+ <tr>
72
+ <td><strong><?php _e('From: ', WPRSS_TEXT_DOMAIN); ?></strong></td>
73
+ <td><input type='text' name='support-name' value="<?php echo esc_attr($customer_name); ?>" placeholder='<?php echo esc_attr('Name', WPRSS_TEXT_DOMAIN); ?>' style='width:100%;'></td>
74
+ <td><input type='text' name='support-email' value="<?php echo esc_attr($customer_email); ?>" placeholder='<?php echo esc_attr('Email', WPRSS_TEXT_DOMAIN); ?>' style='width:100%;'></td>
75
+ </tr>
76
+ <tr>
77
+ <td colspan="3" style="text-align:right;"><small><?php _e('Replies will be sent to this email address.'); ?></small></td>
78
+ </tr>
79
+ <tr>
80
+ <td colspan="3"><input type='text' name='support-subject' placeholder='<?php echo esc_attr('Subject', WPRSS_TEXT_DOMAIN); ?>' style='width:100%;'></td>
81
+ </tr>
82
+ <tr>
83
+ <td colspan="3"><textarea name='support-message' rows='10' cols='80' placeholder='<?php echo esc_attr('Message', WPRSS_TEXT_DOMAIN); ?>'></textarea></td>
84
+ </tr>
85
+ <tr>
86
+ <td colspan="3"><strong><?php _e('Attachments', WPRSS_TEXT_DOMAIN);?>: </strong></td>
87
+ </tr>
88
+ <tr>
89
+ <td colspan="3"><input type='checkbox' name='support-include-log' value='checked' checked><?php _e('WP RSS Aggregator log file', WPRSS_TEXT_DOMAIN); ?></td>
90
+ </tr>
91
+ <tr>
92
+ <td colspan="3"><input type='checkbox' name='support-include-sys' value='checked' checked><?php _e('WordPress information', WPRSS_TEXT_DOMAIN); ?></td>
93
+ </tr>
94
+ </table>
95
+ </form>
96
+ <div style='line-height:2.3em;'>
97
+ <button id='send-message-btn' class='button button-primary'><?php _e('Send Message', WPRSS_TEXT_DOMAIN); ?></button>
98
+ <span id='support-error'></span>
99
+ </div>
100
+
101
+ <?php
102
+ }
103
+
104
+ /**
105
+ * Print the free help section with link to forums.
106
+ *
107
+ * @since 4.7
108
+ */
109
+ function wprss_free_help_display() {
110
+ echo '<h3>' . __( 'Support Forums', WPRSS_TEXT_DOMAIN ) . '</h3>';
111
+ echo wpautop( __( "Users of the free version of WP RSS Aggregator can ask questions on the " . '<a href="http://wordpress.org/support/plugin/wp-rss-aggregator">support forum</a>.', WPRSS_TEXT_DOMAIN ) );
112
+ }
113
+
114
+
115
+ add_action( 'wp_ajax_wprss_ajax_send_premium_support', 'wprss_ajax_send_premium_support' );
116
+ /**
117
+ * Handles the AJAX request to send the support form. Returns a JSON status.
118
+ *
119
+ * @since 4.7
120
+ */
121
+ function wprss_ajax_send_premium_support() {
122
+ $ret = array();
123
+
124
+ // Validate the form fields that were submitted and send any errors.
125
+ $error = wprss_validate_support_request();
126
+ if ($error !== FALSE) {
127
+ $ret['error'] = $error;
128
+ echo json_encode($ret);
129
+ die();
130
+ }
131
+
132
+ // Create the email content.
133
+ $subject = sanitize_text_field($_GET['support-subject']);
134
+ $message = wprss_create_support_message();
135
+ $headers = wprss_create_support_headers();
136
+
137
+ // Send the email.
138
+ $sent = wp_mail( "support@wprssaggregator.com", $subject, $message, $headers );
139
+
140
+ // NB, the retval is a best-guess about email sending. According to the WP Codex it
141
+ // doesn't mean the user received the email, it "only means that the method used
142
+ // was able to process the request without any errors."
143
+ if ($sent === FALSE) {
144
+ $ret['error'] = sprintf(__('There was an error sending the form. Please use the <a href="%s" target="_blank">contact form on our site.</a>'), esc_attr('http://www.wprssaggregator.com/contact/'));
145
+ $ret['message'] = $message;
146
+ } else {
147
+ $ret['status'] = 'OK';
148
+ }
149
+
150
+ echo json_encode($ret);
151
+ die();
152
+ }
153
+
154
+
155
+ /**
156
+ * Ensures that all support form fields have been filled out. Returns TRUE
157
+ *
158
+ * @since 4.7
159
+ * @return FALSE when all fields are valid, or a string containing an error they aren't.
160
+ */
161
+ function wprss_validate_support_request() {
162
+ $fields = array(
163
+ 'support-name',
164
+ 'support-email',
165
+ 'support-subject',
166
+ 'support-message'
167
+ );
168
+
169
+ // Ensure that each required field is present and filled out.
170
+ foreach ($fields as $field) {
171
+ if (!isset($_GET[$field]) || $_GET[$field] === "") {
172
+
173
+ return sprintf(
174
+ __('Please fill out all the fields in the form, including the <strong>%s</strong> field.', WPRSS_TEXT_DOMAIN),
175
+ ucfirst(substr($field, strpos($field, '-') + 1))
176
+ );
177
+ }
178
+ }
179
+
180
+ // Ensure the email is of a valid format.
181
+ if (is_email($_GET['support-email']) === FALSE) {
182
+ return __('Please enter a valid email address.', WPRSS_TEXT_DOMAIN);
183
+ }
184
+
185
+ return FALSE;
186
+ }
187
+
188
+
189
+ /**
190
+ * Creates and returns the support request email's message body.
191
+ *
192
+ * @since 4.7
193
+ */
194
+ function wprss_create_support_message() {
195
+ // Get the WP RSS Aggregator log.
196
+ $log = 'Customer did not send log';
197
+ if ($_GET['support-include-log'] === 'true') {
198
+ $log = wprss_get_log();
199
+ }
200
+
201
+ // Get the system information.
202
+ $sys_info = 'Customer did not send system information';
203
+ if ($_GET['support-include-sys'] === 'true') {
204
+ ob_start();
205
+ wprss_print_system_info();
206
+ $sys_info = ob_get_contents();
207
+ ob_end_clean();
208
+ }
209
+
210
+ // Get the license keys.
211
+ $keys = json_encode( get_option( 'wprss_settings_license_keys', array() ), JSON_PRETTY_PRINT );
212
+
213
+ // Get the message they entered.
214
+ $message = sanitize_text_field($_GET['support-message']);
215
+
216
+ // Remove any generated system data that may be present from previous form submission attempts.
217
+ $idx = strpos($message, "----------------------------------------------");
218
+ if ($idx !== FALSE) {
219
+ $message = substr($message, 0, $idx);
220
+ }
221
+
222
+ // Append the generated system data.
223
+ $message .= "\n\n----------------------------------------------\n";
224
+ $message .= "\nLicense Information:\n" . $keys;
225
+ $message .= "\n\n\nError Log:\n" . $log;
226
+ $message .= "\n\n\nSystem Information:\n" . $sys_info . "\n";
227
+
228
+ $message = apply_filters( 'wprss_support_message', $message );
229
+
230
+ return $message;
231
+ }
232
+
233
+
234
+ /**
235
+ * Creates and returns the support request email's headers.
236
+ *
237
+ * @since 4.7
238
+ */
239
+ function wprss_create_support_headers() {
240
+ $headers = "From: no-reply@wprssaggregator.com\r\n";
241
+ $headers .= "Reply-to: " . sanitize_text_field($_GET['support-name']) . " <" . sanitize_email($_GET['support-email']) . ">\r\n";
242
+
243
+ $headers = apply_filters( 'wprss_support_headers', $headers );
244
+
245
+ return $headers;
246
+ }
247
+
248
+
249
+ /**
250
+ * Encapsulates features for providing inline help in the admin interface.
251
+ *
252
+ * The following filters are introduced:
253
+ *
254
+ * - `wprss_help_default_options` - The default options to be extended.
255
+ *
256
+ * 1. The array of options
257
+ *
258
+ * - `wprss_help_template_path` - The path of template retrieved by WPRSS_Help::get_template().
259
+ *
260
+ * 1. The path to the template.
261
+ * 2. The array of variables passed.
262
+ *
263
+ * - `wprss_help_template_vars` - The variables for the template, received by WPRSS_Help::get_template().
264
+ *
265
+ * 1. The variables array.
266
+ * 2. The path to the template, filtered by `wprss_help_template_path`.
267
+ *
268
+ * - `wprss_help_tooltip_options` - Options that are in effect when adding tooltips with WPRSS_Help::add_tooltip().
269
+ * - `wprss_help_tooltip_handle_html_options` - Options that are in effect when retrieving tooltip handle HTML with WPRSS_Help::wprss_help_tooltip_handle_html_options.
270
+ *
271
+ *
272
+ * Also, the following options are available:
273
+ *
274
+ * - `tooltip_id_prefix` - The HTML element ID prefix that will be used for tooltips.
275
+ * - `tooltip_handle_text` - The text that will appear inside the handle HTML elements.
276
+ * - `tooltip_handle_class` - The CSS class that will be assigned to tooltip handles.
277
+ * - `tooltip_content_class` - The CSS class that will be assigned to tooltip content HTML elements.
278
+ * - `enqueue_tooltip_content` - Whether or not content is to be enqueued, instead of being output directly.
279
+ *
280
+ * 1. The absolute path to the core plugin directory
281
+ */
282
+ class WPRSS_Help {
283
+
284
+ static $_instance;
285
+
286
+ protected $_options;
287
+ protected $_enqueued_tooltip_content = array();
288
+ protected $_tooltips = array();
289
+
290
+ const OPTION_NAME = 'wprss_settings_help';
291
+ const CODE_PREFIX = 'wprss_help_';
292
+ const OVERRIDE_DEFAULT_PREFIX = '!';
293
+ const TEXT_DOMAIN = WPRSS_TEXT_DOMAIN;
294
+ const HASHING_CONCATENATOR = '|';
295
+ const OPTIONS_FILTER_SUFFIX = '_options';
296
+
297
+ const TOOLTIP_DATA_KEY_ID = 'id';
298
+ const TOOLTIP_DATA_KEY_TEXT = 'text';
299
+ const TOOLTIP_DATA_KEY_OPTIONS = 'options';
300
+
301
+ /**
302
+ * Retrieve the singleton instance
303
+ *
304
+ * @return WPRSS_Help
305
+ */
306
+ public static function get_instance() {
307
+ if ( is_null( self::$_instance ) ) {
308
+ $class_name = __CLASS__; // Late static bindings not allowed
309
+ self::$_instance = new $class_name();
310
+ }
311
+
312
+ return self::$_instance;
313
+ }
314
+
315
+
316
+ public static function init() {
317
+ // Actions
318
+ add_action( 'admin_enqueue_scripts', array( self::get_instance(), '_admin_enqueue_scripts' ) );
319
+ add_action( 'admin_footer', array( self::get_instance(), '_admin_footer' ) );
320
+ }
321
+
322
+
323
+ /**
324
+ * Filters used:
325
+ *
326
+ * - `wprss_help_default_options`
327
+ *
328
+ * @param array $options Options that will overwrite defaults.
329
+ */
330
+ public function __construct( $options = array() ) {
331
+ $defaults = apply_filters( 'wprss_help_default_options', array(
332
+ 'tooltip_id_prefix' => 'wprss-tooltip-',
333
+ 'tooltip_handle_text' => '',
334
+ 'tooltip_handle_class' => 'wprss-tooltip-handle', // Used in logic to identify handle elements
335
+ 'tooltip_handle_class_extra' => 'fa fa-question-circle', // Not used in logic
336
+ 'tooltip_content_class' => 'wprss-tooltip-content',
337
+ 'tooltip_class' => 'wprss-ui-tooltip', // Overrides default jQuery UI class
338
+ 'is_enqueue_tooltip_content' => '0',
339
+ 'tooltip_handle_template' => '%1$s/help-tooltip-handle.php',
340
+ 'tooltip_content_template' => '%1$s/help-tooltip-content.php',
341
+ 'admin_footer_js_template' => '%1$s/help-footer-js.php',
342
+ 'tooltip_not_found_handle_html' => '',
343
+ 'text_domain' => self::TEXT_DOMAIN
344
+ ));
345
+ $db_options = $this->get_options_db();
346
+ $this->_set_options( $this->array_merge_recursive_distinct( $db_options, $defaults ) );
347
+
348
+ $this->_construct();
349
+ }
350
+
351
+
352
+ /**
353
+ * Used for parameter-less extension of constructor logic
354
+ */
355
+ protected function _construct() {
356
+
357
+ }
358
+
359
+
360
+ /**
361
+ * Return an option value, or the whole array of internal options.
362
+ * These options are a product of the defaults, the database, and anything
363
+ * set later on, applied on top of eachother and overwriting in that order.
364
+ *
365
+ * @param null|string $key The key of the option to return.
366
+ * @param null|mixed $default What to return if options with the specified key not found.
367
+ * @return array|mixed|null The option value, or an array of options.
368
+ */
369
+ public function get_options( $key = null, $default = null ) {
370
+ $options = $this->_options;
371
+
372
+ if ( is_null( $key ) ) {
373
+ return $options;
374
+ }
375
+
376
+ if( is_array( $key ) ) {
377
+ return $this->array_merge_recursive_distinct( $options, $key );
378
+ }
379
+
380
+ return isset( $options[ $key ] ) ? $options[ $key ] : $default;
381
+ }
382
+
383
+
384
+ /**
385
+ * Set the value of an internal option or options.
386
+ * Existing options will be overwritten. New options will be added.
387
+ * Database options will not be modified.
388
+ *
389
+ * @param string|array $key The key of the option to set, or an array of options.
390
+ * @param null|mixed $value The value of the option to set.
391
+ * @return WPRSS_Help This instance.
392
+ */
393
+ public function set_options( $key, $value = null ) {
394
+ if ( is_array( $key ) ) {
395
+ foreach ( $key as $_key => $_value ) {
396
+ $this->_set_options( $_key, $_value );
397
+ }
398
+
399
+ return $this;
400
+ }
401
+
402
+ $this->_set_options( $key, $value );
403
+ }
404
+
405
+
406
+ /**
407
+ * Set an option value, or all options.
408
+ * In latter case completely overrides the whole options array.
409
+ *
410
+ * @param string|array $key The key of the option to set, or the whole options array.
411
+ * @param null|mixed $value Value of the option to set.
412
+ * @return WPRSS_Help This instance.
413
+ */
414
+ protected function _set_options( $key, $value = null ) {
415
+ if ( is_array( $key ) ) {
416
+ $this->_options = $key;
417
+ return $this;
418
+ }
419
+
420
+ $this->_options[ $key ] = $value;
421
+ return $this;
422
+ }
423
+
424
+
425
+ /**
426
+ * Returns a WPRSS_Help option or options from the database.
427
+ *
428
+ * @param string $key The key of the option to return.
429
+ * @param null|mixed $default What to return if option identified by $key is not found.
430
+ * @return null|array|mixed The options or option value.
431
+ */
432
+ public function get_options_db( $key = null, $default = null ) {
433
+ $options = (array) get_option( self::OPTION_NAME, array() );
434
+
435
+ if ( is_null( $key ) ) {
436
+ return $options;
437
+ }
438
+
439
+ return isset( $options[ $key ] ) ? $options[ $key ] : $default;
440
+ }
441
+
442
+
443
+ /**
444
+ * Get content of a template.
445
+ *
446
+ * Filters used
447
+ *
448
+ * - `wprss_help_template_path`
449
+ * - `wprss_help_template_vars`
450
+ *
451
+ * @param string $path Full path to the template
452
+ * @param array $vars This will be passed to the template
453
+ */
454
+ public function get_template( $path, $vars = array() ) {
455
+ $vars = (array) $vars;
456
+
457
+ // Entry points
458
+ $path = apply_filters( 'wprss_help_template_path', $path, $vars );
459
+ $vars = apply_filters( 'wprss_help_template_vars', $vars, $path );
460
+
461
+ ob_start();
462
+ include($path);
463
+ $content = ob_get_contents();
464
+ ob_end_clean();
465
+
466
+ return $content;
467
+ }
468
+
469
+
470
+ /**
471
+ * This is called during the `admin_enqueue_scripts` action, and will
472
+ * enqueue scripts needed for the backend.
473
+ *
474
+ * Filters used:
475
+ *
476
+ * - `wprss_help_admin_scripts`
477
+ *
478
+ * @return WPRSS_Help This instance.
479
+ */
480
+ public function _admin_enqueue_scripts() {
481
+ $scripts = $this->apply_filters( 'admin_scripts', array(
482
+ 'jquery-ui-tooltip' => array()
483
+ ));
484
+
485
+ foreach ( $scripts as $_handle => $_args ) {
486
+ // Allows numeric array with handles as values
487
+ if ( is_numeric( $_handle ) ) {
488
+ $_handle = $_args;
489
+ }
490
+
491
+ // Allows specifying null as value to simply enqueue handle
492
+ if ( empty( $_args ) ){
493
+ $_args = array();
494
+ }
495
+
496
+ array_unshift( $_args, $_handle );
497
+ call_user_func_array( 'wp_enqueue_script', $_args );
498
+ }
499
+
500
+ return $this;
501
+ }
502
+
503
+
504
+ public function _admin_footer() {
505
+ $html = '';
506
+ $html .= $this->get_enqueued_tooltip_content_html() . "\n";
507
+ $html .= $this->get_admin_footer_js_html();
508
+ $html = $this->apply_filters( 'admin_footer', $html );
509
+
510
+ echo $html;
511
+ }
512
+
513
+
514
+ public function is_overrides_default_prefix( $string ) {
515
+ return strpos( $string, self::OVERRIDE_DEFAULT_PREFIX ) === 0;
516
+ }
517
+
518
+
519
+ /**
520
+ * @return string This class's text domain
521
+ */
522
+ public function get_text_domain() {
523
+ return self::TEXT_DOMAIN;
524
+ }
525
+
526
+ /**
527
+ * Format this string, replacing placeholders with values, and translate it
528
+ * in the class's text domain.
529
+ *
530
+ * @see sprintf()
531
+ * @param string $string The string to translate.
532
+ * @param mixed $argN,.. Additional arguments.
533
+ */
534
+ public function __( $string, $argN = null ) {
535
+ $args = func_get_args();
536
+ $args[0] = $string = __( $string, $this->get_text_domain() );
537
+
538
+ $string = call_user_func_array( 'sprintf', $args );
539
+
540
+ return $string;
541
+ }
542
+
543
+ /**
544
+ * Hashes all the given values into a single hash.
545
+ * Accepts an infinite number of parameters, all of which will be first
546
+ * glued together by a separator, then hashed.
547
+ * Non-scalar values will be serialized.
548
+ *
549
+ * @param mixed $value The value to hash.
550
+ * @param mixed $argN Other values to hash.
551
+ * @return string The hash.
552
+ */
553
+ public function get_hash( $value ) {
554
+ $args = func_get_args();
555
+ $glue = self::HASHING_CONCATENATOR;
556
+
557
+ $blob = '';
558
+ foreach ( $args as $_idx => $_arg ) {
559
+ $blob .= is_scalar( $_arg ) ? $_arg : serialize( $_arg );
560
+ $blob .= $glue;
561
+ }
562
+
563
+ $blob = substr( $blob, 0, -1 );
564
+
565
+ return sha1( $blob );
566
+ }
567
+
568
+ /**
569
+ * Get the class code prefix, or the specified prefixed with it.
570
+ *
571
+ * @param string $string A string to prefix.
572
+ * @return string The code prefix or the prefixed string.
573
+ */
574
+ public function get_code_prefix( $string = '' ) {
575
+ return self::CODE_PREFIX . (string)$string;
576
+ }
577
+
578
+ /**
579
+ * Optionally prefix a string with the class code prefix, unless it
580
+ * contains the "!" character in the very beginning, in which case it will
581
+ * simply be removed.
582
+ *
583
+ * @param string $string The string to consider for prefixing.
584
+ * @return string The prefixed or clean string.
585
+ */
586
+ public function prefix( $string ) {
587
+ return $this->is_overrides_default_prefix( $string )
588
+ ? substr( $string, 1 )
589
+ : $this->get_code_prefix( $string );
590
+ }
591
+
592
+ /**
593
+ * Applies filters, but prefixes the filter name with 'wprss_help_',
594
+ * unless '!' is specified as the first character of the filter.
595
+ *
596
+ * @param string $filter_name Name or "tag" of the filter.
597
+ * @param mixed $subject The value to apply filters to.
598
+ * @param mixed $argN,.. Additional filter arguments
599
+ * @return mixed Result of filtering
600
+ */
601
+ public function apply_filters( $filter_name, $subject, $argN = null ) {
602
+ $args = func_get_args();
603
+
604
+ $args[0] = $filter_name = $this->prefix( $filter_name );
605
+
606
+ return call_user_func_array( 'apply_filters', $args );
607
+ }
608
+
609
+
610
+ /**
611
+ * Applies a filters with the specified name to the options that were
612
+ * applied on top of defaults.
613
+ * The name will be prefixed with the class prefix 'wprss_help_', and
614
+ * suffixed with '_options'.
615
+ *
616
+ * @param string $filter_name Name of the filter to apply to the options
617
+ * @param array $options The options to filter
618
+ * @param mixed $filter_argN,.. Other filter arguments to be passed to filter
619
+ */
620
+ public function apply_options_filters( $filter_name, $options = array(), $filter_argN = null ) {
621
+ $args = func_get_args();
622
+
623
+ // Adding sufix
624
+ $args[0] = $filter_name .= self::OPTIONS_FILTER_SUFFIX;
625
+
626
+ // Applying defaults
627
+ $args[1] = $options = $this->get_options( $options );
628
+
629
+ // Entry point. Order of args is already correct.
630
+ $options = call_user_func_array( array( $this, 'apply_filters' ), $args );
631
+
632
+ return $options;
633
+ }
634
+
635
+
636
+ /**
637
+ * Parses the tooltip handle template path for placeholders.
638
+ *
639
+ * Filters used:
640
+ *
641
+ * - `wprss_help_admin_footer_js_html_template`
642
+ *
643
+ * @param null|string $path Optional path to parse and retrieve. Default: value of the 'admin_footer_js_template' option.
644
+ * @return string Path to the template.
645
+ */
646
+ public function get_admin_footer_js_html_template( $path = null ) {
647
+ // Default is from options
648
+ if ( is_null( $path ) ) {
649
+ $path = $this->get_options( 'admin_footer_js_template' );
650
+ }
651
+
652
+ // Entry point
653
+ $path = $this->apply_filters( 'admin_footer_js_html_template', $path );
654
+
655
+ return $this->parse_path( $path );
656
+ }
657
+
658
+
659
+ /**
660
+ * Get the HTML of the JavaScript for the footer in Admin Panel.
661
+ *
662
+ * Filters used:
663
+ *
664
+ * - `wprss_help_admin_footer_js_html`
665
+ *
666
+ * @param array $options Any additional options to be used with defaults.
667
+ * @return string The HTML.
668
+ */
669
+ public function get_admin_footer_js_html( $options = array() ) {
670
+ $options = $this->apply_options_filters( 'admin_footer_js_html', $options);
671
+
672
+ $templatePath = $this->get_admin_footer_js_html_template( $options['admin_footer_js_template'] );
673
+
674
+ return $this->get_template($templatePath, $options);
675
+ }
676
+
677
+
678
+ /**
679
+ * Parses the tooltip handle template path for placeholders.
680
+ *
681
+ * Filters used:
682
+ *
683
+ * - `wprss_help_tooltip_handle_html_template`
684
+ *
685
+ * @param null|string $path Optional path to parse and retrieve. Default: value of the 'tooltip_handle_template' option.
686
+ * @return string Path to the template.
687
+ */
688
+ public function get_tooltip_handle_html_template( $path = null ) {
689
+ // Default is from options
690
+ if ( is_null( $path ) ) {
691
+ $path = $this->get_options( 'tooltip_handle_template' );
692
+ }
693
+
694
+ // Entry point
695
+ $path = $this->apply_filters( 'tooltip_handle_html_template', $path );
696
+
697
+ return $this->parse_path( $path );
698
+ }
699
+
700
+
701
+ /**
702
+ * Get the HTML of the tooltip handle.
703
+ *
704
+ * Filters used:
705
+ *
706
+ * - `wprss_help_tooltip_handle_html_options`
707
+ *
708
+ * @param string $text Content of the tooltip text.
709
+ * @param string $id ID of the tooltip.
710
+ * @param array $options Any additional options to be used with defaults.
711
+ * @return string The HTML.
712
+ */
713
+ public function get_tooltip_handle_html( $text, $id, $options = array() ) {
714
+ $options = $this->apply_options_filters( 'tooltip_handle_html', $options, $text, $id);
715
+
716
+ // Add template varialbes
717
+ $options['tooltip_id'] = $id;
718
+ $options['tooltip_text'] = $text;
719
+
720
+ $templatePath = $this->get_tooltip_handle_html_template( $options['tooltip_handle_template'] );
721
+
722
+ return $this->get_template($templatePath, $options);
723
+ }
724
+
725
+
726
+ /**
727
+ * Parses the tooltip content template path for placeholders.
728
+ *
729
+ * Filters used:
730
+ *
731
+ * - `wprss_help_tooltip_content_html_template`
732
+ *
733
+ * @param null|string $path Optional path to parse and retrieve. Default: value of the 'tooltip_handle_template' option.
734
+ * @return string Path to the template.
735
+ */
736
+ public function get_tooltip_content_html_template( $path = null ) {
737
+ // Default is from options
738
+ if ( is_null( $path ) ) {
739
+ $path = $this->get_options( 'tooltip_content_template' );
740
+ }
741
+
742
+ // Entry point
743
+ $path = $this->apply_filters( 'tooltip_content_html_template', $path );
744
+
745
+ return $this->parse_path( $path );
746
+ }
747
+
748
+
749
+ /**
750
+ * Get the HTML of the tooltip content.
751
+ *
752
+ * Filters used:
753
+ *
754
+ * - `wprss_help_tooltip_content_html_options`
755
+ *
756
+ * @param string $text Content of the tooltip text.
757
+ * @param string $id ID of the tooltip.
758
+ * @param array $options Any additional options to be used with defaults.
759
+ * @return string The HTML.
760
+ */
761
+ public function get_tooltip_content_html( $text, $id, $options = array() ) {
762
+ $options = $this->apply_options_filters( 'tooltip_content_html', $options, $text, $id );
763
+
764
+ // Add template varialbes
765
+ $options['tooltip_id'] = $id;
766
+ $options['tooltip_text'] = $text;
767
+
768
+ $templatePath = $this->get_tooltip_content_html_template( $options['tooltip_content_template'] );
769
+
770
+ return $this->get_template( $templatePath, $options );
771
+ }
772
+
773
+
774
+ /**
775
+ * Add tooltip and get tooltip HTML.
776
+ * If $text is null, just get the HTML of tooltip with specified ID.
777
+ * The `is_enqueue_tooltip_content` option determines whether to enqueue
778
+ * the content, instead of outputting it after the handle.
779
+ *
780
+ * @param string $id ID for this tooltip
781
+ * @param string|null $text Text of this tooltip. If null, tooltip will not be added, but only retrieved.
782
+ * @param array|bool $options The options for this operation, or a boolean indicating whether or not content is to be enqueued
783
+ * @return string The tooltip handle and, optionally, content.
784
+ */
785
+ public function tooltip( $id, $text = null, $options = array() ) {
786
+ $this->add_tooltip( $id, $text, $options );
787
+ return $this->do_tooltip( $id );
788
+ }
789
+
790
+
791
+ /**
792
+ * Add tooltips in a batch, with optionally prefixed ID.
793
+ *
794
+ * @param array $tooltips An array where key is tooltip ID and value is tooltip text.
795
+ * @param string $prefix A prefix to add to all tooltip IDs.
796
+ * @param array $options Arra of options for all the tooltips to add.
797
+ * @return \WPRSS_Help
798
+ */
799
+ public function add_tooltips( $tooltips, $prefix = null, $options = array() ) {
800
+ $prefix = (string) $prefix;
801
+ if ( !is_array($options) ) $options = array();
802
+
803
+ foreach ( $tooltips as $_id => $_text ) {
804
+ $this->add_tooltip( $prefix . $_id, $_text, $options );
805
+ }
806
+
807
+ return $this;
808
+ }
809
+
810
+
811
+ /**
812
+ * Add a tooltip for later display.
813
+ * Text and options will be replaced by existing text and options, if they
814
+ * are empty, and a tooltip with the same ID is already registered.
815
+ *
816
+ * @param string $id The ID of this tooltip
817
+ * @param string $text Text for this tooltip
818
+ * @param array $options Options for this tooltip.
819
+ * @return WPRSS_Help This instance.
820
+ */
821
+ public function add_tooltip( $id, $text = null, $options = array() ) {
822
+ if ( $tooltip = $this->get_tooltip( $id ) ) {
823
+ if ( is_null( $text ) ) $text = isset( $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] : $text;
824
+ if ( empty( $options ) ) $options = isset( $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] : $options;
825
+ }
826
+
827
+ $this->set_tooltip( $id, $text, $options );
828
+
829
+ return $this;
830
+ }
831
+
832
+
833
+ /**
834
+ * Set a tooltip, existing or not.
835
+ *
836
+ * @param string $id The ID of this tooltip
837
+ * @param string $text Text for this tooltip
838
+ * @param array $options Options for this tooltip.
839
+ * @return WPRSS_Help This instance.
840
+ */
841
+ public function set_tooltip( $id, $text = null, $options = array() ) {
842
+ $this->_tooltips[ $id ] = array(
843
+ self::TOOLTIP_DATA_KEY_ID => $id,
844
+ self::TOOLTIP_DATA_KEY_TEXT => $text,
845
+ self::TOOLTIP_DATA_KEY_OPTIONS => $options
846
+ );
847
+
848
+ return $this;
849
+ }
850
+
851
+
852
+ /**
853
+ * Retrieve one tooltip, or an array containing all tooltips.
854
+ *
855
+ * @param string|null $id The ID of the tooltip to retrieve.
856
+ * @param mixed|null $default What to return if tooltip with specified ID not found.
857
+ * @return array An array that contains the following indexes: 'id', 'text', 'options'. See {@link add_tooltip()} for details.
858
+ */
859
+ public function get_tooltip( $id = null, $default = null ) {
860
+ if ( is_null( $id ) ) {
861
+ return $this->_tooltips;
862
+ }
863
+
864
+ return $this->has_tooltip( $id ) ? $this->_tooltips[ $id ] : $default;
865
+ }
866
+
867
+
868
+ /**
869
+ * Check whether a tooltip with the specified ID exists.
870
+ *
871
+ * @param string $id ID of the tooltip to check for.
872
+ * @return boolean True if a tooltip with the specified ID exists; false otherwise.
873
+ */
874
+ public function has_tooltip( $id ) {
875
+ return isset( $this->_tooltips[ $id ] );
876
+ }
877
+
878
+ /**
879
+ * Get registered tooltip HTML.
880
+ *
881
+ * Filters used:
882
+ *
883
+ * - `wprss_help_tooltip_options` - Filters options used for tooltip
884
+ *
885
+ * @param string $id ID for this tooltip
886
+ * @param string $text Text of this tooltip
887
+ * @param array|bool $options The options for this operation, or a boolean indicating whether or not content is to be enqueued
888
+ * @return string The tooltip handle and, optionally, content.
889
+ */
890
+ public function do_tooltip( $id ) {
891
+ $options = $this->get_options();
892
+
893
+ if ( !($tooltip = $this->get_tooltip( $id )) || !isset($tooltip[ self::TOOLTIP_DATA_KEY_TEXT ]) || !$tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) {
894
+ return isset( $options['tooltip_not_found_handle_html'] )
895
+ ? $options['tooltip_not_found_handle_html']
896
+ : null;
897
+ }
898
+
899
+ $options = isset( $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] : null;
900
+ $text = isset( $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] : null;
901
+
902
+ if ( !is_array( $options ) ) {
903
+ $options = array( 'is_enqueue_tooltip_content' => $options );
904
+ }
905
+
906
+ // Entry point
907
+ $options = $this->apply_options_filters( 'tooltip', $options, $id, $text );
908
+
909
+ // Get handle HTML
910
+ $output = $this->get_tooltip_handle_html( $text, $id, $options );
911
+
912
+ if ( $this->evaluate_boolean( $options['is_enqueue_tooltip_content'] ) ) {
913
+ $this->enqueue_tooltip_content($text, $id, $options);
914
+ }
915
+ else {
916
+ $output .= $this->get_tooltip_content_html( $text, $id, $options );
917
+ }
918
+
919
+ return $output;
920
+ }
921
+
922
+
923
+ /**
924
+ * Enqueue tooltip content to be displayed in another part of the page.
925
+ *
926
+ * @param string $text The text of the tooltip content to enqueue.
927
+ * @param string $id ID of the tooltip, the content of which to enqueue.
928
+ * @param array $options This tooltip's options.
929
+ * @return \WP_Error|\WPRSS_Help This instance, or error if enqueue method is invalid.
930
+ */
931
+ public function enqueue_tooltip_content( $text, $id, $options = array() ) {
932
+ $queue_method = $this->apply_filters( 'enqueue_tooltip_content_method', array( $this, '_enqueue_tooltip_content' ), $options, $id, $text );
933
+
934
+ // "Error handling" WP style
935
+ if ( !is_callable( $queue_method ) ) {
936
+ return new WP_Error( $this->prefix( 'invalid_queue_method' ), $this->__( 'Could not enqueue tooltip content: the queue method is not a valid callable.' ), array(
937
+ 'queue_method' => $queue_method,
938
+ 'text' => $text,
939
+ 'id' => $id,
940
+ 'options' => $options
941
+ ));
942
+ }
943
+
944
+ call_user_func_array( $queue_method, array( $text, $id, $options ) );
945
+
946
+ return $this;
947
+ }
948
+
949
+
950
+ public function _enqueue_tooltip_content( $text, $id, $options = array() ) {
951
+ $hash = $this->get_hash( $text, $id, $options );
952
+ $this->_enqueued_tooltip_content[ $hash ] = array(
953
+ self::TOOLTIP_DATA_KEY_TEXT => $text,
954
+ self::TOOLTIP_DATA_KEY_ID => $id,
955
+ self::TOOLTIP_DATA_KEY_OPTIONS => $options
956
+ );
957
+
958
+ return $this;
959
+ }
960
+
961
+
962
+ public function get_enqueued_tooltip_content() {
963
+ return $this->_enqueued_tooltip_content;
964
+ }
965
+
966
+
967
+ public function get_enqueued_tooltip_content_html() {
968
+ $output = '';
969
+ foreach ( $this->get_enqueued_tooltip_content() as $_hash => $_vars ) {
970
+ $options = is_array( $_vars[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $_vars[ self::TOOLTIP_DATA_KEY_OPTIONS ] : array();
971
+ $output = $this->get_tooltip_content_html( $_vars[ self::TOOLTIP_DATA_KEY_ID ], $_vars[ self::TOOLTIP_DATA_KEY_ID ], $options );
972
+ }
973
+
974
+ echo $output;
975
+ }
976
+
977
+
978
+ /**
979
+ * Check whether or not the given value is false.
980
+ * False values are all {@link empty()} values, and also strings 'false' and 'no'.
981
+ *
982
+ * @param mixed $value The value to check.
983
+ * @return boolean Whether or not the value is considered to be false.
984
+ */
985
+ public function evaluate_boolean( $value ) {
986
+ return (empty( $value ) || strtolower( $value ) === 'false' || strtolower( $value ) === 'no')
987
+ ? false
988
+ : true;
989
+ }
990
+
991
+
992
+ /**
993
+ * Merge two arrays in an intuitive way.
994
+ * Input arrays remain unchanged.
995
+ *
996
+ * @see http://php.net/manual/en/function.array-merge-recursive.php#92195
997
+ * @param array $array1 The array to merge.
998
+ * @param array $array2 The array to merge into.
999
+ * @return array The merged array.
1000
+ */
1001
+ public function array_merge_recursive_distinct( array &$array1, array &$array2 ) {
1002
+ $merged = $array1;
1003
+
1004
+ foreach ( $array2 as $key => &$value ) {
1005
+ if ( is_array( $value ) && isset( $merged[ $key ] ) && is_array( $merged[ $key ] ) ) {
1006
+ $merged[ $key ] = array_merge_recursive_distinct( $merged[ $key ], $value );
1007
+ } else {
1008
+ $merged[ $key ] = $value;
1009
+ }
1010
+ }
1011
+
1012
+ return $merged;
1013
+ }
1014
+
1015
+
1016
+ /**
1017
+ * Converts an array to a numeric array.
1018
+ * If $map is empty, assumes that the array keys are already in order.
1019
+ * If $map is a number, assumes it's the amount of elements to return.
1020
+ * If $map is an array, assumes it is the map of intended numeric indexes to their value in the input array.
1021
+ *
1022
+ * @param array $array The array to convert to a numeric array
1023
+ * @param false|null|array $map The map of the array indexes, or number of array elements to slice, or nothing.
1024
+ * @return array The resulting numeric array.
1025
+ */
1026
+ public function array_to_numeric( $array, $map = null ) {
1027
+ $result = array();
1028
+
1029
+ // If map is not an array, assume it's an indicator
1030
+ if ( !is_array( $map ) ) {
1031
+ $array = array_values( $array );
1032
+ }
1033
+
1034
+ // If map is empty, assume keys are in order
1035
+ if ( empty( $map ) ) {
1036
+ return $array;
1037
+ }
1038
+
1039
+ // If map is a number, assume it's the amount of elements to return
1040
+ if ( is_numeric( $map ) ) {
1041
+ $map = intval( $map );
1042
+ return array_slice( $array, 0, $map );
1043
+ }
1044
+
1045
+ foreach( $map as $_idx => $_key ) {
1046
+ $result[ $_idx ] = $array[ $_key ];
1047
+ }
1048
+
1049
+ return $result;
1050
+ }
1051
+
1052
+
1053
+ /**
1054
+ * Parses the template and replaces placeholders with their values.
1055
+ * This function uses {@see sprintf()} to format the template string using
1056
+ * the values provided in $data.
1057
+ * It is also possible for $data to be an associative array of key-value pairs.
1058
+ * To achieve the same result, a map can be provided, mapping data keys to
1059
+ * their placeholder positions.
1060
+ * If no map is provided,
1061
+ *
1062
+ * @param string $string The template string.
1063
+ * @param array $data The key-value pairs of template data.
1064
+ * @param false|null|array $map {@see array_to_numeric()} The template value map.
1065
+ * @return string The parsed and modified template.
1066
+ */
1067
+ public function parse_template( $string, $data, $map = null ) {
1068
+ $data = $this->array_to_numeric( $data, $map );
1069
+ array_unshift( $data, $string );
1070
+ return call_user_func_array( 'sprintf', $data );
1071
+ }
1072
+
1073
+
1074
+ /**
1075
+ * Parses a path template specifically with WPRSS_Help path placeholders.
1076
+ *
1077
+ * Filters used (in order):
1078
+ *
1079
+ * 1. `parse_path_data_default`;
1080
+ * 2. `parse_path_data`;
1081
+ * 3. `parse_path_map`;
1082
+ * 4. `parse_path_path`.
1083
+ *
1084
+ * @see WPRSS_Help::parse_template()
1085
+ * @param string $path The path to parse.
1086
+ * @param null|array $data Any additional data. Will be merged with defaults.
1087
+ * @param null|array $map The map for parsing.
1088
+ * @return string The path with placeholders replaced
1089
+ */
1090
+ public function parse_path( $path, $data = null, $map = null ) {
1091
+ if( is_null( $data ) ) {
1092
+ $data = array();
1093
+ }
1094
+
1095
+ $defaults = $this->apply_filters( 'parse_path_data_default', array(
1096
+ 'wprss_templates_dir' => wprss_get_templates_dir()
1097
+ ));
1098
+ $data = $this->array_merge_recursive_distinct( $data, $defaults );
1099
+ $data = $this->apply_filters( 'parse_path_data', $data, $path, $map );
1100
+ $map = $this->apply_filters( 'parse_path_map', $map, $data, $path );
1101
+ $path = $this->apply_filters( 'parse_path_path', $path, $data, $map );
1102
+
1103
+ return $this->parse_template( $path, $data, $map );
1104
+ }
1105
+ }
1106
+
1107
+ WPRSS_Help::init();
includes/admin-import-export.php CHANGED
@@ -1,242 +1,242 @@
1
- <?php
2
- /**
3
- * Build the import/export settings page, used to import and export the plugin's settings
4
- * Based on http://wp.tutsplus.com/tutorials/creative-coding/creating-a-simple-backuprestore-settings-feature/
5
- *
6
- * @since 3.1
7
- */
8
-
9
- add_action( 'admin_init', 'wp_rss_aggregator_bulk_import' );
10
- /**
11
- * Checks for the submission of a bulk import.
12
- * If a bulk submission is made, creates the feed sources.
13
- *
14
- * @since 4.5
15
- */
16
- function wp_rss_aggregator_bulk_import() {
17
- // Check if recieving
18
- if ( !empty( $_POST['bulk-feeds'] ) ) {
19
- // Check nonce
20
- check_admin_referer('wprss-bulk-import', 'wprss-bulk-import');
21
- // Get the site which we should post to
22
- $post_site = is_multisite() ? get_current_blog_id() : '';
23
- // Get the text
24
- $bulk_feeds = $_POST['bulk-feeds'];
25
- // Split by lines
26
- $lines = explode("\n", $bulk_feeds);
27
- // Keep a counter
28
- global $wprss_bulk_count;
29
- $wprss_bulk_count = 0;
30
- // Iterate each line
31
- foreach( $lines as $line ) {
32
- // Split by comma
33
- $parts = array_map('trim', explode(",", $line) );
34
- // Check if split was successful
35
- if ( count($parts) < 2 ) continue;
36
- // Prepare the feed data
37
- $name = $parts[0];
38
- $url = $parts[1];
39
- // Check if both name and url are set
40
- if ( empty($name) || empty($url) ) continue;
41
- $feed = array(
42
- 'post_title' => $name,
43
- 'post_status' => 'publish',
44
- 'post_type' => 'wprss_feed',
45
- 'post_site' => $post_site
46
- );
47
- // Insert the feed into the DB
48
- $inserted_id = wp_insert_post( $feed );
49
- // Check if an error occurred
50
- if ( is_wp_error($inserted_id) ) continue;
51
- // Set the URL
52
- update_post_meta($inserted_id, 'wprss_url', $url);
53
- // Increment the counter
54
- $wprss_bulk_count++;
55
- }
56
- add_action('admin_notices', 'wprss_notify_bulk_add');
57
- }
58
- }
59
-
60
- function wprss_notify_bulk_add() {
61
- global $wprss_bulk_count; ?>
62
- <div class="updated">
63
- <p><?php echo sprintf( __( 'Successfully imported <code>%1$s</code> feed sources.', WPRSS_TEXT_DOMAIN ), $wprss_bulk_count )?></p>
64
- </div>
65
- <?php
66
- }
67
-
68
-
69
- add_action( 'admin_init', 'wp_rss_aggregator_export', 1 );
70
-
71
- /**
72
- * Handles exporting of aggregator settings
73
- *
74
- * @since 3.1
75
- */
76
- function wp_rss_aggregator_export() {
77
- if ( isset( $_POST['export'] ) && check_admin_referer( 'wprss-settings-export' ) ) {
78
- $blogname = str_replace( " ", "", get_option( 'blogname' ) );
79
- $date = date( "m-d-Y" );
80
- $json_name = $blogname . "-" . $date; // Naming the filename that will be generated.
81
-
82
- header( 'Content-Description: File Transfer' );
83
- header( "Content-Type: text/json; charset=" . get_option( 'blog_charset' ) );
84
- header( "Content-Disposition: attachment; filename=$json_name.json" );
85
- wp_rss_set_export_data();
86
- die();
87
- }
88
- }
89
-
90
-
91
- /**
92
- * Gathers relevant options, encodes them in Json and echoes the file
93
- *
94
- * @since 3.1
95
- */
96
- function wp_rss_set_export_data() {
97
- $options = apply_filters(
98
- 'wprss_fields_export',
99
- array( 'wprss_settings_general' => get_option( 'wprss_settings_general' ) )
100
- );
101
- $json_file = json_encode( $options );
102
-
103
- foreach ( $options as $key => $value ) {
104
- $value = maybe_unserialize( $value );
105
- $need_options[ $key ] = $value;
106
- }
107
- $json_file = json_encode( $need_options ); // Encode data into json data
108
- echo $json_file;
109
- die();
110
- }
111
-
112
-
113
- /**
114
- * Notice for a successful export
115
- *
116
- * @since 3.1
117
- */
118
- function wp_rss_aggregator_export_notice() {
119
- ?><div class="updated"><?php echo wpautop( __( 'All options are exported successfully.', WPRSS_TEXT_DOMAIN ) ) ?></div><?php
120
-
121
- }
122
-
123
-
124
- /**
125
- * Notice for a successful import
126
- *
127
- * @since 3.1
128
- */
129
- function wp_rss_aggregator_import_notice1() {
130
- ?><div class="updated"><?php echo wpautop( __( 'All options are restored successfully.', WPRSS_TEXT_DOMAIN ) ) ?></div><?php
131
-
132
- }
133
-
134
-
135
- /**
136
- * Notice for an unsuccessful import
137
- *
138
- * @since 3.1
139
- */
140
- function wp_rss_aggregator_import_notice2() {
141
- ?><div class="error"><?php echo wpautop( __( 'Invalid file or file size too big.', WPRSS_TEXT_DOMAIN ) ) ?></div><?php
142
-
143
- }
144
-
145
-
146
- add_action( 'admin_init', 'wp_rss_aggregator_import' );
147
- /**
148
- * Handles the importing of settings
149
- *
150
- * @since 3.1
151
- */
152
- function wp_rss_aggregator_import(){
153
- global $pagenow;
154
- if( $pagenow == 'admin.php' ) {
155
- //Hope this plugin don't use admin.php for anything
156
- return;
157
- }
158
- elseif ( $pagenow == 'edit.php' ) {
159
- if ( isset( $_FILES['import'] ) && check_admin_referer( 'wprss-settings-import' ) ) {
160
- if ( $_FILES['import']['error'] > 0) {
161
- wp_die( "Error during import" );
162
- } else {
163
- $file_name = $_FILES['import']['name'];
164
- $file_ext = strtolower( end( explode( ".", $file_name ) ) );
165
- $file_size = $_FILES['import']['size'];
166
- if ( ( $file_ext == "json" ) && ( $file_size < 500000 ) ) {
167
- $encode_options = file_get_contents( $_FILES['import']['tmp_name'] );
168
- $options = json_decode( $encode_options, true );
169
- foreach ( $options as $key => $value ) {
170
- update_option( $key, $value );
171
- }
172
- add_action( 'admin_notices', 'wp_rss_aggregator_import_notice1' );
173
- do_action( 'wprss_settings_imported' );
174
- }
175
- else {
176
- add_action( 'admin_notices', 'wp_rss_aggregator_import_notice2' );
177
- }
178
- }
179
- }
180
- }
181
- }
182
-
183
-
184
- /**
185
- * Handles the import/export page display
186
- *
187
- * @since 3.1
188
- */
189
- function wprss_import_export_settings_page_display() {
190
- if ( !isset( $_POST['export'] ) ) { ?>
191
- <div class="wrap">
192
- <?php screen_icon( 'wprss-aggregator' ); ?>
193
-
194
- <!-- Bulk Add -->
195
- <h2><?php _e( 'Bulk Feed Import', WPRSS_TEXT_DOMAIN ); ?></h2>
196
- <p><?php _e( 'Import multiple feed sources at once, by entering the name and URLs of your feeds below.', WPRSS_TEXT_DOMAIN ); ?></p>
197
- <p><?php _e( 'Separate the name and the URL using a comma on each line:', WPRSS_TEXT_DOMAIN ); ?>
198
- <code><?php _e( 'Feed Name, http://www.myfeed.com', WPRSS_TEXT_DOMAIN ); ?></code>
199
- </p>
200
- <form id="bulk-add-form" method="POST">
201
- <textarea rows="6" cols="80" form="bulk-add-form" name="bulk-feeds" autofocus></textarea>
202
- <br/>
203
- <?php wp_nonce_field('wprss-bulk-import', 'wprss-bulk-import'); ?>
204
- <input type="submit" class="button-secondary" name="bulk-add" value="<?php _e( 'Bulk Import', WPRSS_TEXT_DOMAIN ) ?>" />
205
- </form>
206
- <hr/>
207
-
208
- <!-- Settings Import/Export -->
209
- <h2><?php _e( 'Import & Export Settings', WPRSS_TEXT_DOMAIN ); ?></h2>
210
-
211
- <h3><?php _e( 'Export Settings', WPRSS_TEXT_DOMAIN ); ?></h3>
212
- <?php echo wpautop( __( 'Click the <strong>Export Settings</strong> button to generate a file containing all the settings used by WP RSS Aggregator', WPRSS_TEXT_DOMAIN ) ) ?>
213
- <?php echo wpautop( __( 'After exporting, you can either use the backup file to restore your settings to this site or to another WordPress site.', WPRSS_TEXT_DOMAIN ) ) ?>
214
- <?php do_action( 'wprss_export_section' ); ?>
215
- <form method="post">
216
- <p class="submit">
217
- <?php wp_nonce_field( 'wprss-settings-export' ); ?>
218
- <input type="submit" name="export" value="<?php _e( 'Export Settings', WPRSS_TEXT_DOMAIN ); ?>" class="button" />
219
- </p>
220
- </form>
221
-
222
- <h3><?php _e( 'Import Settings', WPRSS_TEXT_DOMAIN ); ?></h3>
223
- <?php echo wpautop( __( 'Click the <strong>Choose file</strong> button and choose a backup file.', WPRSS_TEXT_DOMAIN ) ) ?>
224
- <?php echo wpautop( __( 'Press the <strong>Import Settings</strong> button, and WordPress will do the rest for you.', WPRSS_TEXT_DOMAIN ) ) ?>
225
- <?php do_action( 'wprss_import_section' ); ?>
226
- <form method='post' enctype='multipart/form-data'>
227
- <p class="submit">
228
- <?php wp_nonce_field( 'wprss-settings-import' ); ?>
229
- <input type='file' name='import' />
230
- <input type='submit' name='import' value="<?php _e( 'Import Settings', WPRSS_TEXT_DOMAIN ); ?>" class="button" />
231
- </p>
232
- </form>
233
-
234
- <h3><?php _e( 'Importing/Exporting Feed Sources', WPRSS_TEXT_DOMAIN ); ?></h3>
235
- <?php echo wpautop( sprintf( __( 'To import/export your feed sources, please use the standard WordPress <a href="%1$simport.php">Import</a> and <a href="%1$sexport.php">Export</a> functionality.', WPRSS_TEXT_DOMAIN ), get_admin_url() ) ) ?>
236
- <?php echo wpautop( sprintf( __( 'On the <a href="%1$sexport.php">Export</a> page, check the <strong>Feed Sources</strong> radio button and click the <strong>Download Export File</strong> button. WordPress will then create an XML file containing all the feed sources.', WPRSS_TEXT_DOMAIN ), get_admin_url() ) ) ?>
237
- <?php echo wpautop( sprintf( __( 'On the <a href="%1$simport.php">Import</a> page, choose the previously created file and click the <strong>Upload file and import</strong> button.', WPRSS_TEXT_DOMAIN ), get_admin_url() ) ) ?>
238
-
239
- </div>
240
- <?php
241
- }
242
  }
1
+ <?php
2
+ /**
3
+ * Build the import/export settings page, used to import and export the plugin's settings
4
+ * Based on http://wp.tutsplus.com/tutorials/creative-coding/creating-a-simple-backuprestore-settings-feature/
5
+ *
6
+ * @since 3.1
7
+ */
8
+
9
+ add_action( 'admin_init', 'wp_rss_aggregator_bulk_import' );
10
+ /**
11
+ * Checks for the submission of a bulk import.
12
+ * If a bulk submission is made, creates the feed sources.
13
+ *
14
+ * @since 4.5
15
+ */
16
+ function wp_rss_aggregator_bulk_import() {
17
+ // Check if recieving
18
+ if ( !empty( $_POST['bulk-feeds'] ) ) {
19
+ // Check nonce
20
+ check_admin_referer('wprss-bulk-import', 'wprss-bulk-import');
21
+ // Get the site which we should post to
22
+ $post_site = is_multisite() ? get_current_blog_id() : '';
23
+ // Get the text
24
+ $bulk_feeds = $_POST['bulk-feeds'];
25
+ // Split by lines
26
+ $lines = explode("\n", $bulk_feeds);
27
+ // Keep a counter
28
+ global $wprss_bulk_count;
29
+ $wprss_bulk_count = 0;
30
+ // Iterate each line
31
+ foreach( $lines as $line ) {
32
+ // Split by comma
33
+ $parts = array_map('trim', explode(",", $line) );
34
+ // Check if split was successful
35
+ if ( count($parts) < 2 ) continue;
36
+ // Prepare the feed data
37
+ $name = $parts[0];
38
+ $url = $parts[1];
39
+ // Check if both name and url are set
40
+ if ( empty($name) || empty($url) ) continue;
41
+ $feed = array(
42
+ 'post_title' => $name,
43
+ 'post_status' => 'publish',
44
+ 'post_type' => 'wprss_feed',
45
+ 'post_site' => $post_site
46
+ );
47
+ // Insert the feed into the DB
48
+ $inserted_id = wp_insert_post( $feed );
49
+ // Check if an error occurred
50
+ if ( is_wp_error($inserted_id) ) continue;
51
+ // Set the URL
52
+ update_post_meta($inserted_id, 'wprss_url', $url);
53
+ // Increment the counter
54
+ $wprss_bulk_count++;
55
+ }
56
+ add_action('admin_notices', 'wprss_notify_bulk_add');
57
+ }
58
+ }
59
+
60
+ function wprss_notify_bulk_add() {
61
+ global $wprss_bulk_count; ?>
62
+ <div class="updated">
63
+ <p><?php echo sprintf( __( 'Successfully imported <code>%1$s</code> feed sources.', WPRSS_TEXT_DOMAIN ), $wprss_bulk_count )?></p>
64
+ </div>
65
+ <?php
66
+ }
67
+
68
+
69
+ add_action( 'admin_init', 'wp_rss_aggregator_export', 1 );
70
+
71
+ /**
72
+ * Handles exporting of aggregator settings
73
+ *
74
+ * @since 3.1
75
+ */
76
+ function wp_rss_aggregator_export() {
77
+ if ( isset( $_POST['export'] ) && check_admin_referer( 'wprss-settings-export' ) ) {
78
+ $blogname = str_replace( " ", "", get_option( 'blogname' ) );
79
+ $date = date( "m-d-Y" );
80
+ $json_name = $blogname . "-" . $date; // Naming the filename that will be generated.
81
+
82
+ header( 'Content-Description: File Transfer' );
83
+ header( "Content-Type: text/json; charset=" . get_option( 'blog_charset' ) );
84
+ header( "Content-Disposition: attachment; filename=$json_name.json" );
85
+ wp_rss_set_export_data();
86
+ die();
87
+ }
88
+ }
89
+
90
+
91
+ /**
92
+ * Gathers relevant options, encodes them in Json and echoes the file
93
+ *
94
+ * @since 3.1
95
+ */
96
+ function wp_rss_set_export_data() {
97
+ $options = apply_filters(
98
+ 'wprss_fields_export',
99
+ array( 'wprss_settings_general' => get_option( 'wprss_settings_general' ) )
100
+ );
101
+ $json_file = json_encode( $options );
102
+
103
+ foreach ( $options as $key => $value ) {
104
+ $value = maybe_unserialize( $value );
105
+ $need_options[ $key ] = $value;
106
+ }
107
+ $json_file = json_encode( $need_options ); // Encode data into json data
108
+ echo $json_file;
109
+ die();
110
+ }
111
+
112
+
113
+ /**
114
+ * Notice for a successful export
115
+ *
116
+ * @since 3.1
117
+ */
118
+ function wp_rss_aggregator_export_notice() {
119
+ ?><div class="updated"><?php echo wpautop( __( 'All options are exported successfully.', WPRSS_TEXT_DOMAIN ) ) ?></div><?php
120
+
121
+ }
122
+
123
+
124
+ /**
125
+ * Notice for a successful import
126
+ *
127
+ * @since 3.1
128
+ */
129
+ function wp_rss_aggregator_import_notice1() {
130
+ ?><div class="updated"><?php echo wpautop( __( 'All options are restored successfully.', WPRSS_TEXT_DOMAIN ) ) ?></div><?php
131
+
132
+ }
133
+
134
+
135
+ /**
136
+ * Notice for an unsuccessful import
137
+ *
138
+ * @since 3.1
139
+ */
140
+ function wp_rss_aggregator_import_notice2() {
141
+ ?><div class="error"><?php echo wpautop( __( 'Invalid file or file size too big.', WPRSS_TEXT_DOMAIN ) ) ?></div><?php
142
+
143
+ }
144
+
145
+
146
+ add_action( 'admin_init', 'wp_rss_aggregator_import' );
147
+ /**
148
+ * Handles the importing of settings
149
+ *
150
+ * @since 3.1
151
+ */
152
+ function wp_rss_aggregator_import(){
153
+ global $pagenow;
154
+ if( $pagenow == 'admin.php' ) {
155
+ //Hope this plugin don't use admin.php for anything
156
+ return;
157
+ }
158
+ elseif ( $pagenow == 'edit.php' ) {
159
+ if ( isset( $_FILES['import'] ) && check_admin_referer( 'wprss-settings-import' ) ) {
160
+ if ( $_FILES['import']['error'] > 0) {
161
+ wp_die( "Error during import" );
162
+ } else {
163
+ $file_name = $_FILES['import']['name'];
164
+ $file_ext = strtolower( end( explode( ".", $file_name ) ) );
165
+ $file_size = $_FILES['import']['size'];
166
+ if ( ( $file_ext == "json" ) && ( $file_size < 500000 ) ) {
167
+ $encode_options = file_get_contents( $_FILES['import']['tmp_name'] );
168
+ $options = json_decode( $encode_options, true );
169
+ foreach ( $options as $key => $value ) {
170
+ update_option( $key, $value );
171
+ }
172
+ add_action( 'admin_notices', 'wp_rss_aggregator_import_notice1' );
173
+ do_action( 'wprss_settings_imported' );
174
+ }
175
+ else {
176
+ add_action( 'admin_notices', 'wp_rss_aggregator_import_notice2' );
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
182
+
183
+
184
+ /**
185
+ * Handles the import/export page display
186
+ *
187
+ * @since 3.1
188
+ */
189
+ function wprss_import_export_settings_page_display() {
190
+ if ( !isset( $_POST['export'] ) ) { ?>
191
+ <div class="wrap">
192
+ <?php screen_icon( 'wprss-aggregator' ); ?>
193
+
194
+ <!-- Bulk Add -->
195
+ <h2><?php _e( 'Bulk Feed Import', WPRSS_TEXT_DOMAIN ); ?></h2>
196
+ <p><?php _e( 'Import multiple feed sources at once, by entering the name and URLs of your feeds below.', WPRSS_TEXT_DOMAIN ); ?></p>
197
+ <p><?php _e( 'Separate the name and the URL using a comma on each line:', WPRSS_TEXT_DOMAIN ); ?>
198
+ <code><?php _e( 'Feed Name, http://www.myfeed.com', WPRSS_TEXT_DOMAIN ); ?></code>
199
+ </p>
200
+ <form id="bulk-add-form" method="POST">
201
+ <textarea rows="6" cols="80" form="bulk-add-form" name="bulk-feeds" autofocus></textarea>
202
+ <br/>
203
+ <?php wp_nonce_field('wprss-bulk-import', 'wprss-bulk-import'); ?>
204
+ <input type="submit" class="button-secondary" name="bulk-add" value="<?php _e( 'Bulk Import', WPRSS_TEXT_DOMAIN ) ?>" />
205
+ </form>
206
+ <hr/>
207
+
208
+ <!-- Settings Import/Export -->
209
+ <h2><?php _e( 'Import & Export Settings', WPRSS_TEXT_DOMAIN ); ?></h2>
210
+
211
+ <h3><?php _e( 'Export Settings', WPRSS_TEXT_DOMAIN ); ?></h3>
212
+ <?php echo wpautop( __( 'Click the <strong>Export Settings</strong> button to generate a file containing all the settings used by WP RSS Aggregator', WPRSS_TEXT_DOMAIN ) ) ?>
213
+ <?php echo wpautop( __( 'After exporting, you can either use the backup file to restore your settings to this site or to another WordPress site.', WPRSS_TEXT_DOMAIN ) ) ?>
214
+ <?php do_action( 'wprss_export_section' ); ?>
215
+ <form method="post">
216
+ <p class="submit">
217
+ <?php wp_nonce_field( 'wprss-settings-export' ); ?>
218
+ <input type="submit" name="export" value="<?php _e( 'Export Settings', WPRSS_TEXT_DOMAIN ); ?>" class="button" />
219
+ </p>
220
+ </form>
221
+
222
+ <h3><?php _e( 'Import Settings', WPRSS_TEXT_DOMAIN ); ?></h3>
223
+ <?php echo wpautop( __( 'Click the <strong>Choose file</strong> button and choose a backup file.', WPRSS_TEXT_DOMAIN ) ) ?>
224
+ <?php echo wpautop( __( 'Press the <strong>Import Settings</strong> button, and WordPress will do the rest for you.', WPRSS_TEXT_DOMAIN ) ) ?>
225
+ <?php do_action( 'wprss_import_section' ); ?>
226
+ <form method='post' enctype='multipart/form-data'>
227
+ <p class="submit">
228
+ <?php wp_nonce_field( 'wprss-settings-import' ); ?>
229
+ <input type='file' name='import' />
230
+ <input type='submit' name='import' value="<?php _e( 'Import Settings', WPRSS_TEXT_DOMAIN ); ?>" class="button" />
231
+ </p>
232
+ </form>
233
+
234
+ <h3><?php _e( 'Importing/Exporting Feed Sources', WPRSS_TEXT_DOMAIN ); ?></h3>
235
+ <?php echo wpautop( sprintf( __( 'To import/export your feed sources, please use the standard WordPress <a href="%1$simport.php">Import</a> and <a href="%1$sexport.php">Export</a> functionality.', WPRSS_TEXT_DOMAIN ), get_admin_url() ) ) ?>
236
+ <?php echo wpautop( sprintf( __( 'On the <a href="%1$sexport.php">Export</a> page, check the <strong>Feed Sources</strong> radio button and click the <strong>Download Export File</strong> button. WordPress will then create an XML file containing all the feed sources.', WPRSS_TEXT_DOMAIN ), get_admin_url() ) ) ?>
237
+ <?php echo wpautop( sprintf( __( 'On the <a href="%1$simport.php">Import</a> page, choose the previously created file and click the <strong>Upload file and import</strong> button.', WPRSS_TEXT_DOMAIN ), get_admin_url() ) ) ?>
238
+
239
+ </div>
240
+ <?php
241
+ }
242
  }
includes/admin-log.php CHANGED
@@ -1,268 +1,268 @@
1
- <?php
2
-
3
- define( 'WPRSS_LOG_DISPLAY_LIMIT', 100000 ); // Number of chars to display in log
4
- define( 'WPRSS_OPTION_CODE_LOG_LEVEL', 'log_level' );
5
- define( 'WPRSS_LOG_LEVEL_NONE', 0 );
6
- define( 'WPRSS_LOG_LEVEL_SYSTEM', 1 );
7
- define( 'WPRSS_LOG_LEVEL_INFO', 2 );
8
- define( 'WPRSS_LOG_LEVEL_NOTICE', 4 );
9
- define( 'WPRSS_LOG_LEVEL_WARNING', 8 );
10
- define( 'WPRSS_LOG_LEVEL_ERROR', 16 );
11
- define( 'WPRSS_LOG_LEVEL_DEFAULT', 'default' );
12
-
13
-
14
- /**
15
- * Returns the log file path.
16
- *
17
- * @since 4.0.4
18
- */
19
- function wprss_log_file() {
20
- return WPRSS_LOG_FILE . '-' . get_current_blog_id() . WPRSS_LOG_FILE_EXT;
21
- }
22
-
23
-
24
- /**
25
- * Clears the log file.
26
- *
27
- * @since 3.9.6
28
- */
29
- function wprss_clear_log() {
30
- file_put_contents( wprss_log_file(), '' );
31
- }
32
-
33
-
34
- /**
35
- * Alias for wprss_clear_log(). Used for code readability.
36
- *
37
- * @since 3.9.6
38
- */
39
- function wprss_reset_log() {
40
- wprss_clear_log();
41
- }
42
-
43
- /**
44
- * Gets log level from the database.
45
- * @return string The string representing the log level threshold or type.
46
- */
47
- function wprss_log_get_level_db() {
48
- return wprss_get_general_setting( WPRSS_OPTION_CODE_LOG_LEVEL );
49
- }
50
-
51
- /**
52
- * Gets log level used.
53
- * @return string The string representing the log level threshold.
54
- */
55
- function wprss_log_get_level() {
56
- $log_level = wprss_log_get_level_db();
57
- if ( $log_level === WPRSS_LOG_LEVEL_DEFAULT )
58
- $log_level = WPRSS_LOG_LEVEL;
59
-
60
- return apply_filters( 'wprss_log_level', $log_level );
61
- }
62
-
63
-
64
- /**
65
- * Check whether or not the specified logging level is the same as, or one of (only for positive),
66
- * the currently used logging level.
67
- *
68
- * @param int $log_level The log level to check. Must be an unsiged whole number.
69
- */
70
- function wprss_log_is_level( $log_level, $used_log_level = null ) {
71
- $used_log_level = is_null( $used_log_level ) ? wprss_log_get_level() : $used_log_level;
72
-
73
- if( is_numeric( $log_level ) ) {
74
- $log_level = intval( $log_level );
75
- $used_log_level = intval( $used_log_level );
76
-
77
- return ($log_level > 0 && $used_log_level > 0)
78
- // Mostly for the case of 0
79
- ? intval( $log_level ) & intval( $used_log_level )
80
- : $log_level === $used_log_level;
81
- }
82
-
83
- return trim( $log_level ) === trim( $used_log_level );
84
- }
85
-
86
-
87
- /**
88
- * Check whether or not messages with the specified logging level should be logged.
89
- *
90
- * @param int $log_level The log level to check. Must be an unsigned whole number
91
- * @return bool True if messages with the specified logging level should be logged; false otherwise.
92
- */
93
- function wprss_log_is_logging_level( $log_level ) {
94
- $original_used_level = $used_log_level = wprss_log_get_level();
95
-
96
- // Whether to use the indicated level and below
97
- $is_below = ( substr( $used_log_level, 0, 1 ) === '-' );
98
- if ( $is_below )
99
- $used_log_level = substr( $used_log_level, 1 );
100
-
101
- if( (int)$used_log_level === WPRSS_LOG_LEVEL_NONE ) {
102
- $is_log_level = WPRSS_LOG_LEVEL_NONE;
103
- }
104
- else {
105
- $is_log_level = $is_below
106
- ? ((int)$log_level <= (int)$used_log_level && (int)$log_level !== WPRSS_LOG_LEVEL_NONE)
107
- : wprss_log_is_level( (int)$log_level, $used_log_level );
108
- }
109
-
110
- return apply_filters( 'wprss_is_logging_level', $is_log_level, $log_level, $used_log_level, $is_below );
111
- }
112
-
113
-
114
- /**
115
- * Get the available log levels.
116
- *
117
- * @param bool $levels_only Whether or not only numeric actual levels are to be returned.
118
- * If false, returns other types as well.
119
- * @return array An array, where key is level, and value is level's human-readable name
120
- */
121
- function wprss_log_get_levels( $levels_only = true ) {
122
- $log_levels = array(
123
- WPRSS_LOG_LEVEL_NONE => 'None',
124
- WPRSS_LOG_LEVEL_SYSTEM => 'System',
125
- WPRSS_LOG_LEVEL_INFO => 'Info',
126
- WPRSS_LOG_LEVEL_NOTICE => 'Notice',
127
- WPRSS_LOG_LEVEL_WARNING => 'Warning',
128
- WPRSS_LOG_LEVEL_ERROR => 'Error'
129
- );
130
-
131
- if( !$levels_only )
132
- $log_levels[ WPRSS_LOG_LEVEL_DEFAULT ] = 'Default';
133
-
134
- return apply_filters( 'wprss_log_levels', $log_levels, $levels_only );
135
- }
136
-
137
-
138
- /**
139
- *
140
- * @param string|int $level Any valid level value.
141
- * @return string The untranslated label of the specified level, or $default if no such level exists.
142
- */
143
- function wprss_log_get_level_label( $level, $default = 'N/A' ) {
144
- $levels = wprss_log_get_levels( false );
145
- return isset( $levels[$level] ) ? $levels[ $level ] : $default;
146
- }
147
-
148
-
149
- /**
150
- * Adds a log entry to the log file.
151
- *
152
- * @since 3.9.6
153
- */
154
- function wprss_log( $message, $src = NULL, $log_level = WPRSS_LOG_LEVEL_ERROR ) {
155
- if( !wprss_log_is_logging_level( $log_level ) ) return;
156
-
157
- if ( $src === NULL ) {
158
- $callers = debug_backtrace();
159
- $src = $callers[1]['function'];
160
- if ( $src === 'wprss_log_obj' ) {
161
- $src = $callers[2]['function'];
162
- }
163
- }
164
- $log_level_label = wprss_log_get_level_label( $log_level );
165
- $date = date( 'd-m-Y H:i:s' );
166
- $source = 'WPRSS' . ( ( strlen( $src ) > 0 )? " > $src" : '' ) ;
167
- $str = "[$date] [$log_level_label] $source:\n";
168
- $str .= "$message\n\n";
169
- file_put_contents( wprss_log_file() , $str, FILE_APPEND );
170
-
171
- add_action( 'shutdown', 'wprss_log_separator' );
172
- }
173
-
174
-
175
- /**
176
- * Dumps an object to the log file.
177
- *
178
- * @since 3.9.6
179
- */
180
- function wprss_log_obj( $message, $obj, $src = '', $log_level = WPRSS_LOG_LEVEL_ERROR ) {
181
- wprss_log( "$message: " . print_r( $obj, TRUE ), $src, $log_level );
182
- }
183
-
184
-
185
- /**
186
- * Returns the contents of the log file.
187
- *
188
- * @since 3.9.6
189
- */
190
- function wprss_get_log() {
191
- if ( !file_exists( wprss_log_file() ) ) {
192
- wprss_clear_log();
193
- }
194
- $contents = file_get_contents( wprss_log_file() , '' );
195
- // Trim the log file to a fixed number of chars
196
- $limit = WPRSS_LOG_DISPLAY_LIMIT;
197
- if ( strlen( $contents ) > $limit ) {
198
- file_put_contents( wprss_log_file(), substr( $contents, 0, $limit ) );
199
- return wprss_get_log();
200
- } else {
201
- return $contents;
202
- }
203
- }
204
-
205
-
206
- /**
207
- * Adds an empty line at the end of the log file.
208
- *
209
- * This function is called on wordpress shutdown, if at least one new line
210
- * is logged in the log file, to separate logs from different page loads.
211
- *
212
- * @since 3.9.6
213
- */
214
- function wprss_log_separator() {
215
- file_put_contents( wprss_log_file(), "\n", FILE_APPEND );
216
- }
217
-
218
-
219
- /**
220
- * Adding the default setting value.
221
- */
222
- add_filter( 'wprss_default_settings_general', 'wprss_log_default_settings_general' );
223
- function wprss_log_default_settings_general( $settings ) {
224
- /* @todo Add version info */
225
- $settings[ WPRSS_OPTION_CODE_LOG_LEVEL ] = WPRSS_LOG_LEVEL_DEFAULT;
226
- return $settings;
227
- }
228
-
229
-
230
- /**
231
- * Adding the setting field
232
- */
233
- add_filter( 'wprss_settings_array', 'wprss_log_settings_array' );
234
- function wprss_log_settings_array( $sections ) {
235
- $sections['general'][ WPRSS_OPTION_CODE_LOG_LEVEL ] = array(
236
- 'label' => __( 'Log level threshold', WPRSS_TEXT_DOMAIN ),
237
- 'callback' => 'wprss_setting_' . WPRSS_OPTION_CODE_LOG_LEVEL . '_callback'
238
- );
239
- return $sections;
240
- }
241
-
242
-
243
- /**
244
- * Renders the 'log_level' setting field.
245
- *
246
- * @param array $field Info about the field
247
- */
248
- function wprss_setting_log_level_callback( $field ) {
249
- $log_level = wprss_get_general_setting( $field['field_id'] );
250
-
251
- foreach( wprss_log_get_levels( false ) as $_level => $_label ) {
252
- $options[ $_level ] = $_label;
253
- if( is_numeric( $_level ) && ($_level/2 >= 1) ) $options[ (int)$_level * -1 ] = $_label . ' and below';
254
- }
255
-
256
- krsort( $options, defined( 'SORT_NATURAL' ) ? SORT_NATURAL : SORT_STRING );
257
- ?>
258
- <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[<?php echo $field['field_id'] ?>]">
259
- <?php
260
- foreach( $options as $value => $text ) {
261
- $selected = ( (string)$value === (string)$log_level )? 'selected="selected"' : '';
262
- ?><option value="<?php echo $value ?>" <?php echo $selected ?>><?php echo __( $text, WPRSS_TEXT_DOMAIN ) ?></option><?php
263
- }
264
- ?>
265
- </select>
266
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
267
- }
268
 
1
+ <?php
2
+
3
+ define( 'WPRSS_LOG_DISPLAY_LIMIT', 100000 ); // Number of chars to display in log
4
+ define( 'WPRSS_OPTION_CODE_LOG_LEVEL', 'log_level' );
5
+ define( 'WPRSS_LOG_LEVEL_NONE', 0 );
6
+ define( 'WPRSS_LOG_LEVEL_SYSTEM', 1 );
7
+ define( 'WPRSS_LOG_LEVEL_INFO', 2 );
8
+ define( 'WPRSS_LOG_LEVEL_NOTICE', 4 );
9
+ define( 'WPRSS_LOG_LEVEL_WARNING', 8 );
10
+ define( 'WPRSS_LOG_LEVEL_ERROR', 16 );
11
+ define( 'WPRSS_LOG_LEVEL_DEFAULT', 'default' );
12
+
13
+
14
+ /**
15
+ * Returns the log file path.
16
+ *
17
+ * @since 4.0.4
18
+ */
19
+ function wprss_log_file() {
20
+ return WPRSS_LOG_FILE . '-' . get_current_blog_id() . WPRSS_LOG_FILE_EXT;
21
+ }
22
+
23
+
24
+ /**
25
+ * Clears the log file.
26
+ *
27
+ * @since 3.9.6
28
+ */
29
+ function wprss_clear_log() {
30
+ file_put_contents( wprss_log_file(), '' );
31
+ }
32
+
33
+
34
+ /**
35
+ * Alias for wprss_clear_log(). Used for code readability.
36
+ *
37
+ * @since 3.9.6
38
+ */
39
+ function wprss_reset_log() {
40
+ wprss_clear_log();
41
+ }
42
+
43
+ /**
44
+ * Gets log level from the database.
45
+ * @return string The string representing the log level threshold or type.
46
+ */
47
+ function wprss_log_get_level_db() {
48
+ return wprss_get_general_setting( WPRSS_OPTION_CODE_LOG_LEVEL );
49
+ }
50
+
51
+ /**
52
+ * Gets log level used.
53
+ * @return string The string representing the log level threshold.
54
+ */
55
+ function wprss_log_get_level() {
56
+ $log_level = wprss_log_get_level_db();
57
+ if ( $log_level === WPRSS_LOG_LEVEL_DEFAULT )
58
+ $log_level = WPRSS_LOG_LEVEL;
59
+
60
+ return apply_filters( 'wprss_log_level', $log_level );
61
+ }
62
+
63
+
64
+ /**
65
+ * Check whether or not the specified logging level is the same as, or one of (only for positive),
66
+ * the currently used logging level.
67
+ *
68
+ * @param int $log_level The log level to check. Must be an unsiged whole number.
69
+ */
70
+ function wprss_log_is_level( $log_level, $used_log_level = null ) {
71
+ $used_log_level = is_null( $used_log_level ) ? wprss_log_get_level() : $used_log_level;
72
+
73
+ if( is_numeric( $log_level ) ) {
74
+ $log_level = intval( $log_level );
75
+ $used_log_level = intval( $used_log_level );
76
+
77
+ return ($log_level > 0 && $used_log_level > 0)
78
+ // Mostly for the case of 0
79
+ ? intval( $log_level ) & intval( $used_log_level )
80
+ : $log_level === $used_log_level;
81
+ }
82
+
83
+ return trim( $log_level ) === trim( $used_log_level );
84
+ }
85
+
86
+
87
+ /**
88
+ * Check whether or not messages with the specified logging level should be logged.
89
+ *
90
+ * @param int $log_level The log level to check. Must be an unsigned whole number
91
+ * @return bool True if messages with the specified logging level should be logged; false otherwise.
92
+ */
93
+ function wprss_log_is_logging_level( $log_level ) {
94
+ $original_used_level = $used_log_level = wprss_log_get_level();
95
+
96
+ // Whether to use the indicated level and below
97
+ $is_below = ( substr( $used_log_level, 0, 1 ) === '-' );
98
+ if ( $is_below )
99
+ $used_log_level = substr( $used_log_level, 1 );
100
+
101
+ if( (int)$used_log_level === WPRSS_LOG_LEVEL_NONE ) {
102
+ $is_log_level = WPRSS_LOG_LEVEL_NONE;
103
+ }
104
+ else {
105
+ $is_log_level = $is_below
106
+ ? ((int)$log_level <= (int)$used_log_level && (int)$log_level !== WPRSS_LOG_LEVEL_NONE)
107
+ : wprss_log_is_level( (int)$log_level, $used_log_level );
108
+ }
109
+
110
+ return apply_filters( 'wprss_is_logging_level', $is_log_level, $log_level, $used_log_level, $is_below );
111
+ }
112
+
113
+
114
+ /**
115
+ * Get the available log levels.
116
+ *
117
+ * @param bool $levels_only Whether or not only numeric actual levels are to be returned.
118
+ * If false, returns other types as well.
119
+ * @return array An array, where key is level, and value is level's human-readable name
120
+ */
121
+ function wprss_log_get_levels( $levels_only = true ) {
122
+ $log_levels = array(
123
+ WPRSS_LOG_LEVEL_NONE => 'None',
124
+ WPRSS_LOG_LEVEL_SYSTEM => 'System',
125
+ WPRSS_LOG_LEVEL_INFO => 'Info',
126
+ WPRSS_LOG_LEVEL_NOTICE => 'Notice',
127
+ WPRSS_LOG_LEVEL_WARNING => 'Warning',
128
+ WPRSS_LOG_LEVEL_ERROR => 'Error'
129
+ );
130
+
131
+ if( !$levels_only )
132
+ $log_levels[ WPRSS_LOG_LEVEL_DEFAULT ] = 'Default';
133
+
134
+ return apply_filters( 'wprss_log_levels', $log_levels, $levels_only );
135
+ }
136
+
137
+
138
+ /**
139
+ *
140
+ * @param string|int $level Any valid level value.
141
+ * @return string The untranslated label of the specified level, or $default if no such level exists.
142
+ */
143
+ function wprss_log_get_level_label( $level, $default = 'N/A' ) {
144
+ $levels = wprss_log_get_levels( false );
145
+ return isset( $levels[$level] ) ? $levels[ $level ] : $default;
146
+ }
147
+
148
+
149
+ /**
150
+ * Adds a log entry to the log file.
151
+ *
152
+ * @since 3.9.6
153
+ */
154
+ function wprss_log( $message, $src = NULL, $log_level = WPRSS_LOG_LEVEL_ERROR ) {
155
+ if( !wprss_log_is_logging_level( $log_level ) ) return;
156
+
157
+ if ( $src === NULL ) {
158
+ $callers = debug_backtrace();
159
+ $src = $callers[1]['function'];
160
+ if ( $src === 'wprss_log_obj' ) {
161
+ $src = $callers[2]['function'];
162
+ }
163
+ }
164
+ $log_level_label = wprss_log_get_level_label( $log_level );
165
+ $date = date( 'd-m-Y H:i:s' );
166
+ $source = 'WPRSS' . ( ( strlen( $src ) > 0 )? " > $src" : '' ) ;
167
+ $str = "[$date] [$log_level_label] $source:\n";
168
+ $str .= "$message\n\n";
169
+ file_put_contents( wprss_log_file() , $str, FILE_APPEND );
170
+
171
+ add_action( 'shutdown', 'wprss_log_separator' );
172
+ }
173
+
174
+
175
+ /**
176
+ * Dumps an object to the log file.
177
+ *
178
+ * @since 3.9.6
179
+ */
180
+ function wprss_log_obj( $message, $obj, $src = '', $log_level = WPRSS_LOG_LEVEL_ERROR ) {
181
+ wprss_log( "$message: " . print_r( $obj, TRUE ), $src, $log_level );
182
+ }
183
+
184
+
185
+ /**
186
+ * Returns the contents of the log file.
187
+ *
188
+ * @since 3.9.6
189
+ */
190
+ function wprss_get_log() {
191
+ if ( !file_exists( wprss_log_file() ) ) {
192
+ wprss_clear_log();
193
+ }
194
+ $contents = file_get_contents( wprss_log_file() , '' );
195
+ // Trim the log file to a fixed number of chars
196
+ $limit = WPRSS_LOG_DISPLAY_LIMIT;
197
+ if ( strlen( $contents ) > $limit ) {
198
+ file_put_contents( wprss_log_file(), substr( $contents, 0, $limit ) );
199
+ return wprss_get_log();
200
+ } else {
201
+ return $contents;
202
+ }
203
+ }
204
+
205
+
206
+ /**
207
+ * Adds an empty line at the end of the log file.
208
+ *
209
+ * This function is called on wordpress shutdown, if at least one new line
210
+ * is logged in the log file, to separate logs from different page loads.
211
+ *
212
+ * @since 3.9.6
213
+ */
214
+ function wprss_log_separator() {
215
+ file_put_contents( wprss_log_file(), "\n", FILE_APPEND );
216
+ }
217
+
218
+
219
+ /**
220
+ * Adding the default setting value.
221
+ */
222
+ add_filter( 'wprss_default_settings_general', 'wprss_log_default_settings_general' );
223
+ function wprss_log_default_settings_general( $settings ) {
224
+ /* @todo Add version info */
225
+ $settings[ WPRSS_OPTION_CODE_LOG_LEVEL ] = WPRSS_LOG_LEVEL_DEFAULT;
226
+ return $settings;
227
+ }
228
+
229
+
230
+ /**
231
+ * Adding the setting field
232
+ */
233
+ add_filter( 'wprss_settings_array', 'wprss_log_settings_array' );
234
+ function wprss_log_settings_array( $sections ) {
235
+ $sections['general'][ WPRSS_OPTION_CODE_LOG_LEVEL ] = array(
236
+ 'label' => __( 'Log level threshold', WPRSS_TEXT_DOMAIN ),
237
+ 'callback' => 'wprss_setting_' . WPRSS_OPTION_CODE_LOG_LEVEL . '_callback'
238
+ );
239
+ return $sections;
240
+ }
241
+
242
+
243
+ /**
244
+ * Renders the 'log_level' setting field.
245
+ *
246
+ * @param array $field Info about the field
247
+ */
248
+ function wprss_setting_log_level_callback( $field ) {
249
+ $log_level = wprss_get_general_setting( $field['field_id'] );
250
+
251
+ foreach( wprss_log_get_levels( false ) as $_level => $_label ) {
252
+ $options[ $_level ] = $_label;
253
+ if( is_numeric( $_level ) && ($_level/2 >= 1) ) $options[ (int)$_level * -1 ] = $_label . ' and below';
254
+ }
255
+
256
+ krsort( $options, defined( 'SORT_NATURAL' ) ? SORT_NATURAL : SORT_STRING );
257
+ ?>
258
+ <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[<?php echo $field['field_id'] ?>]">
259
+ <?php
260
+ foreach( $options as $value => $text ) {
261
+ $selected = ( (string)$value === (string)$log_level )? 'selected="selected"' : '';
262
+ ?><option value="<?php echo $value ?>" <?php echo $selected ?>><?php echo __( $text, WPRSS_TEXT_DOMAIN ) ?></option><?php
263
+ }
264
+ ?>
265
+ </select>
266
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
267
+ }
268
 
includes/admin-metaboxes.php CHANGED
@@ -1,686 +1,692 @@
1
- <?php
2
-
3
- add_action( 'add_meta_boxes', 'wprss_add_meta_boxes', 99);
4
- /**
5
- * Set up the input boxes for the wprss_feed post type
6
- *
7
- * @since 2.0
8
- */
9
- function wprss_add_meta_boxes() {
10
- global $wprss_meta_fields;
11
-
12
- // Remove the default WordPress Publish box, because we will be using custom ones
13
- remove_meta_box( 'submitdiv', 'wprss_feed', 'side' );
14
-
15
- // Remove some plugin's metaboxes because they're not relevant to the wprss_feed post type.
16
- wprss_remove_unrelated_meta_boxes();
17
-
18
- add_meta_box(
19
- 'submitdiv', // $id
20
- __( 'Save Feed Source', WPRSS_TEXT_DOMAIN ), // $title
21
- 'post_submit_meta_box', // $callback
22
- 'wprss_feed', // $page
23
- 'side', // $context
24
- 'high' // $priority
25
- );
26
-
27
- add_meta_box(
28
- 'preview_meta_box',
29
- __( 'Feed Preview', WPRSS_TEXT_DOMAIN ),
30
- 'wprss_preview_meta_box_callback',
31
- 'wprss_feed',
32
- 'side',
33
- 'high'
34
- );
35
-
36
- add_meta_box(
37
- 'wprss-feed-processing-meta',
38
- __( 'Feed Processing', WPRSS_TEXT_DOMAIN ),
39
- 'wprss_feed_processing_meta_box_callback',
40
- 'wprss_feed',
41
- 'side',
42
- 'high'
43
- );
44
-
45
- if ( !defined('WPRSS_FTP_VERSION') && !defined('WPRSS_ET_VERSION') && !defined('WPRSS_C_VERSION') ) {
46
- add_meta_box(
47
- 'wprss-like-meta',
48
- __( 'Like This Plugin?', WPRSS_TEXT_DOMAIN ),
49
- 'wprss_like_meta_box_callback',
50
- 'wprss_feed',
51
- 'side',
52
- 'low'
53
- );
54
- }
55
-
56
- add_meta_box(
57
- 'custom_meta_box',
58
- __( 'Feed Source Details', WPRSS_TEXT_DOMAIN ),
59
- 'wprss_show_meta_box_callback',
60
- 'wprss_feed',
61
- 'normal',
62
- 'high'
63
- );
64
-
65
- }
66
-
67
-
68
- /**
69
- * Removes some other plugin's metaboxes because they're not relevant to the wprss_feed post type.
70
- *
71
- * @since 4.7
72
- */
73
- function wprss_remove_unrelated_meta_boxes() {
74
- $post_type = 'wprss_feed';
75
- remove_meta_box( 'wpseo_meta', $post_type, 'normal'); // WP SEO Yoast
76
- remove_meta_box( 'ta-reviews-post-meta-box', $post_type, 'normal'); // Author hReview
77
- remove_meta_box( 'wpdf_editor_section', $post_type, 'advanced'); // ImageInject
78
- }
79
-
80
-
81
- /**
82
- * Set up fields for the meta box for the wprss_feed post type
83
- *
84
- * @since 2.0
85
- */
86
- function wprss_get_custom_fields() {
87
- $prefix = 'wprss_';
88
-
89
- // Field Array
90
- $wprss_meta_fields[ 'url' ] = array(
91
- 'label' => __( 'URL', WPRSS_TEXT_DOMAIN ),
92
- 'id' => $prefix .'url',
93
- 'type' => 'url',
94
- 'after' => 'wprss_validate_feed_link',
95
- 'placeholder' => 'http://'
96
- );
97
-
98
- $wprss_meta_fields[ 'limit' ] = array(
99
- 'label' => __( 'Limit', WPRSS_TEXT_DOMAIN ),
100
- 'id' => $prefix . 'limit',
101
- 'type' => 'number'
102
- );
103
-
104
- $wprss_meta_fields[ 'enclosure' ] = array(
105
- 'label' => __( 'Link to enclosure', WPRSS_TEXT_DOMAIN ),
106
- 'id' => $prefix . 'enclosure',
107
- 'type' => 'checkbox'
108
- );
109
-
110
- // for extensibility, allows more meta fields to be added
111
- return apply_filters( 'wprss_fields', $wprss_meta_fields );
112
- }
113
-
114
-
115
- /**
116
- * Set up the meta box for the wprss_feed post type
117
- *
118
- * @since 2.0
119
- */
120
- function wprss_show_meta_box_callback() {
121
- global $post;
122
- $meta_fields = wprss_get_custom_fields();
123
- $field_tooltip_id_prefix = 'field_';
124
- $help = WPRSS_Help::get_instance();
125
-
126
- // Use nonce for verification
127
- wp_nonce_field( basename( __FILE__ ), 'wprss_meta_box_nonce' );
128
-
129
- // Fix for WordpRess SEO JS issue
130
- ?><input type="hidden" id="content" value="" /><?php
131
-
132
- // Begin the field table and loop
133
- ?><table class="form-table wprss-form-table"><?php
134
-
135
- foreach ( $meta_fields as $field ) {
136
- // get value of this field if it exists for this post
137
- $meta = get_post_meta( $post->ID, $field['id'], true );
138
- // begin a table row with
139
- ?><tr>
140
- <th><label for="<?php echo $field['id'] ?>"><?php echo $field['label'] /* Should be already translated */ ?></label></th>
141
- <td><?php
142
-
143
- if ( isset( $field['before'] ) && !empty( $field['before'] ) ) {
144
- call_user_func( $field['before'] );
145
- }
146
-
147
- // Add default placeholder value
148
- $field = wp_parse_args( $field, array(
149
- 'desc' => '',
150
- 'placeholder' => '',
151
- 'type' => 'text'
152
- ) );
153
-
154
- $tooltip = isset( $field['tooltip'] ) ? trim( $field['tooltip'] ) : null;
155
- $tooltip_id = isset( $field['id'] ) ? $field_tooltip_id_prefix . $field['id'] : uniqid( $field_tooltip_id_prefix );
156
-
157
- $field_description = __( $field['desc'], WPRSS_TEXT_DOMAIN );
158
-
159
- /*
160
- * So, here's how tooltips work here.
161
- * Tooltip output will be attempted in any case.
162
- * If 'tooltip' index is not defined, or is null, then
163
- * a registered tooltip will be attempted. If that is
164
- * not found, default value will be output. This value
165
- * is by default an empty string, but can be altered
166
- * by the `tooltip_not_found_handle_html` option of `WPRSS_Help`.
167
- */
168
-
169
- switch( $field['type'] ) {
170
-
171
- // text/url
172
- case 'url':
173
- case 'text':
174
- ?><input type="<?php echo $field['type'] ?>" name="<?php echo $field['id'] ?>" id="<?php echo $field['id'] ?>" value="<?php echo esc_attr( $meta ) ?>" placeholder="<?php _e( $field['placeholder'], WPRSS_TEXT_DOMAIN ) ?>" class="wprss-text-input"/><?php
175
- echo $help->tooltip( $tooltip_id, $tooltip );
176
- if ( strlen( trim( $field['desc'] ) ) > 0 ) {
177
- ?><br /><label for="<?php echo $field['id'] ?>"><span class="description"><?php _e( $field['desc'], WPRSS_TEXT_DOMAIN ) ?></span></label><?php
178
- }
179
- break;
180
-
181
- // textarea
182
- case 'textarea':
183
- ?><textarea name="<?php echo $field['id'] ?>" id="<?php echo $field['id'] ?>" cols="60" rows="4"><?php echo esc_attr( $meta ) ?></textarea><?php
184
- echo $help->tooltip( $tooltip_id, $tooltip );
185
- if ( strlen( trim( $field['desc'] ) ) > 0 ) {
186
- ?><br /><label for="<?php echo $field['id'] ?>"><span class="description"><?php echo $field_description ?></span></label><?php
187
- }
188
- break;
189
-
190
- // checkbox
191
- case 'checkbox':
192
- ?>
193
- <input type="hidden" name="<?php echo $field['id'] ?>" value="false" />
194
- <input type="checkbox" name="<?php echo $field['id'] ?>" id="<?php echo $field['id'] ?>" value="true" <?php checked( $meta, 'true' ) ?> /><?php
195
- echo $help->tooltip( $tooltip_id, $tooltip );
196
- if ( strlen( trim( $field['desc'] ) ) > 0 ) {
197
- ?><label for="<?php echo $field['id'] ?>"><span class="description"><?php echo $field_description ?></span></label><?php
198
- }
199
- break;
200
-
201
- // select
202
- case 'select':
203
- ?><select name="<?php echo $field['id'] ?>" id="<?php $field['id'] ?>"><?php
204
- foreach ($field['options'] as $option) {
205
- ?><option<?php if ( $meta == $option['value'] ): ?> selected="selected"<?php endif ?> value="<?php echo $option['value'] ?>"><?php echo $option['label'] ?></option><?php
206
- }
207
-
208
- ?></select><?php
209
- echo $help->tooltip( $tooltip_id, $tooltip );
210
- if ( strlen( trim( $field['desc'] ) ) > 0 ) {
211
- ?><label for="<?php echo $field['id'] ?>"><span class="description"><?php echo $field_description ?></span></label><?php
212
- }
213
- break;
214
-
215
- // number
216
- case 'number':
217
- ?><input class="wprss-number-roller" type="number" placeholder="<?php _e( 'Default', WPRSS_TEXT_DOMAIN ) ?>" min="0" name="<?php echo $field['id'] ?>" id="<?php echo $field['id'] ?>" value="<?php echo esc_attr( $meta ) ?>" /><?php
218
- echo $help->tooltip( $tooltip_id, $tooltip );
219
- if ( strlen( trim( $field['desc'] ) ) > 0 ) {
220
- ?><label for="<?php echo $field['id'] ?>"><span class="description"><?php echo $field_description ?></span></label><?php
221
- }
222
- break;
223
-
224
- } //end switch
225
-
226
- if ( isset( $field['after'] ) && !empty( $field['after'] ) ) {
227
- call_user_func( $field['after'] );
228
- }
229
-
230
- ?></td></tr><?php
231
- } // end foreach
232
- ?></table><?php
233
- }
234
-
235
-
236
- /**
237
- * Adds the link that validates the feed
238
- * @since 3.9.5
239
- */
240
- function wprss_validate_feed_link() {
241
- ?>
242
- <i id="wprss-url-spinner" class="fa fa-fw fa-refresh fa-spin wprss-updating-feed-icon" title="<?php _e( 'Updating feed source', WPRSS_TEXT_DOMAIN ) ?>"></i>
243
- <div id="wprss-url-error" style="color:red"></div>
244
- <a href="#" id="validate-feed-link">Validate feed</a>
245
- <script type="text/javascript">
246
- (function($){
247
- // When the DOM is ready
248
- $(document).ready( function(){
249
- // Move the link immediately after the url text field, and add the click event handler
250
- $('#validate-feed-link').click(function(e){
251
- // Get the url and proceed only if the url is not empty
252
- var url = $('#wprss_url').val();
253
- if ( url.trim().length > 0 ) {
254
- // Encode the url and generate the full url to the w3 feed validator
255
- var encodedUrl = encodeURIComponent( url );
256
- var fullURL = 'http://validator.w3.org/feed/check.cgi?url=' + encodedUrl;
257
- // Open the window / tab
258
- window.open( fullURL, 'wprss-feed-validator' );
259
- }
260
- // Suppress the default link click behaviour
261
- e.preventDefault();
262
- e.stopPropagation();
263
- return false;
264
- });
265
- });
266
- })(jQuery);
267
- </script>
268
- <?php
269
- }
270
-
271
-
272
-
273
- add_action( 'save_post', 'wprss_save_custom_fields', 10, 2 );
274
- /**
275
- * Save the custom fields
276
- *
277
- * @since 2.0
278
- */
279
- function wprss_save_custom_fields( $post_id, $post ) {
280
- $meta_fields = wprss_get_custom_fields();
281
-
282
- /* Verify the nonce before proceeding. */
283
- if ( !isset( $_POST['wprss_meta_box_nonce'] ) || !wp_verify_nonce( $_POST['wprss_meta_box_nonce'], basename( __FILE__ ) ) )
284
- return $post_id;
285
-
286
- /* Get the post type object. */
287
- $post_type = get_post_type_object( $post->post_type );
288
-
289
- /* Check if the current user has permission to edit the post. */
290
- if ( !current_user_can( $post_type->cap->edit_post, $post_id ) )
291
- return $post_id;
292
-
293
- /* // Stop WP from clearing custom fields on autosave - maybe not needed
294
- if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
295
- return;
296
-
297
- // Prevent quick edit from clearing custom fields - maybe not needed
298
- if (defined('DOING_AJAX') && DOING_AJAX)
299
- return; */
300
-
301
- /** Bail out if running an autosave, ajax or a cron */
302
- if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
303
- return;
304
- if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
305
- return;
306
- if ( defined( 'DOING_CRON' ) && DOING_CRON )
307
- return;
308
-
309
- // Change the limit, if it is zero, to an empty string
310
- if ( isset( $_POST['wprss_limit'] ) && strval( $_POST['wprss_limit'] ) == '0' ) {
311
- $_POST['wprss_limit'] = '';
312
- }
313
-
314
- // loop through fields and save the data
315
- foreach ( $meta_fields as $field ) {
316
- $old = get_post_meta( $post_id, $field[ 'id' ], true );
317
- $new = trim( $_POST[ $field[ 'id' ] ] );
318
- if ( $new && $new != $old ) {
319
- update_post_meta( $post_id, $field[ 'id' ], $new );
320
- } elseif ( '' == $new && $old ) {
321
- delete_post_meta( $post_id, $field[ 'id' ], $old );
322
- }
323
- } // end foreach
324
-
325
- $force_feed = ( isset( $_POST['wprss_force_feed'] ) )? $_POST['wprss_force_feed'] : 'false';
326
- $state = ( isset( $_POST['wprss_state'] ) )? $_POST['wprss_state'] : 'active';
327
- $activate = ( isset( $_POST['wprss_activate_feed'] ) )? stripslashes( $_POST['wprss_activate_feed'] ) : '';
328
- $pause = ( isset( $_POST['wprss_pause_feed'] ) )? stripslashes( $_POST['wprss_pause_feed'] ) : '';
329
- $age_limit = ( isset( $_POST['wprss_age_limit'] ) )? stripslashes( $_POST['wprss_age_limit'] ) : '';
330
- $age_unit = ( isset( $_POST['wprss_age_unit'] ) )? stripslashes( $_POST['wprss_age_unit'] ) : '';
331
- $update_interval = ( isset( $_POST['wprss_update_interval'] ) )? stripslashes( $_POST['wprss_update_interval'] ) : wprss_get_default_feed_source_update_interval();
332
- $old_update_interval = get_post_meta( $post_id, 'wprss_update_interval', TRUE );
333
-
334
- // Update the feed source meta
335
- update_post_meta( $post_id, 'wprss_force_feed', $force_feed );
336
- update_post_meta( $post_id, 'wprss_activate_feed', $activate );
337
- update_post_meta( $post_id, 'wprss_pause_feed', $pause );
338
- update_post_meta( $post_id, 'wprss_age_limit', $age_limit );
339
- update_post_meta( $post_id, 'wprss_age_unit', $age_unit );
340
- update_post_meta( $post_id, 'wprss_update_interval', $update_interval );
341
-
342
- // Check if the state or the update interval has changed
343
- if ( get_post_meta( $post_id, 'wprss_state', TRUE ) !== $state || $old_update_interval !== $update_interval ) {
344
- // Pause the feed source, and if it is active, re-activate it.
345
- // This should update the feed's scheduling
346
- wprss_pause_feed_source( $post_id );
347
- if ( $state === 'active' )
348
- wprss_activate_feed_source( $post_id );
349
- }
350
-
351
- // Update the schedules
352
- wprss_update_feed_processing_schedules( $post_id );
353
-
354
- // If the feed source uses the global updating system, update the feed on publish
355
- if ( $update_interval === wprss_get_default_feed_source_update_interval() ) {
356
- wp_schedule_single_event( time(), 'wprss_fetch_single_feed_hook', array( $post_id ) );
357
- }
358
- }
359
-
360
-
361
- /**
362
- * Generate a preview of the latest 5 posts from the feed source being added/edited
363
- *
364
- * @since 2.0
365
- */
366
- function wprss_preview_meta_box_callback() {
367
- global $post;
368
- $feed_url = get_post_meta( $post->ID, 'wprss_url', true );
369
-
370
- $help = WPRSS_Help::get_instance();
371
- /* @var $help WPRSS_Help */
372
-
373
- echo '<div id="feed-preview-container">';
374
-
375
- if ( ! empty( $feed_url ) ) {
376
- $feed = wprss_fetch_feed( $feed_url, $post->ID );
377
- if ( ! is_wp_error( $feed ) ) {
378
- $items = $feed->get_items();
379
- // Figure out how many total items there are
380
- $total = $feed->get_item_quantity();
381
- // Get the number of items again, but limit it to 5.
382
- $maxitems = $feed->get_item_quantity(5);
383
-
384
- // Build an array of all the items, starting with element 0 (first element).
385
- $items = $feed->get_items( 0, $maxitems );
386
- ?>
387
- <h4><?php echo sprintf( __( 'Latest %1$s feed items out of %2$s available from %3$s' ), $maxitems, $total, get_the_title() ) ?></h4>
388
- <ul>
389
- <?php
390
- foreach ( $items as $item ) {
391
- // Get human date (comment if you want to use non human date)
392
- $has_date = $item->get_date( 'U' ) ? TRUE : FALSE;
393
- if ( $has_date ) {
394
- $item_date = human_time_diff( $item->get_date('U'), current_time('timestamp')).' '.__( 'ago', 'rc_mdm' );
395
- } else {
396
- $item_date = '<em>[' . __( 'No Date', WPRSS_TEXT_DOMAIN ) . ']</em>';
397
- }
398
- // Start displaying item content within a <li> tag
399
- echo '<li>';
400
- // create item link
401
- //echo '<a href="'.esc_url( $item->get_permalink() ).'" title="'.$item_date.'">';
402
- // Get item title
403
- echo esc_html( $item->get_title() );
404
- //echo '</a>';
405
- // Display date
406
- echo ' <div class="rss-date"><small>'.$item_date.'</small></div>';
407
- // End <li> tag
408
- echo '</li>';
409
- }
410
- ?>
411
- </ul>
412
- <?php
413
- }
414
- else {
415
- ?>
416
- <span class="invalid-feed-url">
417
- <?php _e( '<strong>Invalid feed URL</strong> - Double check the feed source URL setting above.', WPRSS_TEXT_DOMAIN ) ?>
418
- <?php wprss_log_obj( 'Failed to preview feed.', $feed->get_error_message(), NULL, WPRSS_LOG_LEVEL_INFO ); ?>
419
- </span>
420
- <?php
421
- echo wpautop( sprintf( __( 'Not sure where to find the RSS feed on a website? <a target="_blank" href="%1$s">Click here</a> for a visual guide. ', WPRSS_TEXT_DOMAIN ), 'http://webtrends.about.com/od/webfeedsyndicationrss/ss/rss_howto.htm' ) );
422
- }
423
-
424
- }
425
- else {
426
- echo '<p>' . __( 'No feed URL defined yet', WPRSS_TEXT_DOMAIN ) . '</p>';
427
- }
428
- echo '</div>';
429
-
430
- echo '<div id="force-feed-container">';
431
- wprss_render_force_feed_option( $post->ID, TRUE );
432
- echo '</div>';
433
- }
434
-
435
-
436
- /**
437
- * Renders the Force Feed option for the Feed Preview.
438
- *
439
- * @param int|string $feed_source_id (Optional) The ID of the feed source for the option will be rendered. If not given or
440
- * its value is null, the option will not be checked.
441
- * @param bool $echo (Optional) If set to true, the function will immediately echo the option,
442
- * rather than return a string of the option's markup. Default: False.
443
- * @return string|null A string containing the HTML for the rendered option if $echo is set to false,
444
- * or null if $echo is set to true.
445
- * @since 4.6.12
446
- */
447
- function wprss_render_force_feed_option( $feed_source_id = NULL, $echo = FALSE ) {
448
- if ( ! $echo ) ob_start();
449
- $force_feed = $feed_source_id === NULL ? '' : get_post_meta( $feed_source_id, 'wprss_force_feed', TRUE ); ?>
450
- <p>
451
- <label for="wprss-force-feed"><?php _e('Force the feed') ?></label>
452
- <input type="hidden" name="wprss_force_feed" value="false" />
453
- <input type="checkbox" name="wprss_force_feed" id="wprss-force-feed" value="true" <?php echo checked( $force_feed, 'true' ); ?> />
454
- <?php echo WPRSS_Help::get_instance()->tooltip( 'field_wprss_force_feed' ) ?>
455
- </p>
456
- <?php
457
- if ( ! $echo ) return ob_get_clean();
458
- return NULL;
459
- }
460
-
461
-
462
- /**
463
- * Renders the Feed Processing metabox
464
- *
465
- * @since 3.7
466
- */
467
- function wprss_feed_processing_meta_box_callback() {
468
- global $post;
469
- // Get the post meta
470
- $state = get_post_meta( $post->ID, 'wprss_state', TRUE );
471
- $activate = get_post_meta( $post->ID, 'wprss_activate_feed', TRUE );
472
- $pause = get_post_meta( $post->ID, 'wprss_pause_feed', TRUE );
473
- $update_interval = get_post_meta( $post->ID, 'wprss_update_interval', TRUE );
474
-
475
- $age_limit = get_post_meta( $post->ID, 'wprss_age_limit', FALSE );
476
- $age_unit = get_post_meta( $post->ID, 'wprss_age_unit', FALSE );
477
-
478
- $age_limit = ( count( $age_limit ) === 0 )? wprss_get_general_setting( 'limit_feed_items_age' ) : $age_limit[0];
479
- $age_unit = ( count( $age_unit ) === 0 )? wprss_get_general_setting( 'limit_feed_items_age_unit' ) : $age_unit[0];
480
-
481
- // Set default strings for activate and pause times
482
- $default_activate = 'immediately';
483
- $default_pause = 'never';
484
-
485
- // Prepare the states
486
- $states = array(
487
- 'active' => __( 'Active', WPRSS_TEXT_DOMAIN ),
488
- 'paused' => __( 'Paused', WPRSS_TEXT_DOMAIN ),
489
- );
490
-
491
- // Prepare the schedules
492
- $default_interval = __( 'Default', WPRSS_TEXT_DOMAIN );
493
- $wprss_schedules = apply_filters( 'wprss_schedules', wprss_get_schedules() );
494
- $default_interval_key = wprss_get_default_feed_source_update_interval();
495
- $schedules = array_merge(
496
- array(
497
- $default_interval_key => array(
498
- 'display' => $default_interval,
499
- 'interval' => $default_interval,
500
- ),
501
- ),
502
- $wprss_schedules
503
- );
504
-
505
- // Inline help
506
- $help = WPRSS_Help::get_instance();
507
- $help_options = array('tooltip_handle_class_extra' => $help->get_options('tooltip_handle_class_extra') . ' ' . $help->get_options('tooltip_handle_class') . '-side');
508
-
509
- ?>
510
-
511
- <div class="wprss-meta-side-setting">
512
- <label for="wprss_state">Feed state:</label>
513
- <select id="wprss_state" name="wprss_state">
514
- <?php foreach( $states as $value => $label ) : ?>
515
- <option value="<?php echo $value; ?>" <?php selected( $state, $value ) ?> ><?php echo $label; ?></option>
516
- <?php endforeach; ?>
517
- </select>
518
- <?php echo $help->tooltip( 'field_wprss_state', null, $help_options ) ?>
519
- </div>
520
-
521
- <div class="wprss-meta-side-setting">
522
- <p>
523
- <label for="">Activate feed: </label>
524
- <strong id="wprss-activate-feed-viewer"><?php echo ( ( $activate !== '' )? $activate : $default_activate ); ?></strong>
525
- <a href="#">Edit</a>
526
- <?php echo $help->tooltip( 'field_wprss_activate_feed', null, $help_options ) ?>
527
- </p>
528
- <div class="wprss-meta-slider" data-collapse-viewer="wprss-activate-feed-viewer" data-default-value="<?php echo $default_activate; ?>">
529
- <input id="wprss_activate_feed" class="wprss-datetimepicker-from-today" name="wprss_activate_feed" value="<?php echo $activate; ?>" />
530
- <span class="description">
531
- Current UTC time is:<br/><code><?php echo date( 'd/m/Y H:i:s', current_time('timestamp',1) ); ?></code>
532
- </span>
533
- </div>
534
- </div>
535
-
536
- <div class="wprss-meta-side-setting">
537
- <p>
538
- <label for="">Pause feed: </label>
539
- <strong id="wprss-pause-feed-viewer"><?php echo ( ( $pause !== '' )? $pause : $default_pause ); ?></strong>
540
- <a href="#">Edit</a>
541
- <?php echo $help->tooltip( 'field_wprss_pause_feed', null, $help_options ) ?>
542
- </p>
543
- <div class="wprss-meta-slider" data-collapse-viewer="wprss-pause-feed-viewer" data-default-value="<?php echo $default_pause; ?>">
544
- <input id="wprss_pause_feed" class="wprss-datetimepicker-from-today" name="wprss_pause_feed" value="<?php echo $pause; ?>" />
545
- <span class="description">
546
- Current UTC time is:<br/><code><?php echo date( 'd/m/Y H:i:s', current_time('timestamp',1) ); ?></code>
547
- </span>
548
- </div>
549
- </div>
550
-
551
-
552
- <div class="wprss-meta-side-setting">
553
- <p>
554
- <label for="">Update interval: </label>
555
- <strong id="wprss-feed-update-interval-viewer">
556
- <?php
557
- if ( $update_interval === '' || $update_interval === wprss_get_default_feed_source_update_interval() ) {
558
- echo $default_interval;
559
- }
560
- else {
561
- echo wprss_interval( $schedules[$update_interval]['interval'] );
562
- }
563
- ?>
564
- </strong>
565
- <a href="#">Edit</a>
566
- <?php echo $help->tooltip( 'field_wprss_update_interval', null, $help_options ) ?>
567
- </p>
568
- <div class="wprss-meta-slider" data-collapse-viewer="wprss-feed-update-interval-viewer" data-default-value="<?php echo $default_interval; ?>">
569
- <select id="feed-update-interval" name="wprss_update_interval">
570
- <?php foreach ( $schedules as $value => $schedule ) : ?>
571
- <?php $text = ( $value === wprss_get_default_feed_source_update_interval() )? $default_interval : wprss_interval( $schedule['interval'] ); ?>
572
- <option value="<?php echo $value; ?>" <?php selected( $update_interval, $value ); ?> ><?php echo $text; ?></option>
573
- <?php endforeach; ?>
574
- </select>
575
- </div>
576
- </div>
577
-
578
-
579
- <div class="wprss-meta-side-setting">
580
- <p>
581
- <label id="wprss-age-limit-feed-label" for="" data-when-empty="Delete old feed items:">Delete feed items older than: </label>
582
- <strong id="wprss-age-limit-feed-viewer"><?php echo $age_limit . ' ' . $age_unit; ?></strong>
583
- <a href="#">Edit</a>
584
- <?php echo $help->tooltip( 'field_wprss_age_limit', null, $help_options ) ?>
585
- </p>
586
- <div class="wprss-meta-slider" data-collapse-viewer="wprss-age-limit-feed-viewer" data-label="#wprss-age-limit-feed-label" data-default-value="" data-empty-controller="#limit-feed-items-age" data-hybrid="#limit-feed-items-age, #limit-feed-items-age-unit">
587
- <input id="limit-feed-items-age" name="wprss_age_limit" type="number" min="0" class="wprss-number-roller" placeholder="No limit" value="<?php echo $age_limit; ?>" />
588
-
589
- <select id="limit-feed-items-age-unit" name="wprss_age_unit">
590
- <?php foreach ( wprss_age_limit_units() as $unit ) : ?>
591
- <option value="<?php echo $unit; ?>" <?php selected( $age_unit, $unit ); ?> ><?php echo $unit; ?></option>
592
- <?php endforeach; ?>
593
- </select>
594
- </div>
595
- </div>
596
-
597
-
598
- <?php
599
- }
600
-
601
-
602
-
603
- /**
604
- * Generate Help meta box
605
- *
606
- * @since 2.0
607
- *
608
- */
609
- function wprss_help_meta_box_callback() {
610
- echo '<p><a href="http://www.wprssaggregator.com/documentation/">View the documentation</p>';
611
- echo '<p><strong>';
612
- _e( 'Need help?', WPRSS_TEXT_DOMAIN );
613
- echo '</strong> <a target="_blank" href="http://wordpress.org/support/plugin/wp-rss-aggregator">';
614
- _e( 'Check out the support forum', WPRSS_TEXT_DOMAIN );
615
- echo '</a></p>';
616
- echo '</strong> <a target="_blank" href="http://www.wprssaggregator.com/feature-requests/">';
617
- _e( 'Suggest a new feature', WPRSS_TEXT_DOMAIN );
618
- echo '</a></p>';
619
- }
620
-
621
- /**
622
- * Generate Like this plugin meta box
623
- *
624
- * @since 2.0
625
- *
626
- */
627
- function wprss_like_meta_box_callback() { ?>
628
-
629
- <ul>
630
- <li><a href="http://wordpress.org/extend/plugins/wp-rss-aggregator/"><?php _e( 'Give it a 5 star rating on WordPress.org', WPRSS_TEXT_DOMAIN ) ?></a></li>
631
- <li class="donate_link"><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=X9GP6BL4BLXBJ"><?php _e( 'Donate a token of your appreciation', WPRSS_TEXT_DOMAIN ); ?></a></li>
632
- </ul>
633
- <?php
634
- echo '<p><strong>';
635
- _e( 'Check out the Premium Extensions:', WPRSS_TEXT_DOMAIN );
636
- echo '</strong>'; ?>
637
- <ul>
638
- <li><a href="http://www.wprssaggregator.com/extension/feed-to-post/"><?php echo 'Feed to Post'; ?></a></li>
639
- <li><a href="http://www.wprssaggregator.com/extension/excerpts-thumbnails/"><?php echo 'Excerpts & Thumbnails'; ?></a></li>
640
- <li><a href="http://www.wprssaggregator.com/extension/categories/"><?php echo 'Categories'; ?></a></li>
641
- <li><a href="http://www.wprssaggregator.com/extension/keyword-filtering/"><?php echo 'Keyword Filtering'; ?></a></li>
642
- </ul>
643
- </p>
644
- <?php }
645
-
646
-
647
- /**
648
- * Generate Follow us plugin meta box
649
- *
650
- * @since 2.0
651
- *
652
- */
653
- function wprss_follow_meta_box_callback() {
654
- ?>
655
- <ul>
656
- <li class="twitter"><a href="http://twitter.com/wpmayor"><?php _e( 'Follow WP Mayor on Twitter.', WPRSS_TEXT_DOMAIN ) ?></a></li>
657
- <li class="facebook"><a href="https://www.facebook.com/wpmayor"><?php _e( 'Like WP Mayor on Facebook.', WPRSS_TEXT_DOMAIN ) ?></a></li>
658
- </ul>
659
- <?php }
660
-
661
-
662
- add_action( 'add_meta_boxes', 'wprss_remove_meta_boxes', 100 );
663
- /**
664
- * Remove unneeded meta boxes from add feed source screen
665
- *
666
- * @since 2.0
667
- */
668
- function wprss_remove_meta_boxes() {
669
- if ( 'wprss_feed' !== get_current_screen()->id ) return;
670
- // Remove meta boxes of other plugins that tend to appear on all posts
671
- //remove_meta_box( 'wpseo_meta', 'wprss_feed' ,'normal' );
672
- remove_meta_box( 'postpsp', 'wprss_feed' ,'normal' );
673
- remove_meta_box( 'su_postmeta', 'wprss_feed' ,'normal' );
674
- remove_meta_box( 'woothemes-settings', 'wprss_feed' ,'normal' );
675
- remove_meta_box( 'wpcf-post-relationship', 'wprss_feed' ,'normal' );
676
- remove_meta_box( 'wpar_plugin_meta_box ', 'wprss_feed' ,'normal' );
677
- remove_meta_box( 'sharing_meta', 'wprss_feed' ,'advanced' );
678
- remove_meta_box( 'content-permissions-meta-box', 'wprss_feed' ,'advanced' );
679
- remove_meta_box( 'theme-layouts-post-meta-box', 'wprss_feed' ,'side' );
680
- remove_meta_box( 'post-stylesheets', 'wprss_feed' ,'side' );
681
- remove_meta_box( 'hybrid-core-post-template', 'wprss_feed' ,'side' );
682
- remove_meta_box( 'wpcf-marketing', 'wprss_feed' ,'side' );
683
- remove_meta_box( 'trackbacksdiv22', 'wprss_feed' ,'advanced' );
684
- remove_meta_box( 'aiosp', 'wprss_feed' ,'advanced' );
685
- remove_action( 'post_submitbox_start', 'fpp_post_submitbox_start_action' );
686
- }
 
 
 
 
 
 
1
+ <?php
2
+
3
+ add_action( 'add_meta_boxes', 'wprss_add_meta_boxes', 99);
4
+ /**
5
+ * Set up the input boxes for the wprss_feed post type
6
+ *
7
+ * @since 2.0
8
+ */
9
+ function wprss_add_meta_boxes() {
10
+ global $wprss_meta_fields;
11
+
12
+ // Remove the default WordPress Publish box, because we will be using custom ones
13
+ remove_meta_box( 'submitdiv', 'wprss_feed', 'side' );
14
+
15
+ // Remove some plugin's metaboxes because they're not relevant to the wprss_feed post type.
16
+ wprss_remove_unrelated_meta_boxes();
17
+
18
+ add_meta_box(
19
+ 'submitdiv', // $id
20
+ __( 'Save Feed Source', WPRSS_TEXT_DOMAIN ), // $title
21
+ 'post_submit_meta_box', // $callback
22
+ 'wprss_feed', // $page
23
+ 'side', // $context
24
+ 'high' // $priority
25
+ );
26
+
27
+ add_meta_box(
28
+ 'preview_meta_box',
29
+ __( 'Feed Preview', WPRSS_TEXT_DOMAIN ),
30
+ 'wprss_preview_meta_box_callback',
31
+ 'wprss_feed',
32
+ 'side',
33
+ 'high'
34
+ );
35
+
36
+ add_meta_box(
37
+ 'wprss-feed-processing-meta',
38
+ __( 'Feed Processing', WPRSS_TEXT_DOMAIN ),
39
+ 'wprss_feed_processing_meta_box_callback',
40
+ 'wprss_feed',
41
+ 'side',
42
+ 'high'
43
+ );
44
+
45
+ if ( !defined('WPRSS_FTP_VERSION') && !defined('WPRSS_ET_VERSION') && !defined('WPRSS_C_VERSION') ) {
46
+ add_meta_box(
47
+ 'wprss-like-meta',
48
+ __( 'Like This Plugin?', WPRSS_TEXT_DOMAIN ),
49
+ 'wprss_like_meta_box_callback',
50
+ 'wprss_feed',
51
+ 'side',
52
+ 'low'
53
+ );
54
+ }
55
+
56
+ add_meta_box(
57
+ 'custom_meta_box',
58
+ __( 'Feed Source Details', WPRSS_TEXT_DOMAIN ),
59
+ 'wprss_show_meta_box_callback',
60
+ 'wprss_feed',
61
+ 'normal',
62
+ 'high'
63
+ );
64
+
65
+ }
66
+
67
+
68
+ /**
69
+ * Removes some other plugin's metaboxes because they're not relevant to the wprss_feed post type.
70
+ *
71
+ * @since 4.7
72
+ */
73
+ function wprss_remove_unrelated_meta_boxes() {
74
+ $post_type = 'wprss_feed';
75
+ remove_meta_box( 'wpseo_meta', $post_type, 'normal'); // WP SEO Yoast
76
+ remove_meta_box( 'ta-reviews-post-meta-box', $post_type, 'normal'); // Author hReview
77
+ remove_meta_box( 'wpdf_editor_section', $post_type, 'advanced'); // ImageInject
78
+ }
79
+
80
+
81
+ /**
82
+ * Set up fields for the meta box for the wprss_feed post type
83
+ *
84
+ * @since 2.0
85
+ */
86
+ function wprss_get_custom_fields() {
87
+ $prefix = 'wprss_';
88
+
89
+ // Field Array
90
+ $wprss_meta_fields[ 'url' ] = array(
91
+ 'label' => __( 'URL', WPRSS_TEXT_DOMAIN ),
92
+ 'id' => $prefix .'url',
93
+ 'type' => 'url',
94
+ 'after' => 'wprss_validate_feed_link',
95
+ 'placeholder' => 'http://'
96
+ );
97
+
98
+ $wprss_meta_fields[ 'limit' ] = array(
99
+ 'label' => __( 'Limit', WPRSS_TEXT_DOMAIN ),
100
+ 'id' => $prefix . 'limit',
101
+ 'type' => 'number'
102
+ );
103
+
104
+ $wprss_meta_fields[ 'enclosure' ] = array(
105
+ 'label' => __( 'Link to enclosure', WPRSS_TEXT_DOMAIN ),
106
+ 'id' => $prefix . 'enclosure',
107
+ 'type' => 'checkbox'
108
+ );
109
+
110
+ $wprss_meta_fields[ 'unique_titles' ] = array(
111
+ 'label' => __( 'Unique titles only', WPRSS_TEXT_DOMAIN ),
112
+ 'id' => $prefix . 'unique_titles',
113
+ 'type' => 'checkbox'
114
+ );
115
+
116
+ // for extensibility, allows more meta fields to be added
117
+ return apply_filters( 'wprss_fields', $wprss_meta_fields );
118
+ }
119
+
120
+
121
+ /**
122
+ * Set up the meta box for the wprss_feed post type
123
+ *
124
+ * @since 2.0
125
+ */
126
+ function wprss_show_meta_box_callback() {
127
+ global $post;
128
+ $meta_fields = wprss_get_custom_fields();
129
+ $field_tooltip_id_prefix = 'field_';
130
+ $help = WPRSS_Help::get_instance();
131
+
132
+ // Use nonce for verification
133
+ wp_nonce_field( basename( __FILE__ ), 'wprss_meta_box_nonce' );
134
+
135
+ // Fix for WordpRess SEO JS issue
136
+ ?><input type="hidden" id="content" value="" /><?php
137
+
138
+ // Begin the field table and loop
139
+ ?><table class="form-table wprss-form-table"><?php
140
+
141
+ foreach ( $meta_fields as $field ) {
142
+ // get value of this field if it exists for this post
143
+ $meta = get_post_meta( $post->ID, $field['id'], true );
144
+ // begin a table row with
145
+ ?><tr>
146
+ <th><label for="<?php echo $field['id'] ?>"><?php echo $field['label'] /* Should be already translated */ ?></label></th>
147
+ <td><?php
148
+
149
+ if ( isset( $field['before'] ) && !empty( $field['before'] ) ) {
150
+ call_user_func( $field['before'] );
151
+ }
152
+
153
+ // Add default placeholder value
154
+ $field = wp_parse_args( $field, array(
155
+ 'desc' => '',
156
+ 'placeholder' => '',
157
+ 'type' => 'text'
158
+ ) );
159
+
160
+ $tooltip = isset( $field['tooltip'] ) ? trim( $field['tooltip'] ) : null;
161
+ $tooltip_id = isset( $field['id'] ) ? $field_tooltip_id_prefix . $field['id'] : uniqid( $field_tooltip_id_prefix );
162
+
163
+ $field_description = __( $field['desc'], WPRSS_TEXT_DOMAIN );
164
+
165
+ /*
166
+ * So, here's how tooltips work here.
167
+ * Tooltip output will be attempted in any case.
168
+ * If 'tooltip' index is not defined, or is null, then
169
+ * a registered tooltip will be attempted. If that is
170
+ * not found, default value will be output. This value
171
+ * is by default an empty string, but can be altered
172
+ * by the `tooltip_not_found_handle_html` option of `WPRSS_Help`.
173
+ */
174
+
175
+ switch( $field['type'] ) {
176
+
177
+ // text/url
178
+ case 'url':
179
+ case 'text':
180
+ ?><input type="<?php echo $field['type'] ?>" name="<?php echo $field['id'] ?>" id="<?php echo $field['id'] ?>" value="<?php echo esc_attr( $meta ) ?>" placeholder="<?php _e( $field['placeholder'], WPRSS_TEXT_DOMAIN ) ?>" class="wprss-text-input"/><?php
181
+ echo $help->tooltip( $tooltip_id, $tooltip );
182
+ if ( strlen( trim( $field['desc'] ) ) > 0 ) {
183
+ ?><br /><label for="<?php echo $field['id'] ?>"><span class="description"><?php _e( $field['desc'], WPRSS_TEXT_DOMAIN ) ?></span></label><?php
184
+ }
185
+ break;
186
+
187
+ // textarea
188
+ case 'textarea':
189
+ ?><textarea name="<?php echo $field['id'] ?>" id="<?php echo $field['id'] ?>" cols="60" rows="4"><?php echo esc_attr( $meta ) ?></textarea><?php
190
+ echo $help->tooltip( $tooltip_id, $tooltip );
191
+ if ( strlen( trim( $field['desc'] ) ) > 0 ) {
192
+ ?><br /><label for="<?php echo $field['id'] ?>"><span class="description"><?php echo $field_description ?></span></label><?php
193
+ }
194
+ break;
195
+
196
+ // checkbox
197
+ case 'checkbox':
198
+ ?>
199
+ <input type="hidden" name="<?php echo $field['id'] ?>" value="false" />
200
+ <input type="checkbox" name="<?php echo $field['id'] ?>" id="<?php echo $field['id'] ?>" value="true" <?php checked( $meta, 'true' ) ?> /><?php
201
+ echo $help->tooltip( $tooltip_id, $tooltip );
202
+ if ( strlen( trim( $field['desc'] ) ) > 0 ) {
203
+ ?><label for="<?php echo $field['id'] ?>"><span class="description"><?php echo $field_description ?></span></label><?php
204
+ }
205
+ break;
206
+
207
+ // select
208
+ case 'select':
209
+ ?><select name="<?php echo $field['id'] ?>" id="<?php $field['id'] ?>"><?php
210
+ foreach ($field['options'] as $option) {
211
+ ?><option<?php if ( $meta == $option['value'] ): ?> selected="selected"<?php endif ?> value="<?php echo $option['value'] ?>"><?php echo $option['label'] ?></option><?php
212
+ }
213
+
214
+ ?></select><?php
215
+ echo $help->tooltip( $tooltip_id, $tooltip );
216
+ if ( strlen( trim( $field['desc'] ) ) > 0 ) {
217
+ ?><label for="<?php echo $field['id'] ?>"><span class="description"><?php echo $field_description ?></span></label><?php
218
+ }
219
+ break;
220
+
221
+ // number
222
+ case 'number':
223
+ ?><input class="wprss-number-roller" type="number" placeholder="<?php _e( 'Default', WPRSS_TEXT_DOMAIN ) ?>" min="0" name="<?php echo $field['id'] ?>" id="<?php echo $field['id'] ?>" value="<?php echo esc_attr( $meta ) ?>" /><?php
224
+ echo $help->tooltip( $tooltip_id, $tooltip );
225
+ if ( strlen( trim( $field['desc'] ) ) > 0 ) {
226
+ ?><label for="<?php echo $field['id'] ?>"><span class="description"><?php echo $field_description ?></span></label><?php
227
+ }
228
+ break;
229
+
230
+ } //end switch
231
+
232
+ if ( isset( $field['after'] ) && !empty( $field['after'] ) ) {
233
+ call_user_func( $field['after'] );
234
+ }
235
+
236
+ ?></td></tr><?php
237
+ } // end foreach
238
+ ?></table><?php
239
+ }
240
+
241
+
242
+ /**
243
+ * Adds the link that validates the feed
244
+ * @since 3.9.5
245
+ */
246
+ function wprss_validate_feed_link() {
247
+ ?>
248
+ <i id="wprss-url-spinner" class="fa fa-fw fa-refresh fa-spin wprss-updating-feed-icon" title="<?php _e( 'Updating feed source', WPRSS_TEXT_DOMAIN ) ?>"></i>
249
+ <div id="wprss-url-error" style="color:red"></div>
250
+ <a href="#" id="validate-feed-link">Validate feed</a>
251
+ <script type="text/javascript">
252
+ (function($){
253
+ // When the DOM is ready
254
+ $(document).ready( function(){
255
+ // Move the link immediately after the url text field, and add the click event handler
256
+ $('#validate-feed-link').click(function(e){
257
+ // Get the url and proceed only if the url is not empty
258
+ var url = $('#wprss_url').val();
259
+ if ( url.trim().length > 0 ) {
260
+ // Encode the url and generate the full url to the w3 feed validator
261
+ var encodedUrl = encodeURIComponent( url );
262
+ var fullURL = 'http://validator.w3.org/feed/check.cgi?url=' + encodedUrl;
263
+ // Open the window / tab
264
+ window.open( fullURL, 'wprss-feed-validator' );
265
+ }
266
+ // Suppress the default link click behaviour
267
+ e.preventDefault();
268
+ e.stopPropagation();
269
+ return false;
270
+ });
271
+ });
272
+ })(jQuery);
273
+ </script>
274
+ <?php
275
+ }
276
+
277
+
278
+
279
+ add_action( 'save_post', 'wprss_save_custom_fields', 10, 2 );
280
+ /**
281
+ * Save the custom fields
282
+ *
283
+ * @since 2.0
284
+ */
285
+ function wprss_save_custom_fields( $post_id, $post ) {
286
+ $meta_fields = wprss_get_custom_fields();
287
+
288
+ /* Verify the nonce before proceeding. */
289
+ if ( !isset( $_POST['wprss_meta_box_nonce'] ) || !wp_verify_nonce( $_POST['wprss_meta_box_nonce'], basename( __FILE__ ) ) )
290
+ return $post_id;
291
+
292
+ /* Get the post type object. */
293
+ $post_type = get_post_type_object( $post->post_type );
294
+
295
+ /* Check if the current user has permission to edit the post. */
296
+ if ( !current_user_can( $post_type->cap->edit_post, $post_id ) )
297
+ return $post_id;
298
+
299
+ /* // Stop WP from clearing custom fields on autosave - maybe not needed
300
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE)
301
+ return;
302
+
303
+ // Prevent quick edit from clearing custom fields - maybe not needed
304
+ if (defined('DOING_AJAX') && DOING_AJAX)
305
+ return; */
306
+
307
+ /** Bail out if running an autosave, ajax or a cron */
308
+ if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
309
+ return;
310
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX )
311
+ return;
312
+ if ( defined( 'DOING_CRON' ) && DOING_CRON )
313
+ return;
314
+
315
+ // Change the limit, if it is zero, to an empty string
316
+ if ( isset( $_POST['wprss_limit'] ) && strval( $_POST['wprss_limit'] ) == '0' ) {
317
+ $_POST['wprss_limit'] = '';
318
+ }
319
+
320
+ // loop through fields and save the data
321
+ foreach ( $meta_fields as $field ) {
322
+ $old = get_post_meta( $post_id, $field[ 'id' ], true );
323
+ $new = trim( $_POST[ $field[ 'id' ] ] );
324
+ if ( $new && $new != $old ) {
325
+ update_post_meta( $post_id, $field[ 'id' ], $new );
326
+ } elseif ( '' == $new && $old ) {
327
+ delete_post_meta( $post_id, $field[ 'id' ], $old );
328
+ }
329
+ } // end foreach
330
+
331
+ $force_feed = ( isset( $_POST['wprss_force_feed'] ) )? $_POST['wprss_force_feed'] : 'false';
332
+ $state = ( isset( $_POST['wprss_state'] ) )? $_POST['wprss_state'] : 'active';
333
+ $activate = ( isset( $_POST['wprss_activate_feed'] ) )? stripslashes( $_POST['wprss_activate_feed'] ) : '';
334
+ $pause = ( isset( $_POST['wprss_pause_feed'] ) )? stripslashes( $_POST['wprss_pause_feed'] ) : '';
335
+ $age_limit = ( isset( $_POST['wprss_age_limit'] ) )? stripslashes( $_POST['wprss_age_limit'] ) : '';
336
+ $age_unit = ( isset( $_POST['wprss_age_unit'] ) )? stripslashes( $_POST['wprss_age_unit'] ) : '';
337
+ $update_interval = ( isset( $_POST['wprss_update_interval'] ) )? stripslashes( $_POST['wprss_update_interval'] ) : wprss_get_default_feed_source_update_interval();
338
+ $old_update_interval = get_post_meta( $post_id, 'wprss_update_interval', TRUE );
339
+
340
+ // Update the feed source meta
341
+ update_post_meta( $post_id, 'wprss_force_feed', $force_feed );
342
+ update_post_meta( $post_id, 'wprss_activate_feed', $activate );
343
+ update_post_meta( $post_id, 'wprss_pause_feed', $pause );
344
+ update_post_meta( $post_id, 'wprss_age_limit', $age_limit );
345
+ update_post_meta( $post_id, 'wprss_age_unit', $age_unit );
346
+ update_post_meta( $post_id, 'wprss_update_interval', $update_interval );
347
+
348
+ // Check if the state or the update interval has changed
349
+ if ( get_post_meta( $post_id, 'wprss_state', TRUE ) !== $state || $old_update_interval !== $update_interval ) {
350
+ // Pause the feed source, and if it is active, re-activate it.
351
+ // This should update the feed's scheduling
352
+ wprss_pause_feed_source( $post_id );
353
+ if ( $state === 'active' )
354
+ wprss_activate_feed_source( $post_id );
355
+ }
356
+
357
+ // Update the schedules
358
+ wprss_update_feed_processing_schedules( $post_id );
359
+
360
+ // If the feed source uses the global updating system, update the feed on publish
361
+ if ( $update_interval === wprss_get_default_feed_source_update_interval() ) {
362
+ wp_schedule_single_event( time(), 'wprss_fetch_single_feed_hook', array( $post_id ) );
363
+ }
364
+ }
365
+
366
+
367
+ /**
368
+ * Generate a preview of the latest 5 posts from the feed source being added/edited
369
+ *
370
+ * @since 2.0
371
+ */
372
+ function wprss_preview_meta_box_callback() {
373
+ global $post;
374
+ $feed_url = get_post_meta( $post->ID, 'wprss_url', true );
375
+
376
+ $help = WPRSS_Help::get_instance();
377
+ /* @var $help WPRSS_Help */
378
+
379
+ echo '<div id="feed-preview-container">';
380
+
381
+ if ( ! empty( $feed_url ) ) {
382
+ $feed = wprss_fetch_feed( $feed_url, $post->ID );
383
+ if ( ! is_wp_error( $feed ) ) {
384
+ $items = $feed->get_items();
385
+ // Figure out how many total items there are
386
+ $total = $feed->get_item_quantity();
387
+ // Get the number of items again, but limit it to 5.
388
+ $maxitems = $feed->get_item_quantity(5);
389
+
390
+ // Build an array of all the items, starting with element 0 (first element).
391
+ $items = $feed->get_items( 0, $maxitems );
392
+ ?>
393
+ <h4><?php echo sprintf( __( 'Latest %1$s feed items out of %2$s available from %3$s' ), $maxitems, $total, get_the_title() ) ?></h4>
394
+ <ul>
395
+ <?php
396
+ foreach ( $items as $item ) {
397
+ // Get human date (comment if you want to use non human date)
398
+ $has_date = $item->get_date( 'U' ) ? TRUE : FALSE;
399
+ if ( $has_date ) {
400
+ $item_date = human_time_diff( $item->get_date('U'), current_time('timestamp')).' '.__( 'ago', 'rc_mdm' );
401
+ } else {
402
+ $item_date = '<em>[' . __( 'No Date', WPRSS_TEXT_DOMAIN ) . ']</em>';
403
+ }
404
+ // Start displaying item content within a <li> tag
405
+ echo '<li>';
406
+ // create item link
407
+ //echo '<a href="'.esc_url( $item->get_permalink() ).'" title="'.$item_date.'">';
408
+ // Get item title
409
+ echo esc_html( $item->get_title() );
410
+ //echo '</a>';
411
+ // Display date
412
+ echo ' <div class="rss-date"><small>'.$item_date.'</small></div>';
413
+ // End <li> tag
414
+ echo '</li>';
415
+ }
416
+ ?>
417
+ </ul>
418
+ <?php
419
+ }
420
+ else {
421
+ ?>
422
+ <span class="invalid-feed-url">
423
+ <?php _e( '<strong>Invalid feed URL</strong> - Double check the feed source URL setting above.', WPRSS_TEXT_DOMAIN ) ?>
424
+ <?php wprss_log_obj( 'Failed to preview feed.', $feed->get_error_message(), NULL, WPRSS_LOG_LEVEL_INFO ); ?>
425
+ </span>
426
+ <?php
427
+ echo wpautop( sprintf( __( 'Not sure where to find the RSS feed on a website? <a target="_blank" href="%1$s">Click here</a> for a visual guide. ', WPRSS_TEXT_DOMAIN ), 'http://webtrends.about.com/od/webfeedsyndicationrss/ss/rss_howto.htm' ) );
428
+ }
429
+
430
+ }
431
+ else {
432
+ echo '<p>' . __( 'No feed URL defined yet', WPRSS_TEXT_DOMAIN ) . '</p>';
433
+ }
434
+ echo '</div>';
435
+
436
+ echo '<div id="force-feed-container">';
437
+ wprss_render_force_feed_option( $post->ID, TRUE );
438
+ echo '</div>';
439
+ }
440
+
441
+
442
+ /**
443
+ * Renders the Force Feed option for the Feed Preview.
444
+ *
445
+ * @param int|string $feed_source_id (Optional) The ID of the feed source for the option will be rendered. If not given or
446
+ * its value is null, the option will not be checked.
447
+ * @param bool $echo (Optional) If set to true, the function will immediately echo the option,
448
+ * rather than return a string of the option's markup. Default: False.
449
+ * @return string|null A string containing the HTML for the rendered option if $echo is set to false,
450
+ * or null if $echo is set to true.
451
+ * @since 4.6.12
452
+ */
453
+ function wprss_render_force_feed_option( $feed_source_id = NULL, $echo = FALSE ) {
454
+ if ( ! $echo ) ob_start();
455
+ $force_feed = $feed_source_id === NULL ? '' : get_post_meta( $feed_source_id, 'wprss_force_feed', TRUE ); ?>
456
+ <p>
457
+ <label for="wprss-force-feed"><?php _e('Force the feed') ?></label>
458
+ <input type="hidden" name="wprss_force_feed" value="false" />
459
+ <input type="checkbox" name="wprss_force_feed" id="wprss-force-feed" value="true" <?php echo checked( $force_feed, 'true' ); ?> />
460
+ <?php echo WPRSS_Help::get_instance()->tooltip( 'field_wprss_force_feed' ) ?>
461
+ </p>
462
+ <?php
463
+ if ( ! $echo ) return ob_get_clean();
464
+ return NULL;
465
+ }
466
+
467
+
468
+ /**
469
+ * Renders the Feed Processing metabox
470
+ *
471
+ * @since 3.7
472
+ */
473
+ function wprss_feed_processing_meta_box_callback() {
474
+ global $post;
475
+ // Get the post meta
476
+ $state = get_post_meta( $post->ID, 'wprss_state', TRUE );
477
+ $activate = get_post_meta( $post->ID, 'wprss_activate_feed', TRUE );
478
+ $pause = get_post_meta( $post->ID, 'wprss_pause_feed', TRUE );
479
+ $update_interval = get_post_meta( $post->ID, 'wprss_update_interval', TRUE );
480
+
481
+ $age_limit = get_post_meta( $post->ID, 'wprss_age_limit', FALSE );
482
+ $age_unit = get_post_meta( $post->ID, 'wprss_age_unit', FALSE );
483
+
484
+ $age_limit = ( count( $age_limit ) === 0 )? wprss_get_general_setting( 'limit_feed_items_age' ) : $age_limit[0];
485
+ $age_unit = ( count( $age_unit ) === 0 )? wprss_get_general_setting( 'limit_feed_items_age_unit' ) : $age_unit[0];
486
+
487
+ // Set default strings for activate and pause times
488
+ $default_activate = 'immediately';
489
+ $default_pause = 'never';
490
+
491
+ // Prepare the states
492
+ $states = array(
493
+ 'active' => __( 'Active', WPRSS_TEXT_DOMAIN ),
494
+ 'paused' => __( 'Paused', WPRSS_TEXT_DOMAIN ),
495
+ );
496
+
497
+ // Prepare the schedules
498
+ $default_interval = __( 'Default', WPRSS_TEXT_DOMAIN );
499
+ $wprss_schedules = apply_filters( 'wprss_schedules', wprss_get_schedules() );
500
+ $default_interval_key = wprss_get_default_feed_source_update_interval();
501
+ $schedules = array_merge(
502
+ array(
503
+ $default_interval_key => array(
504
+ 'display' => $default_interval,
505
+ 'interval' => $default_interval,
506
+ ),
507
+ ),
508
+ $wprss_schedules
509
+ );
510
+
511
+ // Inline help
512
+ $help = WPRSS_Help::get_instance();
513
+ $help_options = array('tooltip_handle_class_extra' => $help->get_options('tooltip_handle_class_extra') . ' ' . $help->get_options('tooltip_handle_class') . '-side');
514
+
515
+ ?>
516
+
517
+ <div class="wprss-meta-side-setting">
518
+ <label for="wprss_state">Feed state:</label>
519
+ <select id="wprss_state" name="wprss_state">
520
+ <?php foreach( $states as $value => $label ) : ?>
521
+ <option value="<?php echo $value; ?>" <?php selected( $state, $value ) ?> ><?php echo $label; ?></option>
522
+ <?php endforeach; ?>
523
+ </select>
524
+ <?php echo $help->tooltip( 'field_wprss_state', null, $help_options ) ?>
525
+ </div>
526
+
527
+ <div class="wprss-meta-side-setting">
528
+ <p>
529
+ <label for="">Activate feed: </label>
530
+ <strong id="wprss-activate-feed-viewer"><?php echo ( ( $activate !== '' )? $activate : $default_activate ); ?></strong>
531
+ <a href="#">Edit</a>
532
+ <?php echo $help->tooltip( 'field_wprss_activate_feed', null, $help_options ) ?>
533
+ </p>
534
+ <div class="wprss-meta-slider" data-collapse-viewer="wprss-activate-feed-viewer" data-default-value="<?php echo $default_activate; ?>">
535
+ <input id="wprss_activate_feed" class="wprss-datetimepicker-from-today" name="wprss_activate_feed" value="<?php echo $activate; ?>" />
536
+ <span class="description">
537
+ Current UTC time is:<br/><code><?php echo date( 'd/m/Y H:i:s', current_time('timestamp',1) ); ?></code>
538
+ </span>
539
+ </div>
540
+ </div>
541
+
542
+ <div class="wprss-meta-side-setting">
543
+ <p>
544
+ <label for="">Pause feed: </label>
545
+ <strong id="wprss-pause-feed-viewer"><?php echo ( ( $pause !== '' )? $pause : $default_pause ); ?></strong>
546
+ <a href="#">Edit</a>
547
+ <?php echo $help->tooltip( 'field_wprss_pause_feed', null, $help_options ) ?>
548
+ </p>
549
+ <div class="wprss-meta-slider" data-collapse-viewer="wprss-pause-feed-viewer" data-default-value="<?php echo $default_pause; ?>">
550
+ <input id="wprss_pause_feed" class="wprss-datetimepicker-from-today" name="wprss_pause_feed" value="<?php echo $pause; ?>" />
551
+ <span class="description">
552
+ Current UTC time is:<br/><code><?php echo date( 'd/m/Y H:i:s', current_time('timestamp',1) ); ?></code>
553
+ </span>
554
+ </div>
555
+ </div>
556
+
557
+
558
+ <div class="wprss-meta-side-setting">
559
+ <p>
560
+ <label for="">Update interval: </label>
561
+ <strong id="wprss-feed-update-interval-viewer">
562
+ <?php
563
+ if ( $update_interval === '' || $update_interval === wprss_get_default_feed_source_update_interval() ) {
564
+ echo $default_interval;
565
+ }
566
+ else {
567
+ echo wprss_interval( $schedules[$update_interval]['interval'] );
568
+ }
569
+ ?>
570
+ </strong>
571
+ <a href="#">Edit</a>
572
+ <?php echo $help->tooltip( 'field_wprss_update_interval', null, $help_options ) ?>
573
+ </p>
574
+ <div class="wprss-meta-slider" data-collapse-viewer="wprss-feed-update-interval-viewer" data-default-value="<?php echo $default_interval; ?>">
575
+ <select id="feed-update-interval" name="wprss_update_interval">
576
+ <?php foreach ( $schedules as $value => $schedule ) : ?>
577
+ <?php $text = ( $value === wprss_get_default_feed_source_update_interval() )? $default_interval : wprss_interval( $schedule['interval'] ); ?>
578
+ <option value="<?php echo $value; ?>" <?php selected( $update_interval, $value ); ?> ><?php echo $text; ?></option>
579
+ <?php endforeach; ?>
580
+ </select>
581
+ </div>
582
+ </div>
583
+
584
+
585
+ <div class="wprss-meta-side-setting">
586
+ <p>
587
+ <label id="wprss-age-limit-feed-label" for="" data-when-empty="Delete old feed items:">Delete feed items older than: </label>
588
+ <strong id="wprss-age-limit-feed-viewer"><?php echo $age_limit . ' ' . $age_unit; ?></strong>
589
+ <a href="#">Edit</a>
590
+ <?php echo $help->tooltip( 'field_wprss_age_limit', null, $help_options ) ?>
591
+ </p>
592
+ <div class="wprss-meta-slider" data-collapse-viewer="wprss-age-limit-feed-viewer" data-label="#wprss-age-limit-feed-label" data-default-value="" data-empty-controller="#limit-feed-items-age" data-hybrid="#limit-feed-items-age, #limit-feed-items-age-unit">
593
+ <input id="limit-feed-items-age" name="wprss_age_limit" type="number" min="0" class="wprss-number-roller" placeholder="No limit" value="<?php echo $age_limit; ?>" />
594
+
595
+ <select id="limit-feed-items-age-unit" name="wprss_age_unit">
596
+ <?php foreach ( wprss_age_limit_units() as $unit ) : ?>
597
+ <option value="<?php echo $unit; ?>" <?php selected( $age_unit, $unit ); ?> ><?php echo $unit; ?></option>
598
+ <?php endforeach; ?>
599
+ </select>
600
+ </div>
601
+ </div>
602
+
603
+
604
+ <?php
605
+ }
606
+
607
+
608
+
609
+ /**
610
+ * Generate Help meta box
611
+ *
612
+ * @since 2.0
613
+ *
614
+ */
615
+ function wprss_help_meta_box_callback() {
616
+ echo '<p><a href="http://www.wprssaggregator.com/documentation/">View the documentation</p>';
617
+ echo '<p><strong>';
618
+ _e( 'Need help?', WPRSS_TEXT_DOMAIN );
619
+ echo '</strong> <a target="_blank" href="http://wordpress.org/support/plugin/wp-rss-aggregator">';
620
+ _e( 'Check out the support forum', WPRSS_TEXT_DOMAIN );
621
+ echo '</a></p>';
622
+ echo '</strong> <a target="_blank" href="http://www.wprssaggregator.com/feature-requests/">';
623
+ _e( 'Suggest a new feature', WPRSS_TEXT_DOMAIN );
624
+ echo '</a></p>';
625
+ }
626
+
627
+ /**
628
+ * Generate Like this plugin meta box
629
+ *
630
+ * @since 2.0
631
+ *
632
+ */
633
+ function wprss_like_meta_box_callback() { ?>
634
+
635
+ <ul>
636
+ <li><a href="http://wordpress.org/extend/plugins/wp-rss-aggregator/"><?php _e( 'Give it a 5 star rating on WordPress.org', WPRSS_TEXT_DOMAIN ) ?></a></li>
637
+ <li class="donate_link"><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=X9GP6BL4BLXBJ"><?php _e( 'Donate a token of your appreciation', WPRSS_TEXT_DOMAIN ); ?></a></li>
638
+ </ul>
639
+ <?php
640
+ echo '<p><strong>';
641
+ _e( 'Check out the Premium Extensions:', WPRSS_TEXT_DOMAIN );
642
+ echo '</strong>'; ?>
643
+ <ul>
644
+ <li><a href="http://www.wprssaggregator.com/extension/feed-to-post/"><?php echo 'Feed to Post'; ?></a></li>
645
+ <li><a href="http://www.wprssaggregator.com/extension/excerpts-thumbnails/"><?php echo 'Excerpts & Thumbnails'; ?></a></li>
646
+ <li><a href="http://www.wprssaggregator.com/extension/categories/"><?php echo 'Categories'; ?></a></li>
647
+ <li><a href="http://www.wprssaggregator.com/extension/keyword-filtering/"><?php echo 'Keyword Filtering'; ?></a></li>
648
+ </ul>
649
+ </p>
650
+ <?php }
651
+
652
+
653
+ /**
654
+ * Generate Follow us plugin meta box
655
+ *
656
+ * @since 2.0
657
+ *
658
+ */
659
+ function wprss_follow_meta_box_callback() {
660
+ ?>
661
+ <ul>
662
+ <li class="twitter"><a href="http://twitter.com/wpmayor"><?php _e( 'Follow WP Mayor on Twitter.', WPRSS_TEXT_DOMAIN ) ?></a></li>
663
+ <li class="facebook"><a href="https://www.facebook.com/wpmayor"><?php _e( 'Like WP Mayor on Facebook.', WPRSS_TEXT_DOMAIN ) ?></a></li>
664
+ </ul>
665
+ <?php }
666
+
667
+
668
+ add_action( 'add_meta_boxes', 'wprss_remove_meta_boxes', 100 );
669
+ /**
670
+ * Remove unneeded meta boxes from add feed source screen
671
+ *
672
+ * @since 2.0
673
+ */
674
+ function wprss_remove_meta_boxes() {
675
+ if ( 'wprss_feed' !== get_current_screen()->id ) return;
676
+ // Remove meta boxes of other plugins that tend to appear on all posts
677
+ //remove_meta_box( 'wpseo_meta', 'wprss_feed' ,'normal' );
678
+ remove_meta_box( 'postpsp', 'wprss_feed' ,'normal' );
679
+ remove_meta_box( 'su_postmeta', 'wprss_feed' ,'normal' );
680
+ remove_meta_box( 'woothemes-settings', 'wprss_feed' ,'normal' );
681
+ remove_meta_box( 'wpcf-post-relationship', 'wprss_feed' ,'normal' );
682
+ remove_meta_box( 'wpar_plugin_meta_box ', 'wprss_feed' ,'normal' );
683
+ remove_meta_box( 'sharing_meta', 'wprss_feed' ,'advanced' );
684
+ remove_meta_box( 'content-permissions-meta-box', 'wprss_feed' ,'advanced' );
685
+ remove_meta_box( 'theme-layouts-post-meta-box', 'wprss_feed' ,'side' );
686
+ remove_meta_box( 'post-stylesheets', 'wprss_feed' ,'side' );
687
+ remove_meta_box( 'hybrid-core-post-template', 'wprss_feed' ,'side' );
688
+ remove_meta_box( 'wpcf-marketing', 'wprss_feed' ,'side' );
689
+ remove_meta_box( 'trackbacksdiv22', 'wprss_feed' ,'advanced' );
690
+ remove_meta_box( 'aiosp', 'wprss_feed' ,'advanced' );
691
+ remove_action( 'post_submitbox_start', 'fpp_post_submitbox_start_action' );
692
+ }
includes/admin-options.php CHANGED
@@ -1,1133 +1,1156 @@
1
- <?php
2
- /**
3
- * Plugin settings related functions
4
- *
5
- * Note: Wording of options and settings is confusing, due to the plugin originally only having
6
- * an 'options' page to enter feed sources, and now needing two screens, one for feed sources and one for
7
- * general settings. Might implement something cleaner in the future.
8
- *
9
- * @package WP PRSS Aggregator
10
- */
11
-
12
-
13
-
14
- /**
15
- * Returns the given general setting option value form the database, or the default value if it is not found.
16
- *
17
- * @param option_name The name of the option to get
18
- * @return mixed
19
- * @since 3.7.1
20
- */
21
- function wprss_get_general_setting( $option_name ) {
22
- $options = get_option( 'wprss_settings_general', array() );
23
- $defaults = wprss_get_default_settings_general();
24
- return ( ( isset( $options[ $option_name ] ) )? $options[$option_name] : $defaults[$option_name] );
25
- }
26
-
27
-
28
- add_action( 'admin_init', 'wprss_admin_init' );
29
- /**
30
- * Register and define options and settings
31
- * @since 2.0
32
- * @todo add option for cron frequency
33
- *
34
- * Note: In the future might change to
35
- * the way EDD builds the settings pages, cleaner method.
36
- */
37
- function wprss_admin_init() {
38
-
39
- register_setting(
40
- 'wprss_settings_general', // A settings group name.
41
- 'wprss_settings_general', // The name of an option to sanitize and save.
42
- 'wprss_settings_general_validate' // A callback function that sanitizes the option's value.
43
- );
44
-
45
- // Licensing of add-ons
46
- register_setting(
47
- 'wprss_settings_license_keys',
48
- 'wprss_settings_license_keys',
49
- 'wprss_settings_license_keys_validate'
50
- );
51
-
52
-
53
- $sections = apply_filters(
54
- 'wprss_settings_sections_array',
55
- array(
56
- 'general' => __( 'General plugin settings', WPRSS_TEXT_DOMAIN ),
57
- 'display' => __( 'General display settings', WPRSS_TEXT_DOMAIN ),
58
- 'source' => __( 'Source display settings', WPRSS_TEXT_DOMAIN ),
59
- 'date' => __( 'Date display settings', WPRSS_TEXT_DOMAIN ),
60
- 'styles' => __( 'Styles', WPRSS_TEXT_DOMAIN ),
61
- )
62
- );
63
-
64
- // Define the settings per section
65
- $settings = apply_filters(
66
- 'wprss_settings_array',
67
- array(
68
- 'general' => array(
69
- 'limit-feed-items-by-age' => array(
70
- 'label' => __( 'Limit feed items by age', WPRSS_TEXT_DOMAIN ),
71
- 'callback' => 'wprss_setting_limit_feed_items_age_callback'
72
- ),
73
- 'limit-feed-items-db' => array(
74
- 'label' => __( 'Limit feed items stored', WPRSS_TEXT_DOMAIN ),
75
- 'callback' => 'wprss_setting_limit_feed_items_callback'
76
- ),
77
- 'limit-feed-items-imported' => array(
78
- 'label' => __( 'Limit feed items per feed', WPRSS_TEXT_DOMAIN ),
79
- 'callback' => 'wprss_setting_limit_feed_items_imported_callback'
80
- ),
81
- 'cron-interval' => array(
82
- 'label' => __( 'Feed processing interval', WPRSS_TEXT_DOMAIN ),
83
- 'callback' => 'wprss_setting_cron_interval_callback'
84
- ),
85
- 'custom-feed-url' => array(
86
- 'label' => __( 'Custom feed URL', WPRSS_TEXT_DOMAIN ),
87
- 'callback' => 'wprss_settings_custom_feed_url_callback'
88
- ),
89
- 'custom-feed-title' => array(
90
- 'label' => __( 'Custom feed Title', WPRSS_TEXT_DOMAIN ),
91
- 'callback' => 'wprss_settings_custom_feed_title_callback'
92
- ),
93
- 'custom-feed-limit' => array(
94
- 'label' => __( 'Custom feed limit', WPRSS_TEXT_DOMAIN ),
95
- 'callback' => 'wprss_setings_custom_feed_limit_callback'
96
- ),
97
- 'tracking' => array(
98
- 'label' => __( 'Anonymous tracking', WPRSS_TEXT_DOMAIN ),
99
- 'callback' => 'wprss_tracking_callback',
100
- )
101
- ),
102
-
103
- 'display' => array(
104
- // Title options
105
- 'link-enable' => array(
106
- 'label' => __( 'Link title', WPRSS_TEXT_DOMAIN ),
107
- 'callback' => 'wprss_setting_title_link_callback'
108
- ),
109
- 'title-limit' => array(
110
- 'label' => __( 'Title maximum length', WPRSS_TEXT_DOMAIN ),
111
- 'callback' => 'wprss_setting_title_length_callback'
112
- ),
113
-
114
-
115
-
116
- // Misc Options
117
- 'authors-enable' => array(
118
- 'label' => __( 'Show authors', WPRSS_TEXT_DOMAIN ),
119
- 'callback' => 'wprss_setting_authors_enable_callback',
120
- ),
121
- 'video-links' => array(
122
- 'label' => __( 'For video feed items use', WPRSS_TEXT_DOMAIN ),
123
- 'callback' => 'wprss_setting_video_links_callback'
124
- ),
125
- 'pagination' => array(
126
- 'label' => __( 'Pagination type', WPRSS_TEXT_DOMAIN ),
127
- 'callback' => 'wprss_setting_pagination_type_callback',
128
- ),
129
- 'feed-limit' => array(
130
- 'label' => __( 'Feed display limit', WPRSS_TEXT_DOMAIN ),
131
- 'callback' => 'wprss_setting_feed_limit_callback'
132
- ),
133
- 'open-dd' => array(
134
- 'label' => __( 'Open links behaviour', WPRSS_TEXT_DOMAIN ),
135
- 'callback' => 'wprss_setting_open_dd_callback'
136
- ),
137
- 'follow-dd' => array(
138
- 'label' => __( 'Set links as nofollow', WPRSS_TEXT_DOMAIN ),
139
- 'callback' => 'wprss_setting_follow_dd_callback'
140
- ),
141
- ),
142
-
143
- // Source Options
144
- 'source' => array(
145
- 'source-enable' => array(
146
- 'label' => __( 'Show source', WPRSS_TEXT_DOMAIN ),
147
- 'callback' => 'wprss_setting_source_enable_callback'
148
- ),
149
- 'text-preceding-source' => array(
150
- 'label' => __( 'Text preceding source', WPRSS_TEXT_DOMAIN ),
151
- 'callback' => 'wprss_setting_text_preceding_source_callback'
152
- ),
153
- 'source-link' => array(
154
- 'label' => __( 'Link source', WPRSS_TEXT_DOMAIN ),
155
- 'callback' => 'wprss_setting_source_link_callback'
156
- ),
157
- ),
158
-
159
- // Date options
160
- 'date' => array(
161
- 'date-enable' => array(
162
- 'label' => __( 'Show date', WPRSS_TEXT_DOMAIN ),
163
- 'callback' => 'wprss_setting_date_enable_callback'
164
- ),
165
- 'text-preceding-date' => array(
166
- 'label' => __( 'Text preceding date', WPRSS_TEXT_DOMAIN ),
167
- 'callback' => 'wprss_setting_text_preceding_date_callback'
168
- ),
169
- 'date-format' => array(
170
- 'label' => __( 'Date format', WPRSS_TEXT_DOMAIN ),
171
- 'callback' => 'wprss_setting_date_format_callback'
172
- ),
173
- 'time-ago-format-enable' => array(
174
- 'label' => __( 'Time ago format', WPRSS_TEXT_DOMAIN ),
175
- 'callback' => 'wprss_setting_time_ago_format_enable_callback'
176
- ),
177
- ),
178
-
179
- 'styles' => array(
180
- 'styles-disable' => array(
181
- 'label' => __( 'Disable Styles', WPRSS_TEXT_DOMAIN ),
182
- 'callback' => 'wprss_setting_styles_disable_callback'
183
- )
184
- )
185
- )
186
- );
187
-
188
- if ( apply_filters( 'wprss_use_fixed_feed_limit', FALSE ) === FALSE ) {
189
- unset( $settings['general']['limit-feed-items-db'] );
190
- }
191
-
192
- $setting_field_id_prefix = 'wprss-settings-';
193
-
194
-
195
- // Loop through each setting field and register it
196
- foreach( $settings as $section => $fields ) {
197
- if ( count( $fields ) > 0 ) {
198
- $section_desc = $sections[ $section ];
199
- add_settings_section(
200
- "wprss_settings_${section}_section",
201
- $section_desc,
202
- "wprss_settings_${section}_callback",
203
- 'wprss_settings_general'
204
- );
205
-
206
- foreach ( $fields as $id => $data ) {
207
-
208
- /**
209
- * @var This will be passed to the field callback as the only argument
210
- * @see http://codex.wordpress.org/Function_Reference/add_settings_field#Parameters
211
- */
212
- $callback_args = array(
213
- 'field_id' => $id,
214
- 'field_id_prefix' => $setting_field_id_prefix,
215
- 'section_id' => $section,
216
- 'field_label' => isset( $data['label'] ) ? $data['label'] : null,
217
- 'tooltip' => isset( $data['tooltip'] ) ? $data['tooltip'] : null
218
- );
219
-
220
-
221
- add_settings_field(
222
- $setting_field_id_prefix . $id,
223
- $data['label'],
224
- $data['callback'],
225
- 'wprss_settings_general',
226
- "wprss_settings_${section}_section",
227
- $callback_args
228
- );
229
-
230
- }
231
- }
232
- }
233
-
234
-
235
- /*
236
- // SECURE RESET OPTION
237
- register_setting(
238
- 'wprss_secure_reset', // A settings group name.
239
- 'wprss_secure_reset_code', // The name of an option to sanitize and save.
240
- '' // A callback function that sanitizes the option's value.
241
- );
242
- add_settings_section(
243
- 'wprss_secure_reset_section', // ID of section
244
- __( 'Secure Reset', WPRSS_TEXT_DOMAIN ), // Title of section
245
- 'wprss_secure_reset_section_callback', // Callback that renders the section header
246
- 'wprss_settings_general' // The page on which to display the section
247
- );
248
- add_settings_field(
249
- 'wprss-settings-secure-reset', // ID of setting
250
- __( 'Secure Reset', WPRSS_TEXT_DOMAIN ), // The title of the setting
251
- 'wprss_settings_secure_reset_code_callback', // The callback that renders the setting
252
- 'wprss_settings_general', // The page on which to display the setting
253
- "wprss_secure_reset_section" // The section in which to display the setting
254
- );
255
- */
256
-
257
-
258
-
259
- do_action( 'wprss_admin_init' );
260
- }
261
-
262
-
263
- /**
264
- * Returns the HTML of a tooltip handle.
265
- *
266
- * Filters used:
267
- * - `wprss_settings_inline_help_default_options` - The default options for "Settings" page's tooltips
268
- * - `wprss_settings_inline_help_id_prefix` - The prefix for all tooltip IDs for the "Settings" page.
269
- *
270
- * @param string $id The ID of the tooltip
271
- * @param string|null $text Text for this tooltip, if any.
272
- * @param array $options Any options for this setting.
273
- * @return string Tooltip handle HTML. See {@link WPRSS_Help::tooltip()}.
274
- */
275
- function wprss_settings_inline_help( $id, $text = null, $options = array() ) {
276
- $help = WPRSS_Help::get_instance();
277
-
278
- // Default options, entry point
279
- $defaults = apply_filters( 'wprss_settings_inline_help_default_options', array(
280
- 'tooltip_handle_class_extra' => $help->get_options('tooltip_handle_class_extra') . ' ' . $help->get_options('tooltip_handle_class') . '-setting'
281
- ));
282
-
283
- $options = $help->array_merge_recursive_distinct( $defaults, $options );
284
-
285
- // ID Prefix
286
- $id = apply_filters( 'wprss_settings_inline_help_id_prefix', 'setting-' ) . $id;
287
-
288
- return $help->tooltip( $id, $text, $options );
289
- }
290
-
291
-
292
- /**
293
- *
294
- * @param type $string
295
- * @return type
296
- */
297
- function wprss_settings_field_name_prefix( $string = '' ) {
298
- $string = (string) $string;
299
- $prefix = apply_filters( 'wprss_settings_field_name_prefix', 'wprss_settings_', $string );
300
- return $prefix . $string;
301
- }
302
-
303
-
304
- /**
305
- * Generates a uniform setting field name for use in HTML.
306
- * The parts used are the ID of the field, the section it is in, and an optional prefix.
307
- * All parts are optional, but, if they appear, they shall appear in this order: $prefix, $section, $id.
308
- *
309
- * If only the section is not specified, the $id will be simply prefixed by $prefix.
310
- * If either the $id or the $section are empty (but not both), $prefix will be stripped of known separators.
311
- * Empty parts will be excluded.
312
- *
313
- * @param string $id ID of the field.
314
- * @param string|null $section Name of the section, to which this field belongs.
315
- * @param string|null $prefix The string to prefix the name with; appears first. If boolean false, no prefix will be applied. Default: return value of {@link wprss_settings_field_name_prefix()}.
316
- * @return string Name of the settings field, namespaced and optionally prefixed.
317
- */
318
- function wprss_settings_field_name( $id = null, $section = null, $prefix = null ) {
319
- if( $prefix !== false ) $prefix = is_null( $prefix ) ? wprss_settings_field_name_prefix() : $prefix;
320
- else $prefix = '';
321
-
322
- $section = (string) $section;
323
-
324
- $format = '';
325
- if( !strlen( $section ) xor !strlen($id) ) $prefix = trim ( $prefix, "\t\n\r _-:" );
326
- if( strlen( $prefix ) ) $format .= '%3$s';
327
- if( strlen( $section ) ) $format .= '%2$s';
328
- if( strlen( $id ) ) $format .= ( !strlen( $section ) ? '%1$s' : '[%1$s]' );
329
-
330
- return apply_filters( 'wprss_settings_field_name', sprintf( $format, $id, $section, $prefix ), $id, $section, $prefix );
331
- }
332
-
333
-
334
- /**
335
- * Build the plugin settings page, used to save general settings like whether a link should be follow or no follow
336
- * @since 1.1
337
- */
338
- function wprss_settings_page_display() {
339
- ?>
340
- <div class="wrap">
341
- <?php screen_icon( 'wprss-aggregator' ); ?>
342
-
343
- <h2><?php _e( 'WP RSS Aggregator Settings', WPRSS_TEXT_DOMAIN ); ?></h2>
344
-
345
- <?php settings_errors(); ?>
346
-
347
- <?php $active_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : 'general_settings'; ?>
348
-
349
- <?php
350
-
351
- $default_tabs = array(
352
- 'general' => array(
353
- 'label' => __( 'General', WPRSS_TEXT_DOMAIN ),
354
- 'slug' => 'general_settings',
355
- ),
356
- 'licenses' => array(
357
- 'label' => __( 'Licenses', WPRSS_TEXT_DOMAIN ),
358
- 'slug' => 'licenses_settings'
359
- )
360
- );
361
-
362
- $addon_tabs = apply_filters( 'wprss_options_tabs', array() );
363
-
364
- $tabs = array_merge( array( $default_tabs['general'] ), $addon_tabs , array( $default_tabs['licenses'] ) );
365
-
366
- $show_tabs = ( count( $addon_tabs ) > 0 ) || apply_filters( 'wprss_show_settings_tabs_condition', FALSE );
367
-
368
- if ( $show_tabs ) { ?>
369
- <h2 class="nav-tab-wrapper">
370
- <?php
371
- foreach ( $tabs as $tab => $tab_property ) { ?>
372
- <a href="?post_type=wprss_feed&page=wprss-aggregator-settings&tab=<?php echo esc_attr( $tab_property['slug'] ); ?>"
373
- class="nav-tab <?php echo $active_tab == $tab_property['slug'] ? 'nav-tab-active' : ''; ?>"><?php echo esc_html( $tab_property['label'] ); ?></a>
374
- <?php } ?>
375
- <?php } ?>
376
- </h2>
377
-
378
- <form action="options.php" method="post">
379
-
380
- <?php
381
-
382
- if ( $active_tab === 'general_settings' ) {
383
- settings_fields( 'wprss_settings_general' );
384
- //settings_fields( 'wprss_secure_reset' );
385
- do_settings_sections( 'wprss_settings_general' );
386
- }
387
- elseif ( $show_tabs ) {
388
-
389
- if ( $active_tab === 'licenses_settings' ) {
390
- settings_fields( 'wprss_settings_license_keys' );
391
- do_settings_sections( 'wprss_settings_license_keys' );
392
- }
393
-
394
- do_action( 'wprss_add_settings_fields_sections', $active_tab );
395
- }
396
-
397
- submit_button( __( 'Save Settings', WPRSS_TEXT_DOMAIN ) );
398
-
399
- ?>
400
- </form>
401
- </div>
402
- <?php
403
- }
404
-
405
-
406
- /**
407
- * General settings section header
408
- * @since 3.0
409
- */
410
- function wprss_settings_general_callback() {
411
- echo wpautop( __( 'These are the general settings for WP RSS Aggregator.', WPRSS_TEXT_DOMAIN ) );
412
- }
413
-
414
-
415
- /**
416
- * General settings display section header
417
- * @since 3.5
418
- */
419
- function wprss_settings_display_callback() {
420
- echo wpautop( __( 'In this section you can find some general options that control how the feed items are displayed.', WPRSS_TEXT_DOMAIN ) );
421
- }
422
-
423
-
424
- /**
425
- * Display settings for source section header
426
- *
427
- * @since 4.2.4
428
- */
429
- function wprss_settings_source_callback() {
430
- echo wpautop( __( "Options that control how the feed item's source is displayed.", WPRSS_TEXT_DOMAIN ) );
431
- }
432
-
433
- /**
434
- * Display settings for date section header
435
- *
436
- * @since 4.2.4
437
- */
438
- function wprss_settings_date_callback() {
439
- echo wpautop( __( "Options that control how the feed item's date is displayed.", WPRSS_TEXT_DOMAIN ) );
440
- }
441
-
442
-
443
- /**
444
- * General settings styles section header
445
- * @since 3.0
446
- */
447
- function wprss_settings_styles_callback() {
448
- echo wpautop( __( 'If you would like to disable all styles used in this plugin, tick the checkbox.', WPRSS_TEXT_DOMAIN ) );
449
- }
450
-
451
-
452
- /**
453
- * General settings scure reset section header
454
- * @since 3.0
455
- */
456
- function wprss_secure_reset_section_callback() {
457
- echo wpautop( __( 'Set your security reset code, in case of any errors.', WPRSS_TEXT_DOMAIN ) );
458
- }
459
-
460
-
461
- /**
462
- * Tracking settings section header
463
- * @since 3.0
464
- */
465
- function wprss_tracking_section_callback() {
466
- echo wpautop( __( 'Participate in helping us make the plugin better.', WPRSS_TEXT_DOMAIN ) );
467
- }
468
-
469
-
470
- /**
471
- * Follow or No Follow dropdown
472
- * @since 1.1
473
- */
474
- function wprss_setting_follow_dd_callback( $field ) {
475
- $follow_dd = wprss_get_general_setting( 'follow_dd' );
476
-
477
- $checked = ( $follow_dd === 'no_follow' );
478
- $checked_attr = ( $checked )? 'checked="checked"' : '';
479
- ?>
480
- <input type="hidden" name="wprss_settings_general[follow_dd]" value="follow" />
481
- <input type="checkbox" id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[follow_dd]" value="no_follow" <?php echo $checked_attr ?> />
482
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
483
- }
484
-
485
-
486
- /**
487
- * Use original video link, or embedded video links dropwdown
488
- * @since 3.4
489
- */
490
- function wprss_setting_video_links_callback( $field ) {
491
- $video_link = wprss_get_general_setting('video_link');
492
- $items = array(
493
- 'false' => __( 'Original page link', WPRSS_TEXT_DOMAIN ),
494
- 'true' => __( 'Embedded video player link', WPRSS_TEXT_DOMAIN )
495
- );
496
- ?>
497
- <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[video_link]">
498
- <?php
499
- foreach ( $items as $boolean => $text ) {
500
- $selected = ( $video_link === $boolean )? 'selected="selected"' : '';
501
- ?><option value="<?php echo $boolean ?>" <?php echo $selected ?>><?php echo $text ?></option><?php
502
- }
503
- ?>
504
- </select>
505
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?>
506
- <p>
507
- <span class="description">
508
- <?php _e( 'This will not affect already imported feed items.', WPRSS_TEXT_DOMAIN ) ?>
509
- </span>
510
- </p>
511
- <?php
512
- }
513
-
514
-
515
- /**
516
- * Link open setting dropdown
517
- * @since 1.1
518
- */
519
- function wprss_setting_open_dd_callback( $field ) {
520
- $open_dd = wprss_get_general_setting('open_dd');
521
-
522
- $items = array(
523
- __( 'Lightbox', WPRSS_TEXT_DOMAIN ),
524
- __( 'New window', WPRSS_TEXT_DOMAIN ),
525
- __( 'Self', WPRSS_TEXT_DOMAIN )
526
- );
527
- ?>
528
- <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[open_dd]">
529
- <?php
530
- foreach( $items as $item ) {
531
- $selected = ( $open_dd == $item ) ? 'selected="selected"' : '';
532
- ?><option value="<?php echo $item ?>" <?php echo $selected ?>><?php echo $item ?></option><?php
533
- }
534
- ?>
535
- </select>
536
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
537
- }
538
-
539
-
540
- /**
541
- * Set limit for feeds on frontend
542
- * @since 2.0
543
- */
544
- function wprss_setting_feed_limit_callback( $field ) {
545
- $feed_limit = wprss_get_general_setting( 'feed_limit' );
546
- ?>
547
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[feed_limit]" type="text" value="<?php echo $feed_limit ?>" />
548
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
549
- }
550
-
551
-
552
- /**
553
- * Set date format
554
- * @since 3.0
555
- */
556
- function wprss_setting_date_format_callback( $field ) {
557
- $date_format = wprss_get_general_setting( 'date_format' );
558
- ?>
559
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[date_format]" type="text" value="<?php echo $date_format ?>" />
560
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?>
561
- <p>
562
- <a href="http://codex.wordpress.org/Formatting_Date_and_Time">
563
- <?php _e( 'PHP Date Format Reference', WPRSS_TEXT_DOMAIN ); ?>
564
- </a>
565
- </p>
566
- <?php
567
- }
568
-
569
-
570
-
571
- /**
572
- * Enable linked title
573
- * @since 3.0
574
- */
575
- function wprss_setting_title_link_callback( $field ) {
576
- $title_link = wprss_get_general_setting( 'title_link' );
577
- ?>
578
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[title_link]" type="checkbox" value="1" <?php echo checked( 1, $title_link, false ) ?> />
579
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
580
- }
581
-
582
-
583
-
584
- /**
585
- * Set the title length limit
586
- * @since 3.0
587
- */
588
- function wprss_setting_title_length_callback( $field ) {
589
- $title_limit = wprss_get_general_setting( 'title_limit' );
590
- ?>
591
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[title_limit]" type="number" class="wprss-number-roller" min="0" value="<?php echo $title_limit ?>" placeholder="<?php _e( 'No limit', WPRSS_TEXT_DOMAIN ) ?>" />
592
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
593
- }
594
-
595
-
596
- /**
597
- * Enable source
598
- * @since 3.0
599
- */
600
- function wprss_setting_source_enable_callback( $field ) {
601
- $source_enable = wprss_get_general_setting( 'source_enable' );
602
- ?>
603
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[source_enable]" type="checkbox" value="1" <?php echo checked( 1, $source_enable, false ) ?> />
604
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
605
- }
606
-
607
- /**
608
- * Enable linked title
609
- * @since 3.0
610
- */
611
- function wprss_setting_source_link_callback( $field ) {
612
- $source_link = wprss_get_general_setting( 'source_link' );
613
- ?>
614
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[source_link]" type="checkbox" value="1" <?php echo checked( 1, $source_link, false ) ?> />
615
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
616
- }
617
-
618
-
619
- /**
620
- * Set text preceding source
621
- * @since 3.0
622
- */
623
- function wprss_setting_text_preceding_source_callback( $field ) {
624
- $text_preceding_source = wprss_get_general_setting( 'text_preceding_source' );
625
- ?>
626
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[text_preceding_source]" type="text" value="<?php echo $text_preceding_source ?>" />
627
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
628
- }
629
- /**
630
- * Enable date
631
- * @since 3.0
632
- */
633
- function wprss_setting_date_enable_callback( $field ) {
634
- $date_enable = wprss_get_general_setting( 'date_enable' );
635
- ?>
636
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[date_enable]" type="checkbox" value="1" <?php echo checked( 1, $date_enable, false ) ?> />
637
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
638
- }
639
-
640
- /**
641
- * Set text preceding date
642
- * @since 3.0
643
- */
644
- function wprss_setting_text_preceding_date_callback( $field ) {
645
- $text_preceding_date = wprss_get_general_setting( 'text_preceding_date' );
646
- ?>
647
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[text_preceding_date]" type="text" value="<?php echo $text_preceding_date ?>" />
648
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
649
- }
650
-
651
-
652
- /**
653
- * Shows the feed item authors option
654
- *
655
- * @since 4.2.4
656
- */
657
- function wprss_setting_authors_enable_callback( $field ) {
658
- $authors_enable = wprss_get_general_setting( 'authors_enable' );
659
- ?>
660
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[authors_enable]" type="checkbox" value="1" <?php echo checked( 1, $authors_enable, false ) ?> />
661
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
662
- }
663
-
664
-
665
- /**
666
- * Pagination Type
667
- *
668
- * @since 4.2.3
669
- */
670
- function wprss_setting_pagination_type_callback( $field ) {
671
- $pagination = wprss_get_general_setting( 'pagination' );
672
- $options = array(
673
- 'default' => __( '"Older posts" and "Newer posts" links', WPRSS_TEXT_DOMAIN ),
674
- 'numbered' => __( 'Page numbers with "Next" and "Previous" page links', WPRSS_TEXT_DOMAIN ),
675
- );
676
- ?>
677
- <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[pagination]">
678
- <?php
679
- foreach( $options as $value => $text ) {
680
- $selected = ( $value === $pagination )? 'selected="selected"' : '';
681
- ?><option value="<?php echo $value ?>" <?php echo $selected ?>><?php echo $text ?></option><?php
682
- }
683
- ?>
684
- </select>
685
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
686
- }
687
-
688
-
689
-
690
- /**
691
- * Limit number of feed items stored by their age
692
- * @since 3.0
693
- */
694
- function wprss_setting_limit_feed_items_age_callback( $field ) {
695
- $limit_feed_items_age = wprss_get_general_setting( 'limit_feed_items_age' );
696
- $limit_feed_items_age_unit = wprss_get_general_setting( 'limit_feed_items_age_unit' );
697
- $units = wprss_age_limit_units();
698
- // echo wprss_settings_field_name( $field_info['field_id'], $field_info['section_id'], $field_info['field_name_prefix'] )
699
- ?>
700
-
701
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[limit_feed_items_age]" type="number" min="0"
702
- class="wprss-number-roller" placeholder="<?php _e( 'No limit', WPRSS_TEXT_DOMAIN ) ?>" value="<?php echo $limit_feed_items_age; ?>" />
703
-
704
- <select id="limit-feed-items-age-unit" name="wprss_settings_general[limit_feed_items_age_unit]">
705
- <?php foreach ( $units as $unit ) : ?>
706
- <option value="<?php echo $unit ?>" <?php selected( $limit_feed_items_age_unit, $unit ) ?> ><?php _e( $unit, WPRSS_TEXT_DOMAIN ) ?></option>
707
- <?php endforeach ?>
708
- </select>
709
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?>
710
-
711
- <br/>
712
- <?php
713
- }
714
-
715
-
716
-
717
- /**
718
- * Limit number of feed items stored
719
- * @since 3.0
720
- */
721
- function wprss_setting_limit_feed_items_callback( $field ) {
722
- $limit_feed_items_db = wprss_get_general_setting( 'limit_feed_items_db' );
723
- ?>
724
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[limit_feed_items_db]" type="text" value="<?php echo $limit_feed_items_db ?>" />
725
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
726
- }
727
-
728
-
729
- /**
730
- * Limit number of feed items imported per feed
731
- * @since 3.1
732
- */
733
- function wprss_setting_limit_feed_items_imported_callback( $field ) {
734
- $limit_feed_items_imported = wprss_get_general_setting( 'limit_feed_items_imported' );
735
- ?>
736
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[limit_feed_items_imported]" type="text" value="<?php echo $limit_feed_items_imported ?>" placeholder="<?php _e( 'No Limit', WPRSS_TEXT_DOMAIN ) ?>" />
737
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
738
- }
739
-
740
-
741
- /**
742
- * Gets a sorted (according to interval) list of the cron schedules
743
- * @since 3.0
744
- */
745
- function wprss_get_schedules() {
746
- $schedules = wp_get_schedules();
747
- uasort( $schedules, create_function( '$a,$b', 'return $a["interval"]-$b["interval"];' ) );
748
- return $schedules;
749
- }
750
-
751
-
752
- /**
753
- * Cron interval dropdown callback
754
- * @since 3.0
755
- */
756
- function wprss_setting_cron_interval_callback( $field ) {
757
- $options = get_option( 'wprss_settings_general' );
758
- $current = $options['cron_interval'];
759
-
760
- $schedules = wprss_get_schedules();
761
- // Set the allowed Cron schedules, we don't want any intervals that can lead to issues with server load
762
- $wprss_schedules = apply_filters(
763
- 'wprss_schedules',
764
- array( 'fifteen_min', 'thirty_min', 'hourly', 'two_hours', 'twicedaily', 'daily' )
765
- );
766
- ?>
767
- <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[cron_interval]">
768
- <?php
769
- foreach( $schedules as $schedule_name => $schedule_data ):
770
- if ( in_array( $schedule_name, $wprss_schedules ) ): ?>
771
- <option value="<?php echo $schedule_name ?>" <?php selected( $current, $schedule_name ) ?> >
772
- <?php echo $schedule_data['display'] ?> (<?php echo wprss_interval( $schedule_data['interval'] ) ?>)
773
- </option>
774
- <?php endif ?>
775
- <?php endforeach ?>
776
- </select>
777
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?><?php
778
- }
779
-
780
- /**
781
- * Sets the custom feed URL
782
- * @since 3.3
783
- */
784
- function wprss_settings_custom_feed_url_callback( $field ) {
785
- $custom_feed_url = wprss_get_general_setting( 'custom_feed_url' );
786
- ?>
787
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[custom_feed_url]" type="text" value="<?php echo $custom_feed_url ?>" />
788
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
789
- }
790
-
791
- /**
792
- * Sets the custom feed title
793
- * @since 4.1.2
794
- */
795
- function wprss_settings_custom_feed_title_callback( $field ) {
796
- $custom_feed_title = wprss_get_general_setting( 'custom_feed_title' );
797
- ?>
798
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[custom_feed_title]" type="text" value="<?php echo $custom_feed_title ?>" />
799
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
800
- }
801
-
802
- /**
803
- * Sets the custom feed limit
804
- * @since 3.3
805
- */
806
- function wprss_setings_custom_feed_limit_callback( $field ) {
807
- $custom_feed_limit = wprss_get_general_setting( 'custom_feed_limit' );
808
- ?>
809
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[custom_feed_limit]" placeholder="<?php _e( 'Default', WPRSS_TEXT_DOMAIN ) ?>" min="0" class="wprss-number-roller" type="number" value="<?php echo $custom_feed_limit ?>" />
810
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
811
- }
812
-
813
- /**
814
- * Disable styles
815
- * @since 3.0
816
- */
817
- function wprss_setting_styles_disable_callback( $field ) {
818
- $styles_disable = wprss_get_general_setting( 'styles_disable' );
819
- ?>
820
- <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[styles_disable]" type="checkbox" value="1" <?php echo checked( 1, $styles_disable, false ) ?> />
821
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
822
- }
823
-
824
-
825
-
826
- /**
827
- * Secure Reset section
828
- *
829
- * @since 3.7.1
830
- */
831
- function wprss_settings_secure_reset_code_callback( $args ) {
832
- $reset_code = get_option( 'wprss_secure_reset_code', '' );
833
- ?>
834
- <input id="wprss-secure-reset-code" name="wprss_secure_reset_code" type="input" value="<?php echo $reset_code; ?>" />
835
- <button type="button" role="button" id="wprss-secure-reset-generate"><?php _e( 'Generate Random Code', WPRSS_TEXT_DOMAIN ) ?></button>
836
-
837
- <br/>
838
-
839
- <label class="description" for="wprss-secure-reset-code">
840
- <?php _e( 'Enter the code to use to securely reset the plugin and deactivate it. Be sure to save this code somewhere safe.', WPRSS_TEXT_DOMAIN ) ?><br/>
841
- </label>
842
-
843
- <br/>
844
-
845
- <p>
846
- <?php _e( 'Leave this empty to disable the secure reset function.<br/>
847
- You use this code by adding any of the following to any URL on your site.', WPRSS_TEXT_DOMAIN ) ?>
848
- <ol>
849
- <li>"?wprss_action=reset&wprss_security_code=&lt;your_code&gt;" - <b><?php _e( 'Resets your WP RSS Aggregator settings', WPRSS_TEXT_DOMAIN ) ?></b></li>
850
- <li>"?wprss_action=deactivate&wprss_security_code=&lt;your_code&gt;" - <b><?php _e( 'Deactivates WP RSS Aggregator', WPRSS_TEXT_DOMAIN ) ?></b></li>
851
- <li>"?wprss_action=reset_and_deactivate&wprss_security_code=&lt;your_code&gt;" - <b><?php _e( 'Does both of the above', WPRSS_TEXT_DOMAIN ) ?></b></li>
852
- </ol>
853
- </p>
854
- <p class="description">
855
- <?php _e( 'Use the above actions only when absolutely necessary, or when instructed to by support staff.', WPRSS_TEXT_DOMAIN ) ?>
856
- </p>
857
- <?php
858
- }
859
-
860
-
861
- /**
862
- * Tracking checkbox
863
- * @since 3.6
864
- */
865
- function wprss_tracking_callback( $field ) {
866
- $tracking = wprss_get_general_setting( 'tracking' );
867
- ?>
868
- <input type="checkbox" id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[tracking]" value="1" <?php echo checked( 1, $tracking, false ) ?> />
869
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?>
870
- <label class="description" for="<?php echo $field['field_id'] ?>">
871
- <?php _e( 'Please help us improve WP RSS Aggregator by allowing us to gather anonymous usage statistics. No sensitive data is collected.', WPRSS_TEXT_DOMAIN ) ?>
872
- </label>
873
- <?php
874
- }
875
-
876
- /**
877
- * Time ago format checkbox
878
- * @since 4.2
879
- */
880
- function wprss_setting_time_ago_format_enable_callback( $field ) {
881
- $time_ago_format = wprss_get_general_setting( 'time_ago_format_enable' );
882
- ?>
883
- <input type="checkbox" id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[time_ago_format_enable]" value="1" <?php echo checked( 1, $time_ago_format, false ) ?> />
884
- <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
885
- }
886
-
887
-
888
- /**
889
- * Pretty-prints the difference in two times.
890
- *
891
- * @since 3.0
892
- * @param time $older_date
893
- * @param time $newer_date
894
- * @return string The pretty time_since value
895
- * @link http://wordpress.org/extend/plugins/wp-crontrol/
896
- */
897
- function wprss_time_since( $older_date, $newer_date ) {
898
- return wprss_interval( $newer_date - $older_date );
899
- }
900
-
901
- /**
902
- * Calculates difference between times
903
- *
904
- * Taken from the WP-Crontrol plugin
905
- * @link http://wordpress.org/extend/plugins/wp-crontrol/
906
- * @since 3.0
907
- *
908
- */
909
- function wprss_interval( $since ) {
910
- if ( $since === wprss_get_default_feed_source_update_interval() ) {
911
- return __( 'Default', WPRSS_TEXT_DOMAIN );
912
- }
913
- // array of time period chunks
914
- $chunks = array(
915
- array(60 * 60 * 24 * 365 , _n_noop('%s year', '%s years', 'crontrol')),
916
- array(60 * 60 * 24 * 30 , _n_noop('%s month', '%s months', 'crontrol')),
917
- array(60 * 60 * 24 * 7, _n_noop('%s week', '%s weeks', 'crontrol')),
918
- array(60 * 60 * 24 , _n_noop('%s day', '%s days', 'crontrol')),
919
- array(60 * 60 , _n_noop('%s hour', '%s hours', 'crontrol')),
920
- array(60 , _n_noop('%s minute', '%s minutes', 'crontrol')),
921
- array( 1 , _n_noop('%s second', '%s seconds', 'crontrol')),
922
- );
923
-
924
-
925
- if( $since <= 0 ) {
926
- return __( 'now', WPRSS_TEXT_DOMAIN );
927
- }
928
-
929
- // we only want to output two chunks of time here, eg:
930
- // x years, xx months
931
- // x days, xx hours
932
- // so there's only two bits of calculation below:
933
-
934
- // step one: the first chunk
935
- for ($i = 0, $j = count($chunks); $i < $j; $i++)
936
- {
937
- $seconds = $chunks[$i][0];
938
- $name = $chunks[$i][1];
939
-
940
- // finding the biggest chunk (if the chunk fits, break)
941
- if (($count = floor($since / $seconds)) != 0)
942
- {
943
- break;
944
- }
945
- }
946
-
947
- // set output var
948
- $output = sprintf(_n($name[0], $name[1], $count, WPRSS_TEXT_DOMAIN), $count);
949
-
950
- // step two: the second chunk
951
- if ($i + 1 < $j)
952
- {
953
- $seconds2 = $chunks[$i + 1][0];
954
- $name2 = $chunks[$i + 1][1];
955
-
956
- if (($count2 = floor(($since - ($seconds * $count)) / $seconds2)) != 0)
957
- {
958
- // add to output var
959
- $output .= ' '.sprintf(_n($name2[0], $name2[1], $count2, WPRSS_TEXT_DOMAIN), $count2);
960
- }
961
- }
962
-
963
- return $output;
964
- }
965
-
966
-
967
- /**
968
- * Validate inputs from the general settings page
969
- * @since 3.0
970
- */
971
- function wprss_settings_general_validate( $input ) {
972
- $options = get_option( 'wprss_settings_general' );
973
- $current_cron_interval = $options['cron_interval'];
974
-
975
- // Create our array for storing the validated options
976
- $output = array();
977
-
978
- // Loop through each of the incoming options
979
- foreach( $input as $key => $value ) {
980
-
981
- // Check to see if the current option has a value. If so, process it.
982
- if( isset( $input[ $key ] ) ) {
983
-
984
- // Strip all HTML and PHP tags and properly handle quoted strings
985
- $output[ $key ] = strip_tags( stripslashes( $input[ $key ] ) );
986
-
987
- } // end if
988
-
989
- } // end foreach
990
-
991
- // If limit_feed_items_age_unit is not set or it set to zero, set it to empty
992
- if ( ! isset( $input['limit_feed_items_age'] ) || strval( $input['limit_feed_items_age'] ) == '0' ) {
993
- $output['limit_feed_items_age'] = '';
994
- }
995
-
996
- if ( ! isset( $input['title_link'] ) || $input['title_link'] != '1' )
997
- $output['title_link'] = 0;
998
- else
999
- $output['title_link'] = 1;
1000
-
1001
- if ( ! isset( $input['source_enable'] ) || $input['source_enable'] != '1' )
1002
- $output['source_enable'] = 0;
1003
- else
1004
- $output['source_enable'] = 1;
1005
-
1006
- if ( ! isset( $input['date_enable'] ) || $input['date_enable'] != '1' )
1007
- $output['date_enable'] = 0;
1008
- else
1009
- $output['date_enable'] = 1;
1010
-
1011
- if ( ! isset( $input['styles_disable'] ) || $input['styles_disable'] != '1' )
1012
- $output['styles_disable'] = 0;
1013
- else
1014
- $output['styles_disable'] = 1;
1015
-
1016
- if ( ! isset( $input['video_link'] ) || strtolower( $input['video_link'] ) !== 'true' )
1017
- $output['video_link'] = 'false';
1018
- else
1019
- $output['video_link'] = 'true';
1020
-
1021
- if ( $input['cron_interval'] != $current_cron_interval ) {
1022
- wp_clear_scheduled_hook( 'wprss_fetch_all_feeds_hook' );
1023
- wp_schedule_event( time(), $input['cron_interval'], 'wprss_fetch_all_feeds_hook' );
1024
- }
1025
-
1026
- // Return the array processing any additional functions filtered by this action
1027
- return apply_filters( 'wprss_settings_general_validate', $output, $input );
1028
- }
1029
-
1030
-
1031
- /**
1032
- * Validates the licenses settings
1033
- *
1034
- * @since 3.8
1035
- */
1036
- function wprss_settings_license_keys_validate( $input ) {
1037
- // Get the current licenses option
1038
- $licenses = get_option( 'wprss_settings_license_keys' );
1039
- // If no licenses have been defined yet, create an empty array
1040
- if ( !is_array( $licenses ) ) {
1041
- $licenses = array();
1042
- }
1043
- // For each entry in the received input
1044
- foreach ( $input as $addon => $license_code ) {
1045
- $addon_code = explode( '_', $addon );
1046
- $addon_code = isset( $addon_code[0] ) ? $addon_code[0] : null;
1047
- // Only save if the entry does not exist OR the code is different
1048
- if ( array_key_exists( $addon, $licenses ) && $license_code === $licenses[ $addon ] )
1049
- continue;
1050
-
1051
- $is_valid = apply_filters( 'wprss_settings_license_key_is_valid', true, $license_code );
1052
- if( $addon_code )
1053
- $is_valid = apply_filters( "wprss_settings_license_key_{$addon_code}_is_valid", $is_valid, $license_code );
1054
- if( !$is_valid ) continue;
1055
-
1056
- // Save it to the licenses option
1057
- $licenses[ $addon ] = $license_code;
1058
- }
1059
- wprss_check_license_statuses();
1060
- // Return the new licenses
1061
- return $licenses;
1062
- }
1063
-
1064
-
1065
-
1066
- add_action( 'wprss_check_license_statuses', 'wprss_check_license_statuses' );
1067
- /**
1068
- * Checks the license statuses
1069
- *
1070
- * @since 3.8.1
1071
- */
1072
- function wprss_check_license_statuses() {
1073
- $license_statuses = get_option( 'wprss_settings_license_statuses', array() );
1074
-
1075
- if ( count( $license_statuses ) === 0 ) return;
1076
-
1077
- $found_inactive = FALSE;
1078
- foreach ( $license_statuses as $addon => $status ) {
1079
- if ( $status !== 'active' ) {
1080
- $found_inactive = TRUE;
1081
- break;
1082
- }
1083
- }
1084
-
1085
- if ( $found_inactive ) {
1086
- set_transient( 'wprss_notify_inactive_licenses', 1, 0 );
1087
- }
1088
- }
1089
-
1090
-
1091
-
1092
- /**
1093
- * Validates the wprss_secure_reset_code option
1094
- *
1095
- * @since 3.7.1
1096
- */
1097
- function wprss_secure_reset_code_validate( $input ) {
1098
- return $input;
1099
- }
1100
-
1101
-
1102
-
1103
- /**
1104
- * Validates the presstrends setting
1105
- *
1106
- * @since 3.6
1107
- */
1108
- function wprss_tracking_validate ( $input ) {
1109
- $output = $input;
1110
- if ( ! isset( $input['wprss_tracking'] ) ) {
1111
- $output['wprss_tracking'] = 0;
1112
- }
1113
- return $output;
1114
- }
1115
-
1116
-
1117
-
1118
- /**
1119
- * Returns the units used for the limit by age option.
1120
- *
1121
- * @since 3.8
1122
- */
1123
- function wprss_age_limit_units() {
1124
- return apply_filters(
1125
- 'wprss_age_limit_units',
1126
- array(
1127
- 'days',
1128
- 'weeks',
1129
- 'months',
1130
- 'years'
1131
- )
1132
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1133
  }
1
+ <?php
2
+ /**
3
+ * Plugin settings related functions
4
+ *
5
+ * Note: Wording of options and settings is confusing, due to the plugin originally only having
6
+ * an 'options' page to enter feed sources, and now needing two screens, one for feed sources and one for
7
+ * general settings. Might implement something cleaner in the future.
8
+ *
9
+ * @package WP PRSS Aggregator
10
+ */
11
+
12
+
13
+
14
+ /**
15
+ * Returns the given general setting option value form the database, or the default value if it is not found.
16
+ *
17
+ * @param option_name The name of the option to get
18
+ * @return mixed
19
+ * @since 3.7.1
20
+ */
21
+ function wprss_get_general_setting( $option_name ) {
22
+ $options = get_option( 'wprss_settings_general', array() );
23
+ $defaults = wprss_get_default_settings_general();
24
+ return ( ( isset( $options[ $option_name ] ) )? $options[$option_name] : $defaults[$option_name] );
25
+ }
26
+
27
+
28
+ add_action( 'admin_init', 'wprss_admin_init' );
29
+ /**
30
+ * Register and define options and settings
31
+ * @since 2.0
32
+ * @todo add option for cron frequency
33
+ *
34
+ * Note: In the future might change to
35
+ * the way EDD builds the settings pages, cleaner method.
36
+ */
37
+ function wprss_admin_init() {
38
+
39
+ register_setting(
40
+ 'wprss_settings_general', // A settings group name.
41
+ 'wprss_settings_general', // The name of an option to sanitize and save.
42
+ 'wprss_settings_general_validate' // A callback function that sanitizes the option's value.
43
+ );
44
+
45
+ // Licensing of add-ons
46
+ register_setting(
47
+ 'wprss_settings_license_keys',
48
+ 'wprss_settings_license_keys',
49
+ 'wprss_settings_license_keys_validate'
50
+ );
51
+
52
+
53
+ $sections = apply_filters(
54
+ 'wprss_settings_sections_array',
55
+ array(
56
+ 'general' => __( 'General plugin settings', WPRSS_TEXT_DOMAIN ),
57
+ 'display' => __( 'General display settings', WPRSS_TEXT_DOMAIN ),
58
+ 'source' => __( 'Source display settings', WPRSS_TEXT_DOMAIN ),
59
+ 'date' => __( 'Date display settings', WPRSS_TEXT_DOMAIN ),
60
+ 'styles' => __( 'Styles', WPRSS_TEXT_DOMAIN ),
61
+ )
62
+ );
63
+
64
+ // Define the settings per section
65
+ $settings = apply_filters(
66
+ 'wprss_settings_array',
67
+ array(
68
+ 'general' => array(
69
+ 'limit-feed-items-by-age' => array(
70
+ 'label' => __( 'Limit feed items by age', WPRSS_TEXT_DOMAIN ),
71
+ 'callback' => 'wprss_setting_limit_feed_items_age_callback'
72
+ ),
73
+ 'limit-feed-items-db' => array(
74
+ 'label' => __( 'Limit feed items stored', WPRSS_TEXT_DOMAIN ),
75
+ 'callback' => 'wprss_setting_limit_feed_items_callback'
76
+ ),
77
+ 'limit-feed-items-imported' => array(
78
+ 'label' => __( 'Limit feed items per feed', WPRSS_TEXT_DOMAIN ),
79
+ 'callback' => 'wprss_setting_limit_feed_items_imported_callback'
80
+ ),
81
+ 'cron-interval' => array(
82
+ 'label' => __( 'Feed processing interval', WPRSS_TEXT_DOMAIN ),
83
+ 'callback' => 'wprss_setting_cron_interval_callback'
84
+ ),
85
+ 'unique-titles' => array(
86
+ 'label' => __( 'Unique titles only', WPRSS_TEXT_DOMAIN),
87
+ 'callback' => 'wprss_setting_unique_titles'
88
+ ),
89
+ 'custom-feed-url' => array(
90
+ 'label' => __( 'Custom feed URL', WPRSS_TEXT_DOMAIN ),
91
+ 'callback' => 'wprss_settings_custom_feed_url_callback'
92
+ ),
93
+ 'custom-feed-title' => array(
94
+ 'label' => __( 'Custom feed Title', WPRSS_TEXT_DOMAIN ),
95
+ 'callback' => 'wprss_settings_custom_feed_title_callback'
96
+ ),
97
+ 'custom-feed-limit' => array(
98
+ 'label' => __( 'Custom feed limit', WPRSS_TEXT_DOMAIN ),
99
+ 'callback' => 'wprss_setings_custom_feed_limit_callback'
100
+ ),
101
+ 'tracking' => array(
102
+ 'label' => __( 'Anonymous tracking', WPRSS_TEXT_DOMAIN ),
103
+ 'callback' => 'wprss_tracking_callback',
104
+ )
105
+ ),
106
+
107
+ 'display' => array(
108
+ // Title options
109
+ 'link-enable' => array(
110
+ 'label' => __( 'Link title', WPRSS_TEXT_DOMAIN ),
111
+ 'callback' => 'wprss_setting_title_link_callback'
112
+ ),
113
+ 'title-limit' => array(
114
+ 'label' => __( 'Title maximum length', WPRSS_TEXT_DOMAIN ),
115
+ 'callback' => 'wprss_setting_title_length_callback'
116
+ ),
117
+
118
+
119
+
120
+ // Misc Options
121
+ 'authors-enable' => array(
122
+ 'label' => __( 'Show authors', WPRSS_TEXT_DOMAIN ),
123
+ 'callback' => 'wprss_setting_authors_enable_callback',
124
+ ),
125
+ 'video-links' => array(
126
+ 'label' => __( 'For video feed items use', WPRSS_TEXT_DOMAIN ),
127
+ 'callback' => 'wprss_setting_video_links_callback'
128
+ ),
129
+ 'pagination' => array(
130
+ 'label' => __( 'Pagination type', WPRSS_TEXT_DOMAIN ),
131
+ 'callback' => 'wprss_setting_pagination_type_callback',
132
+ ),
133
+ 'feed-limit' => array(
134
+ 'label' => __( 'Feed display limit', WPRSS_TEXT_DOMAIN ),
135
+ 'callback' => 'wprss_setting_feed_limit_callback'
136
+ ),
137
+ 'open-dd' => array(
138
+ 'label' => __( 'Open links behaviour', WPRSS_TEXT_DOMAIN ),
139
+ 'callback' => 'wprss_setting_open_dd_callback'
140
+ ),
141
+ 'follow-dd' => array(
142
+ 'label' => __( 'Set links as nofollow', WPRSS_TEXT_DOMAIN ),
143
+ 'callback' => 'wprss_setting_follow_dd_callback'
144
+ ),
145
+ ),
146
+
147
+ // Source Options
148
+ 'source' => array(
149
+ 'source-enable' => array(
150
+ 'label' => __( 'Show source', WPRSS_TEXT_DOMAIN ),
151
+ 'callback' => 'wprss_setting_source_enable_callback'
152
+ ),
153
+ 'text-preceding-source' => array(
154
+ 'label' => __( 'Text preceding source', WPRSS_TEXT_DOMAIN ),
155
+ 'callback' => 'wprss_setting_text_preceding_source_callback'
156
+ ),
157
+ 'source-link' => array(
158
+ 'label' => __( 'Link source', WPRSS_TEXT_DOMAIN ),
159
+ 'callback' => 'wprss_setting_source_link_callback'
160
+ ),
161
+ ),
162
+
163
+ // Date options
164
+ 'date' => array(
165
+ 'date-enable' => array(
166
+ 'label' => __( 'Show date', WPRSS_TEXT_DOMAIN ),
167
+ 'callback' => 'wprss_setting_date_enable_callback'
168
+ ),
169
+ 'text-preceding-date' => array(
170
+ 'label' => __( 'Text preceding date', WPRSS_TEXT_DOMAIN ),
171
+ 'callback' => 'wprss_setting_text_preceding_date_callback'
172
+ ),
173
+ 'date-format' => array(
174
+ 'label' => __( 'Date format', WPRSS_TEXT_DOMAIN ),
175
+ 'callback' => 'wprss_setting_date_format_callback'
176
+ ),
177
+ 'time-ago-format-enable' => array(
178
+ 'label' => __( 'Time ago format', WPRSS_TEXT_DOMAIN ),
179
+ 'callback' => 'wprss_setting_time_ago_format_enable_callback'
180
+ ),
181
+ ),
182
+
183
+ 'styles' => array(
184
+ 'styles-disable' => array(
185
+ 'label' => __( 'Disable Styles', WPRSS_TEXT_DOMAIN ),
186
+ 'callback' => 'wprss_setting_styles_disable_callback'
187
+ )
188
+ )
189
+ )
190
+ );
191
+
192
+ if ( apply_filters( 'wprss_use_fixed_feed_limit', FALSE ) === FALSE ) {
193
+ unset( $settings['general']['limit-feed-items-db'] );
194
+ }
195
+
196
+ $setting_field_id_prefix = 'wprss-settings-';
197
+
198
+
199
+ // Loop through each setting field and register it
200
+ foreach( $settings as $section => $fields ) {
201
+ if ( count( $fields ) > 0 ) {
202
+ $section_desc = $sections[ $section ];
203
+ add_settings_section(
204
+ "wprss_settings_${section}_section",
205
+ $section_desc,
206
+ "wprss_settings_${section}_callback",
207
+ 'wprss_settings_general'
208
+ );
209
+
210
+ foreach ( $fields as $id => $data ) {
211
+
212
+ /**
213
+ * @var This will be passed to the field callback as the only argument
214
+ * @see http://codex.wordpress.org/Function_Reference/add_settings_field#Parameters
215
+ */
216
+ $callback_args = array(
217
+ 'field_id' => $id,
218
+ 'field_id_prefix' => $setting_field_id_prefix,
219
+ 'section_id' => $section,
220
+ 'field_label' => isset( $data['label'] ) ? $data['label'] : null,
221
+ 'tooltip' => isset( $data['tooltip'] ) ? $data['tooltip'] : null
222
+ );
223
+
224
+
225
+ add_settings_field(
226
+ $setting_field_id_prefix . $id,
227
+ $data['label'],
228
+ $data['callback'],
229
+ 'wprss_settings_general',
230
+ "wprss_settings_${section}_section",
231
+ $callback_args
232
+ );
233
+
234
+ }
235
+ }
236
+ }
237
+
238
+
239
+ /*
240
+ // SECURE RESET OPTION
241
+ register_setting(
242
+ 'wprss_secure_reset', // A settings group name.
243
+ 'wprss_secure_reset_code', // The name of an option to sanitize and save.
244
+ '' // A callback function that sanitizes the option's value.
245
+ );
246
+ add_settings_section(
247
+ 'wprss_secure_reset_section', // ID of section
248
+ __( 'Secure Reset', WPRSS_TEXT_DOMAIN ), // Title of section
249
+ 'wprss_secure_reset_section_callback', // Callback that renders the section header
250
+ 'wprss_settings_general' // The page on which to display the section
251
+ );
252
+ add_settings_field(
253
+ 'wprss-settings-secure-reset', // ID of setting
254
+ __( 'Secure Reset', WPRSS_TEXT_DOMAIN ), // The title of the setting
255
+ 'wprss_settings_secure_reset_code_callback', // The callback that renders the setting
256
+ 'wprss_settings_general', // The page on which to display the setting
257
+ "wprss_secure_reset_section" // The section in which to display the setting
258
+ );
259
+ */
260
+
261
+
262
+
263
+ do_action( 'wprss_admin_init' );
264
+ }
265
+
266
+
267
+ /**
268
+ * Returns the HTML of a tooltip handle.
269
+ *
270
+ * Filters used:
271
+ * - `wprss_settings_inline_help_default_options` - The default options for "Settings" page's tooltips
272
+ * - `wprss_settings_inline_help_id_prefix` - The prefix for all tooltip IDs for the "Settings" page.
273
+ *
274
+ * @param string $id The ID of the tooltip
275
+ * @param string|null $text Text for this tooltip, if any.
276
+ * @param array $options Any options for this setting.
277
+ * @return string Tooltip handle HTML. See {@link WPRSS_Help::tooltip()}.
278
+ */
279
+ function wprss_settings_inline_help( $id, $text = null, $options = array() ) {
280
+ $help = WPRSS_Help::get_instance();
281
+
282
+ // Default options, entry point
283
+ $defaults = apply_filters( 'wprss_settings_inline_help_default_options', array(
284
+ 'tooltip_handle_class_extra' => $help->get_options('tooltip_handle_class_extra') . ' ' . $help->get_options('tooltip_handle_class') . '-setting'
285
+ ));
286
+
287
+ $options = $help->array_merge_recursive_distinct( $defaults, $options );
288
+
289
+ // ID Prefix
290
+ $id = apply_filters( 'wprss_settings_inline_help_id_prefix', 'setting-' ) . $id;
291
+
292
+ return $help->tooltip( $id, $text, $options );
293
+ }
294
+
295
+
296
+ /**
297
+ *
298
+ * @param type $string
299
+ * @return type
300
+ */
301
+ function wprss_settings_field_name_prefix( $string = '' ) {
302
+ $string = (string) $string;
303
+ $prefix = apply_filters( 'wprss_settings_field_name_prefix', 'wprss_settings_', $string );
304
+ return $prefix . $string;
305
+ }
306
+
307
+
308
+ /**
309
+ * Generates a uniform setting field name for use in HTML.
310
+ * The parts used are the ID of the field, the section it is in, and an optional prefix.
311
+ * All parts are optional, but, if they appear, they shall appear in this order: $prefix, $section, $id.
312
+ *
313
+ * If only the section is not specified, the $id will be simply prefixed by $prefix.
314
+ * If either the $id or the $section are empty (but not both), $prefix will be stripped of known separators.
315
+ * Empty parts will be excluded.
316
+ *
317
+ * @param string $id ID of the field.
318
+ * @param string|null $section Name of the section, to which this field belongs.
319
+ * @param string|null $prefix The string to prefix the name with; appears first. If boolean false, no prefix will be applied. Default: return value of {@link wprss_settings_field_name_prefix()}.
320
+ * @return string Name of the settings field, namespaced and optionally prefixed.
321
+ */
322
+ function wprss_settings_field_name( $id = null, $section = null, $prefix = null ) {
323
+ if( $prefix !== false ) $prefix = is_null( $prefix ) ? wprss_settings_field_name_prefix() : $prefix;
324
+ else $prefix = '';
325
+
326
+ $section = (string) $section;
327
+
328
+ $format = '';
329
+ if( !strlen( $section ) xor !strlen($id) ) $prefix = trim ( $prefix, "\t\n\r _-:" );
330
+ if( strlen( $prefix ) ) $format .= '%3$s';
331
+ if( strlen( $section ) ) $format .= '%2$s';
332
+ if( strlen( $id ) ) $format .= ( !strlen( $section ) ? '%1$s' : '[%1$s]' );
333
+
334
+ return apply_filters( 'wprss_settings_field_name', sprintf( $format, $id, $section, $prefix ), $id, $section, $prefix );
335
+ }
336
+
337
+
338
+ /**
339
+ * Build the plugin settings page, used to save general settings like whether a link should be follow or no follow
340
+ * @since 1.1
341
+ */
342
+ function wprss_settings_page_display() {
343
+ ?>
344
+ <div class="wrap">
345
+ <?php screen_icon( 'wprss-aggregator' ); ?>
346
+
347
+ <h2><?php _e( 'WP RSS Aggregator Settings', WPRSS_TEXT_DOMAIN ); ?></h2>
348
+
349
+ <?php settings_errors(); ?>
350
+
351
+ <?php $active_tab = isset( $_GET['tab'] ) ? $_GET['tab'] : 'general_settings'; ?>
352
+
353
+ <?php
354
+
355
+ $default_tabs = array(
356
+ 'general' => array(
357
+ 'label' => __( 'General', WPRSS_TEXT_DOMAIN ),
358
+ 'slug' => 'general_settings',
359
+ ),
360
+ 'licenses' => array(
361
+ 'label' => __( 'Licenses', WPRSS_TEXT_DOMAIN ),
362
+ 'slug' => 'licenses_settings'
363
+ )
364
+ );
365
+
366
+ $addon_tabs = apply_filters( 'wprss_options_tabs', array() );
367
+
368
+ $tabs = array_merge( array( $default_tabs['general'] ), $addon_tabs , array( $default_tabs['licenses'] ) );
369
+
370
+ $show_tabs = ( count( $addon_tabs ) > 0 ) || apply_filters( 'wprss_show_settings_tabs_condition', FALSE );
371
+
372
+ if ( $show_tabs ) { ?>
373
+ <h2 class="nav-tab-wrapper">
374
+ <?php
375
+ foreach ( $tabs as $tab => $tab_property ) { ?>
376
+ <a href="?post_type=wprss_feed&page=wprss-aggregator-settings&tab=<?php echo esc_attr( $tab_property['slug'] ); ?>"
377
+ class="nav-tab <?php echo $active_tab == $tab_property['slug'] ? 'nav-tab-active' : ''; ?>"><?php echo esc_html( $tab_property['label'] ); ?></a>
378
+ <?php } ?>
379
+ <?php } ?>
380
+ </h2>
381
+
382
+ <form action="options.php" method="post">
383
+
384
+ <?php
385
+
386
+ if ( $active_tab === 'general_settings' ) {
387
+ settings_fields( 'wprss_settings_general' );
388
+ //settings_fields( 'wprss_secure_reset' );
389
+ do_settings_sections( 'wprss_settings_general' );
390
+ }
391
+ elseif ( $show_tabs ) {
392
+
393
+ if ( $active_tab === 'licenses_settings' ) {
394
+ settings_fields( 'wprss_settings_license_keys' );
395
+ do_settings_sections( 'wprss_settings_license_keys' );
396
+ }
397
+
398
+ do_action( 'wprss_add_settings_fields_sections', $active_tab );
399
+ }
400
+
401
+ submit_button( __( 'Save Settings', WPRSS_TEXT_DOMAIN ) );
402
+
403
+ ?>
404
+ </form>
405
+ </div>
406
+ <?php
407
+ }
408
+
409
+
410
+ /**
411
+ * General settings section header
412
+ * @since 3.0
413
+ */
414
+ function wprss_settings_general_callback() {
415
+ echo wpautop( __( 'These are the general settings for WP RSS Aggregator.', WPRSS_TEXT_DOMAIN ) );
416
+ }
417
+
418
+
419
+ /**
420
+ * General settings display section header
421
+ * @since 3.5
422
+ */
423
+ function wprss_settings_display_callback() {
424
+ echo wpautop( __( 'In this section you can find some general options that control how the feed items are displayed.', WPRSS_TEXT_DOMAIN ) );
425
+ }
426
+
427
+
428
+ /**
429
+ * Display settings for source section header
430
+ *
431
+ * @since 4.2.4
432
+ */
433
+ function wprss_settings_source_callback() {
434
+ echo wpautop( __( "Options that control how the feed item's source is displayed.", WPRSS_TEXT_DOMAIN ) );
435
+ }
436
+
437
+ /**
438
+ * Display settings for date section header
439
+ *
440
+ * @since 4.2.4
441
+ */
442
+ function wprss_settings_date_callback() {
443
+ echo wpautop( __( "Options that control how the feed item's date is displayed.", WPRSS_TEXT_DOMAIN ) );
444
+ }
445
+
446
+
447
+ /**
448
+ * General settings styles section header
449
+ * @since 3.0
450
+ */
451
+ function wprss_settings_styles_callback() {
452
+ echo wpautop( __( 'If you would like to disable all styles used in this plugin, tick the checkbox.', WPRSS_TEXT_DOMAIN ) );
453
+ }
454
+
455
+
456
+ /**
457
+ * General settings scure reset section header
458
+ * @since 3.0
459
+ */
460
+ function wprss_secure_reset_section_callback() {
461
+ echo wpautop( __( 'Set your security reset code, in case of any errors.', WPRSS_TEXT_DOMAIN ) );
462
+ }
463
+
464
+
465
+ /**
466
+ * Tracking settings section header
467
+ * @since 3.0
468
+ */
469
+ function wprss_tracking_section_callback() {
470
+ echo wpautop( __( 'Participate in helping us make the plugin better.', WPRSS_TEXT_DOMAIN ) );
471
+ }
472
+
473
+
474
+ /**
475
+ * Follow or No Follow dropdown
476
+ * @since 1.1
477
+ */
478
+ function wprss_setting_follow_dd_callback( $field ) {
479
+ $follow_dd = wprss_get_general_setting( 'follow_dd' );
480
+
481
+ $checked = ( $follow_dd === 'no_follow' );
482
+ $checked_attr = ( $checked )? 'checked="checked"' : '';
483
+ ?>
484
+ <input type="hidden" name="wprss_settings_general[follow_dd]" value="follow" />
485
+ <input type="checkbox" id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[follow_dd]" value="no_follow" <?php echo $checked_attr ?> />
486
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
487
+ }
488
+
489
+
490
+ /**
491
+ * Use original video link, or embedded video links dropwdown
492
+ * @since 3.4
493
+ */
494
+ function wprss_setting_video_links_callback( $field ) {
495
+ $video_link = wprss_get_general_setting('video_link');
496
+ $items = array(
497
+ 'false' => __( 'Original page link', WPRSS_TEXT_DOMAIN ),
498
+ 'true' => __( 'Embedded video player link', WPRSS_TEXT_DOMAIN )
499
+ );
500
+ ?>
501
+ <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[video_link]">
502
+ <?php
503
+ foreach ( $items as $boolean => $text ) {
504
+ $selected = ( $video_link === $boolean )? 'selected="selected"' : '';
505
+ ?><option value="<?php echo $boolean ?>" <?php echo $selected ?>><?php echo $text ?></option><?php
506
+ }
507
+ ?>
508
+ </select>
509
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?>
510
+ <p>
511
+ <span class="description">
512
+ <?php _e( 'This will not affect already imported feed items.', WPRSS_TEXT_DOMAIN ) ?>
513
+ </span>
514
+ </p>
515
+ <?php
516
+ }
517
+
518
+
519
+ /**
520
+ * Link open setting dropdown
521
+ * @since 1.1
522
+ */
523
+ function wprss_setting_open_dd_callback( $field ) {
524
+ $open_dd = wprss_get_general_setting('open_dd');
525
+
526
+ $items = array(
527
+ __( 'Lightbox', WPRSS_TEXT_DOMAIN ),
528
+ __( 'New window', WPRSS_TEXT_DOMAIN ),
529
+ __( 'Self', WPRSS_TEXT_DOMAIN )
530
+ );
531
+ ?>
532
+ <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[open_dd]">
533
+ <?php
534
+ foreach( $items as $item ) {
535
+ $selected = ( $open_dd == $item ) ? 'selected="selected"' : '';
536
+ ?><option value="<?php echo $item ?>" <?php echo $selected ?>><?php echo $item ?></option><?php
537
+ }
538
+ ?>
539
+ </select>
540
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
541
+ }
542
+
543
+
544
+ /**
545
+ * Set limit for feeds on frontend
546
+ * @since 2.0
547
+ */
548
+ function wprss_setting_feed_limit_callback( $field ) {
549
+ $feed_limit = wprss_get_general_setting( 'feed_limit' );
550
+ ?>
551
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[feed_limit]" type="text" value="<?php echo $feed_limit ?>" />
552
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
553
+ }
554
+
555
+
556
+ /**
557
+ * Set date format
558
+ * @since 3.0
559
+ */
560
+ function wprss_setting_date_format_callback( $field ) {
561
+ $date_format = wprss_get_general_setting( 'date_format' );
562
+ ?>
563
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[date_format]" type="text" value="<?php echo $date_format ?>" />
564
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?>
565
+ <p>
566
+ <a href="http://codex.wordpress.org/Formatting_Date_and_Time">
567
+ <?php _e( 'PHP Date Format Reference', WPRSS_TEXT_DOMAIN ); ?>
568
+ </a>
569
+ </p>
570
+ <?php
571
+ }
572
+
573
+
574
+
575
+ /**
576
+ * Enable linked title
577
+ * @since 3.0
578
+ */
579
+ function wprss_setting_title_link_callback( $field ) {
580
+ $title_link = wprss_get_general_setting( 'title_link' );
581
+ ?>
582
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[title_link]" type="checkbox" value="1" <?php echo checked( 1, $title_link, false ) ?> />
583
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
584
+ }
585
+
586
+
587
+
588
+ /**
589
+ * Set the title length limit
590
+ * @since 3.0
591
+ */
592
+ function wprss_setting_title_length_callback( $field ) {
593
+ $title_limit = wprss_get_general_setting( 'title_limit' );
594
+ ?>
595
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[title_limit]" type="number" class="wprss-number-roller" min="0" value="<?php echo $title_limit ?>" placeholder="<?php _e( 'No limit', WPRSS_TEXT_DOMAIN ) ?>" />
596
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
597
+ }
598
+
599
+
600
+ /**
601
+ * Enable source
602
+ * @since 3.0
603
+ */
604
+ function wprss_setting_source_enable_callback( $field ) {
605
+ $source_enable = wprss_get_general_setting( 'source_enable' );
606
+ ?>
607
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[source_enable]" type="checkbox" value="1" <?php echo checked( 1, $source_enable, false ) ?> />
608
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
609
+ }
610
+
611
+ /**
612
+ * Enable linked title
613
+ * @since 3.0
614
+ */
615
+ function wprss_setting_source_link_callback( $field ) {
616
+ $source_link = wprss_get_general_setting( 'source_link' );
617
+ ?>
618
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[source_link]" type="checkbox" value="1" <?php echo checked( 1, $source_link, false ) ?> />
619
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
620
+ }
621
+
622
+
623
+ /**
624
+ * Set text preceding source
625
+ * @since 3.0
626
+ */
627
+ function wprss_setting_text_preceding_source_callback( $field ) {
628
+ $text_preceding_source = wprss_get_general_setting( 'text_preceding_source' );
629
+ ?>
630
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[text_preceding_source]" type="text" value="<?php echo $text_preceding_source ?>" />
631
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
632
+ }
633
+ /**
634
+ * Enable date
635
+ * @since 3.0
636
+ */
637
+ function wprss_setting_date_enable_callback( $field ) {
638
+ $date_enable = wprss_get_general_setting( 'date_enable' );
639
+ ?>
640
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[date_enable]" type="checkbox" value="1" <?php echo checked( 1, $date_enable, false ) ?> />
641
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
642
+ }
643
+
644
+ /**
645
+ * Set text preceding date
646
+ * @since 3.0
647
+ */
648
+ function wprss_setting_text_preceding_date_callback( $field ) {
649
+ $text_preceding_date = wprss_get_general_setting( 'text_preceding_date' );
650
+ ?>
651
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[text_preceding_date]" type="text" value="<?php echo $text_preceding_date ?>" />
652
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
653
+ }
654
+
655
+
656
+ /**
657
+ * Shows the feed item authors option
658
+ *
659
+ * @since 4.2.4
660
+ */
661
+ function wprss_setting_authors_enable_callback( $field ) {
662
+ $authors_enable = wprss_get_general_setting( 'authors_enable' );
663
+ ?>
664
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[authors_enable]" type="checkbox" value="1" <?php echo checked( 1, $authors_enable, false ) ?> />
665
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
666
+ }
667
+
668
+
669
+ /**
670
+ * Pagination Type
671
+ *
672
+ * @since 4.2.3
673
+ */
674
+ function wprss_setting_pagination_type_callback( $field ) {
675
+ $pagination = wprss_get_general_setting( 'pagination' );
676
+ $options = array(
677
+ 'default' => __( '"Older posts" and "Newer posts" links', WPRSS_TEXT_DOMAIN ),
678
+ 'numbered' => __( 'Page numbers with "Next" and "Previous" page links', WPRSS_TEXT_DOMAIN ),
679
+ );
680
+ ?>
681
+ <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[pagination]">
682
+ <?php
683
+ foreach( $options as $value => $text ) {
684
+ $selected = ( $value === $pagination )? 'selected="selected"' : '';
685
+ ?><option value="<?php echo $value ?>" <?php echo $selected ?>><?php echo $text ?></option><?php
686
+ }
687
+ ?>
688
+ </select>
689
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
690
+ }
691
+
692
+
693
+
694
+ /**
695
+ * Limit number of feed items stored by their age
696
+ * @since 3.0
697
+ */
698
+ function wprss_setting_limit_feed_items_age_callback( $field ) {
699
+ $limit_feed_items_age = wprss_get_general_setting( 'limit_feed_items_age' );
700
+ $limit_feed_items_age_unit = wprss_get_general_setting( 'limit_feed_items_age_unit' );
701
+ $units = wprss_age_limit_units();
702
+ // echo wprss_settings_field_name( $field_info['field_id'], $field_info['section_id'], $field_info['field_name_prefix'] )
703
+ ?>
704
+
705
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[limit_feed_items_age]" type="number" min="0"
706
+ class="wprss-number-roller" placeholder="<?php _e( 'No limit', WPRSS_TEXT_DOMAIN ) ?>" value="<?php echo $limit_feed_items_age; ?>" />
707
+
708
+ <select id="limit-feed-items-age-unit" name="wprss_settings_general[limit_feed_items_age_unit]">
709
+ <?php foreach ( $units as $unit ) : ?>
710
+ <option value="<?php echo $unit ?>" <?php selected( $limit_feed_items_age_unit, $unit ) ?> ><?php _e( $unit, WPRSS_TEXT_DOMAIN ) ?></option>
711
+ <?php endforeach ?>
712
+ </select>
713
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?>
714
+
715
+ <br/>
716
+ <?php
717
+ }
718
+
719
+
720
+
721
+ /**
722
+ * Limit number of feed items stored
723
+ * @since 3.0
724
+ */
725
+ function wprss_setting_limit_feed_items_callback( $field ) {
726
+ $limit_feed_items_db = wprss_get_general_setting( 'limit_feed_items_db' );
727
+ ?>
728
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[limit_feed_items_db]" type="text" value="<?php echo $limit_feed_items_db ?>" />
729
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
730
+ }
731
+
732
+
733
+ /**
734
+ * Limit number of feed items imported per feed
735
+ * @since 3.1
736
+ */
737
+ function wprss_setting_limit_feed_items_imported_callback( $field ) {
738
+ $limit_feed_items_imported = wprss_get_general_setting( 'limit_feed_items_imported' );
739
+ ?>
740
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[limit_feed_items_imported]" type="text" value="<?php echo $limit_feed_items_imported ?>" placeholder="<?php _e( 'No Limit', WPRSS_TEXT_DOMAIN ) ?>" />
741
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
742
+ }
743
+
744
+
745
+ /**
746
+ * Gets a sorted (according to interval) list of the cron schedules
747
+ * @since 3.0
748
+ */
749
+ function wprss_get_schedules() {
750
+ $schedules = wp_get_schedules();
751
+ uasort( $schedules, create_function( '$a,$b', 'return $a["interval"]-$b["interval"];' ) );
752
+ return $schedules;
753
+ }
754
+
755
+
756
+ /**
757
+ * Cron interval dropdown callback
758
+ * @since 3.0
759
+ */
760
+ function wprss_setting_cron_interval_callback( $field ) {
761
+ $options = get_option( 'wprss_settings_general' );
762
+ $current = $options['cron_interval'];
763
+
764
+ $schedules = wprss_get_schedules();
765
+ // Set the allowed Cron schedules, we don't want any intervals that can lead to issues with server load
766
+ $wprss_schedules = apply_filters(
767
+ 'wprss_schedules',
768
+ array( 'fifteen_min', 'thirty_min', 'hourly', 'two_hours', 'twicedaily', 'daily' )
769
+ );
770
+ ?>
771
+ <select id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[cron_interval]">
772
+ <?php
773
+ foreach( $schedules as $schedule_name => $schedule_data ):
774
+ if ( in_array( $schedule_name, $wprss_schedules ) ): ?>
775
+ <option value="<?php echo $schedule_name ?>" <?php selected( $current, $schedule_name ) ?> >
776
+ <?php echo $schedule_data['display'] ?> (<?php echo wprss_interval( $schedule_data['interval'] ) ?>)
777
+ </option>
778
+ <?php endif ?>
779
+ <?php endforeach ?>
780
+ </select>
781
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?><?php
782
+ }
783
+
784
+
785
+ /**
786
+ * Unique titles only checkbox callback
787
+ * @since 4.7
788
+ */
789
+ function wprss_setting_unique_titles( $field ) {
790
+ $unique_titles = wprss_get_general_setting( 'unique_titles' );
791
+ ?>
792
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[unique_titles]" type="checkbox" value="1" <?php echo checked( 1, $unique_titles, false ) ?> />
793
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
794
+ }
795
+
796
+
797
+ /**
798
+ * Sets the custom feed URL
799
+ * @since 3.3
800
+ */
801
+ function wprss_settings_custom_feed_url_callback( $field ) {
802
+ $custom_feed_url = wprss_get_general_setting( 'custom_feed_url' );
803
+ ?>
804
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[custom_feed_url]" type="text" value="<?php echo $custom_feed_url ?>" />
805
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
806
+ }
807
+
808
+ /**
809
+ * Sets the custom feed title
810
+ * @since 4.1.2
811
+ */
812
+ function wprss_settings_custom_feed_title_callback( $field ) {
813
+ $custom_feed_title = wprss_get_general_setting( 'custom_feed_title' );
814
+ ?>
815
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[custom_feed_title]" type="text" value="<?php echo $custom_feed_title ?>" />
816
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
817
+ }
818
+
819
+ /**
820
+ * Sets the custom feed limit
821
+ * @since 3.3
822
+ */
823
+ function wprss_setings_custom_feed_limit_callback( $field ) {
824
+ $custom_feed_limit = wprss_get_general_setting( 'custom_feed_limit' );
825
+ ?>
826
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[custom_feed_limit]" placeholder="<?php _e( 'Default', WPRSS_TEXT_DOMAIN ) ?>" min="0" class="wprss-number-roller" type="number" value="<?php echo $custom_feed_limit ?>" />
827
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
828
+ }
829
+
830
+ /**
831
+ * Disable styles
832
+ * @since 3.0
833
+ */
834
+ function wprss_setting_styles_disable_callback( $field ) {
835
+ $styles_disable = wprss_get_general_setting( 'styles_disable' );
836
+ ?>
837
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[styles_disable]" type="checkbox" value="1" <?php echo checked( 1, $styles_disable, false ) ?> />
838
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
839
+ }
840
+
841
+
842
+
843
+ /**
844
+ * Secure Reset section
845
+ *
846
+ * @since 3.7.1
847
+ */
848
+ function wprss_settings_secure_reset_code_callback( $args ) {
849
+ $reset_code = get_option( 'wprss_secure_reset_code', '' );
850
+ ?>
851
+ <input id="wprss-secure-reset-code" name="wprss_secure_reset_code" type="input" value="<?php echo $reset_code; ?>" />
852
+ <button type="button" role="button" id="wprss-secure-reset-generate"><?php _e( 'Generate Random Code', WPRSS_TEXT_DOMAIN ) ?></button>
853
+
854
+ <br/>
855
+
856
+ <label class="description" for="wprss-secure-reset-code">
857
+ <?php _e( 'Enter the code to use to securely reset the plugin and deactivate it. Be sure to save this code somewhere safe.', WPRSS_TEXT_DOMAIN ) ?><br/>
858
+ </label>
859
+
860
+ <br/>
861
+
862
+ <p>
863
+ <?php _e( 'Leave this empty to disable the secure reset function.<br/>
864
+ You use this code by adding any of the following to any URL on your site.', WPRSS_TEXT_DOMAIN ) ?>
865
+ <ol>
866
+ <li>"?wprss_action=reset&wprss_security_code=&lt;your_code&gt;" - <b><?php _e( 'Resets your WP RSS Aggregator settings', WPRSS_TEXT_DOMAIN ) ?></b></li>
867
+ <li>"?wprss_action=deactivate&wprss_security_code=&lt;your_code&gt;" - <b><?php _e( 'Deactivates WP RSS Aggregator', WPRSS_TEXT_DOMAIN ) ?></b></li>
868
+ <li>"?wprss_action=reset_and_deactivate&wprss_security_code=&lt;your_code&gt;" - <b><?php _e( 'Does both of the above', WPRSS_TEXT_DOMAIN ) ?></b></li>
869
+ </ol>
870
+ </p>
871
+ <p class="description">
872
+ <?php _e( 'Use the above actions only when absolutely necessary, or when instructed to by support staff.', WPRSS_TEXT_DOMAIN ) ?>
873
+ </p>
874
+ <?php
875
+ }
876
+
877
+
878
+ /**
879
+ * Tracking checkbox
880
+ * @since 3.6
881
+ */
882
+ function wprss_tracking_callback( $field ) {
883
+ $tracking = wprss_get_general_setting( 'tracking' );
884
+ ?>
885
+ <input type="checkbox" id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[tracking]" value="1" <?php echo checked( 1, $tracking, false ) ?> />
886
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] ) ?>
887
+ <label class="description" for="<?php echo $field['field_id'] ?>">
888
+ <?php _e( 'Please help us improve WP RSS Aggregator by allowing us to gather anonymous usage statistics. No sensitive data is collected.', WPRSS_TEXT_DOMAIN ) ?>
889
+ </label>
890
+ <?php
891
+ }
892
+
893
+ /**
894
+ * Time ago format checkbox
895
+ * @since 4.2
896
+ */
897
+ function wprss_setting_time_ago_format_enable_callback( $field ) {
898
+ $time_ago_format = wprss_get_general_setting( 'time_ago_format_enable' );
899
+ ?>
900
+ <input type="checkbox" id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[time_ago_format_enable]" value="1" <?php echo checked( 1, $time_ago_format, false ) ?> />
901
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
902
+ }
903
+
904
+
905
+ /**
906
+ * Pretty-prints the difference in two times.
907
+ *
908
+ * @since 3.0
909
+ * @param time $older_date
910
+ * @param time $newer_date
911
+ * @return string The pretty time_since value
912
+ * @link http://wordpress.org/extend/plugins/wp-crontrol/
913
+ */
914
+ function wprss_time_since( $older_date, $newer_date ) {
915
+ return wprss_interval( $newer_date - $older_date );
916
+ }
917
+
918
+ /**
919
+ * Calculates difference between times
920
+ *
921
+ * Taken from the WP-Crontrol plugin
922
+ * @link http://wordpress.org/extend/plugins/wp-crontrol/
923
+ * @since 3.0
924
+ *
925
+ */
926
+ function wprss_interval( $since ) {
927
+ if ( $since === wprss_get_default_feed_source_update_interval() ) {
928
+ return __( 'Default', WPRSS_TEXT_DOMAIN );
929
+ }
930
+ // array of time period chunks
931
+ $chunks = array(
932
+ array(60 * 60 * 24 * 365 , _n_noop('%s year', '%s years', 'crontrol')),
933
+ array(60 * 60 * 24 * 30 , _n_noop('%s month', '%s months', 'crontrol')),
934
+ array(60 * 60 * 24 * 7, _n_noop('%s week', '%s weeks', 'crontrol')),
935
+ array(60 * 60 * 24 , _n_noop('%s day', '%s days', 'crontrol')),
936
+ array(60 * 60 , _n_noop('%s hour', '%s hours', 'crontrol')),
937
+ array(60 , _n_noop('%s minute', '%s minutes', 'crontrol')),
938
+ array( 1 , _n_noop('%s second', '%s seconds', 'crontrol')),
939
+ );
940
+
941
+
942
+ if( $since <= 0 ) {
943
+ return __( 'now', WPRSS_TEXT_DOMAIN );
944
+ }
945
+
946
+ // we only want to output two chunks of time here, eg:
947
+ // x years, xx months
948
+ // x days, xx hours
949
+ // so there's only two bits of calculation below:
950
+
951
+ // step one: the first chunk
952
+ for ($i = 0, $j = count($chunks); $i < $j; $i++)
953
+ {
954
+ $seconds = $chunks[$i][0];
955
+ $name = $chunks[$i][1];
956
+
957
+ // finding the biggest chunk (if the chunk fits, break)
958
+ if (($count = floor($since / $seconds)) != 0)
959
+ {
960
+ break;
961
+ }
962
+ }
963
+
964
+ // set output var
965
+ $output = sprintf(_n($name[0], $name[1], $count, WPRSS_TEXT_DOMAIN), $count);
966
+
967
+ // step two: the second chunk
968
+ if ($i + 1 < $j)
969
+ {
970
+ $seconds2 = $chunks[$i + 1][0];
971
+ $name2 = $chunks[$i + 1][1];
972
+
973
+ if (($count2 = floor(($since - ($seconds * $count)) / $seconds2)) != 0)
974
+ {
975
+ // add to output var
976
+ $output .= ' '.sprintf(_n($name2[0], $name2[1], $count2, WPRSS_TEXT_DOMAIN), $count2);
977
+ }
978
+ }
979
+
980
+ return $output;
981
+ }
982
+
983
+
984
+ /**
985
+ * Validate inputs from the general settings page
986
+ * @since 3.0
987
+ */
988
+ function wprss_settings_general_validate( $input ) {
989
+ $options = get_option( 'wprss_settings_general' );
990
+ $current_cron_interval = $options['cron_interval'];
991
+
992
+ // Create our array for storing the validated options
993
+ $output = array();
994
+
995
+ // Loop through each of the incoming options
996
+ foreach( $input as $key => $value ) {
997
+
998
+ // Check to see if the current option has a value. If so, process it.
999
+ if( isset( $input[ $key ] ) ) {
1000
+
1001
+ // Strip all HTML and PHP tags and properly handle quoted strings
1002
+ $output[ $key ] = strip_tags( stripslashes( $input[ $key ] ) );
1003
+
1004
+ } // end if
1005
+
1006
+ } // end foreach
1007
+
1008
+ // If limit_feed_items_age_unit is not set or it set to zero, set it to empty
1009
+ if ( ! isset( $input['limit_feed_items_age'] ) || strval( $input['limit_feed_items_age'] ) == '0' ) {
1010
+ $output['limit_feed_items_age'] = '';
1011
+ }
1012
+
1013
+ if ( ! isset( $input['title_link'] ) || $input['title_link'] != '1' )
1014
+ $output['title_link'] = 0;
1015
+ else
1016
+ $output['title_link'] = 1;
1017
+
1018
+ if ( ! isset( $input['source_enable'] ) || $input['source_enable'] != '1' )
1019
+ $output['source_enable'] = 0;
1020
+ else
1021
+ $output['source_enable'] = 1;
1022
+
1023
+ if ( ! isset( $input['date_enable'] ) || $input['date_enable'] != '1' )
1024
+ $output['date_enable'] = 0;
1025
+ else
1026
+ $output['date_enable'] = 1;
1027
+
1028
+ if ( ! isset( $input['styles_disable'] ) || $input['styles_disable'] != '1' )
1029
+ $output['styles_disable'] = 0;
1030
+ else
1031
+ $output['styles_disable'] = 1;
1032
+
1033
+ if ( ! isset( $input['video_link'] ) || strtolower( $input['video_link'] ) !== 'true' )
1034
+ $output['video_link'] = 'false';
1035
+ else
1036
+ $output['video_link'] = 'true';
1037
+
1038
+ if ( $input['cron_interval'] != $current_cron_interval ) {
1039
+ wp_clear_scheduled_hook( 'wprss_fetch_all_feeds_hook' );
1040
+ wp_schedule_event( time(), $input['cron_interval'], 'wprss_fetch_all_feeds_hook' );
1041
+ }
1042
+
1043
+ if ( ! isset( $input['unique_titles'] ) || $input['unique_titles'] !== '1' )
1044
+ $output['unique_titles'] = 0;
1045
+ else
1046
+ $output['unique_titles'] = 1;
1047
+
1048
+
1049
+ // Return the array processing any additional functions filtered by this action
1050
+ return apply_filters( 'wprss_settings_general_validate', $output, $input );
1051
+ }
1052
+
1053
+
1054
+ /**
1055
+ * Validates the licenses settings
1056
+ *
1057
+ * @since 3.8
1058
+ */
1059
+ function wprss_settings_license_keys_validate( $input ) {
1060
+ // Get the current licenses option
1061
+ $licenses = get_option( 'wprss_settings_license_keys' );
1062
+ // If no licenses have been defined yet, create an empty array
1063
+ if ( !is_array( $licenses ) ) {
1064
+ $licenses = array();
1065
+ }
1066
+ // For each entry in the received input
1067
+ foreach ( $input as $addon => $license_code ) {
1068
+ $addon_code = explode( '_', $addon );
1069
+ $addon_code = isset( $addon_code[0] ) ? $addon_code[0] : null;
1070
+ // Only save if the entry does not exist OR the code is different
1071
+ if ( array_key_exists( $addon, $licenses ) && $license_code === $licenses[ $addon ] )
1072
+ continue;
1073
+
1074
+ $is_valid = apply_filters( 'wprss_settings_license_key_is_valid', true, $license_code );
1075
+ if( $addon_code )
1076
+ $is_valid = apply_filters( "wprss_settings_license_key_{$addon_code}_is_valid", $is_valid, $license_code );
1077
+ if( !$is_valid ) continue;
1078
+
1079
+ // Save it to the licenses option
1080
+ $licenses[ $addon ] = $license_code;
1081
+ }
1082
+ wprss_check_license_statuses();
1083
+ // Return the new licenses
1084
+ return $licenses;
1085
+ }
1086
+
1087
+
1088
+
1089
+ add_action( 'wprss_check_license_statuses', 'wprss_check_license_statuses' );
1090
+ /**
1091
+ * Checks the license statuses
1092
+ *
1093
+ * @since 3.8.1
1094
+ */
1095
+ function wprss_check_license_statuses() {
1096
+ $license_statuses = get_option( 'wprss_settings_license_statuses', array() );
1097
+
1098
+ if ( count( $license_statuses ) === 0 ) return;
1099
+
1100
+ $found_inactive = FALSE;
1101
+ foreach ( $license_statuses as $addon => $status ) {
1102
+ if ( $status !== 'active' ) {
1103
+ $found_inactive = TRUE;
1104
+ break;
1105
+ }
1106
+ }
1107
+
1108
+ if ( $found_inactive ) {
1109
+ set_transient( 'wprss_notify_inactive_licenses', 1, 0 );
1110
+ }
1111
+ }
1112
+
1113
+
1114
+
1115
+ /**
1116
+ * Validates the wprss_secure_reset_code option
1117
+ *
1118
+ * @since 3.7.1
1119
+ */
1120
+ function wprss_secure_reset_code_validate( $input ) {
1121
+ return $input;
1122
+ }
1123
+
1124
+
1125
+
1126
+ /**
1127
+ * Validates the presstrends setting
1128
+ *
1129
+ * @since 3.6
1130
+ */
1131
+ function wprss_tracking_validate ( $input ) {
1132
+ $output = $input;
1133
+ if ( ! isset( $input['wprss_tracking'] ) ) {
1134
+ $output['wprss_tracking'] = 0;
1135
+ }
1136
+ return $output;
1137
+ }
1138
+
1139
+
1140
+
1141
+ /**
1142
+ * Returns the units used for the limit by age option.
1143
+ *
1144
+ * @since 3.8
1145
+ */
1146
+ function wprss_age_limit_units() {
1147
+ return apply_filters(
1148
+ 'wprss_age_limit_units',
1149
+ array(
1150
+ 'days',
1151
+ 'weeks',
1152
+ 'months',
1153
+ 'years'
1154
+ )
1155
+ );
1156
  }
includes/admin-statistics.php CHANGED
@@ -1,152 +1,152 @@
1
- <?php
2
-
3
- if ( !defined( 'WPRSS_TRACKING_SERVER_URL' ) )
4
- define( 'WPRSS_TRACKING_SERVER_URL', 'http://www.wprssaggregator.com/', TRUE );
5
-
6
- if ( !defined( 'WPRSS_TRACKING_INTEVAL' ) )
7
- define( 'WPRSS_TRACKING_INTEVAL', 'daily', TRUE );
8
-
9
-
10
- add_action( 'admin_init', 'wprss_send_tracking_data' );
11
- function wprss_send_tracking_data() {
12
-
13
- // Check the tracking option - if turned off, exit out of function
14
- $tracking_option = wprss_get_general_setting('tracking');
15
- if ( $tracking_option == 0 || $tracking_option == FALSE ) return;
16
-
17
- // Get the tracking transient.
18
- $transient = get_transient( 'wprss_tracking_transient' );
19
- // If the transient did not expire, exit out of function
20
- if ( $transient !== FALSE && !isset( $_GET['wprss_send_report'] ) ) return;
21
- // If the GET parameter is set, show an admin notice
22
- if ( isset( $_GET['wprss_send_report'] ) ) {
23
- add_action( 'admin_notices', 'wprss_tracking_notice' );
24
- }
25
-
26
- // Check if running on localhost
27
- $site_url = site_url();
28
- $running_on_local = preg_match_all( "/(localhost|127\.0\.0\.1)/", $site_url, $matches ) > 0;
29
- if( $running_on_local ) {
30
- return;
31
- }
32
-
33
- // Get data about the plugin
34
- $plugin_data = get_plugin_data( WPRSS_FILE_CONSTANT );
35
-
36
- // Get the theme name
37
- if ( function_exists( 'wp_get_theme' ) ) {
38
- $theme_data = wp_get_theme();
39
- $theme_name = $theme_data->Name;
40
- } else {
41
- $theme_data = get_theme_data( get_stylesheet_directory() . '/style.css' );
42
- $theme_name = $theme_data['Name'];
43
- }
44
-
45
- // Get plugins
46
- $plugins = get_plugins();
47
- $active_plugins_option = get_option( 'active_plugins', array() );
48
- // Prepare plugin arrays
49
- $active_plugins = array();
50
- $inactive_plugins = array();
51
- // Loop through plugins
52
- foreach ( $plugins as $plugins_path => $plugin_info ) {
53
- // If plugin found in active plugins list, then add to the active_plugins array
54
- if ( in_array( $plugins_path, $active_plugins_option ) ) {
55
- $add_to = &$active_plugins;
56
- }
57
- // Otherwise add to inactive_plugins array
58
- else {
59
- $add_to = &$inactive_plugins;
60
- }
61
- // Add the plugin info to the chosen array
62
- $add_to[] = $plugin_info['Name'] . ' v' . $plugin_info['Version'];
63
- }
64
-
65
- // If multisite
66
- if ( is_multisite() ) {
67
- // Get network plugins
68
- $network_plugins = wp_get_active_network_plugins();
69
- $network_active_plugins_option = get_site_option( 'active_sitewide_plugins', array() );
70
- // Prepare plugin array
71
- $network_active_plugins = array();
72
- // Loop through plugins
73
- foreach ( $network_plugins as $plugin_path ) {
74
- // Get plugin basename
75
- $plugin_base = plugin_basename( $plugin_path );
76
- // If the plugin basename is found in the active network plugin list
77
- if ( array_key_exists( $plugin_base, $network_active_plugins_option ) ) {
78
- // Get the plugin info and add it to the plugin list
79
- $plugin_info = get_plugin_data( $plugin_path );
80
- $network_active_plugins[] = $plugin_info['Name'] . ' v' . $plugin_info['Version'];
81
- }
82
- }
83
- } else {
84
- // Otherwise, indicate that the site is not a multisite installation
85
- $network_active_plugins = 'Not multisite';
86
- }
87
-
88
-
89
- // Detect add-ons
90
- $addons = array();
91
- if ( defined( 'WPRSS_C_VERSION' ) ) {
92
- $addons[] = 'Categories';
93
- }
94
- if ( defined( 'WPRSS_ET_VERSION' ) ) {
95
- $addons[] = 'Excerpts & Thumbnails';
96
- }
97
- if ( defined( 'WPRSS_KF_VERSION' ) ) {
98
- $addons[] = 'Keyword Filtering';
99
- }
100
- if ( defined( 'WPRSS_FTP_VERSION' ) ) {
101
- $addons[] = 'Feed to Post';
102
- }
103
-
104
- // Compile the data
105
- $data = array(
106
- 'Site URL' => base64_encode( $site_url ),
107
- 'Plugin Version' => $plugin_data['Version'],
108
- 'Active Add-ons' => $addons,
109
- 'Theme Name' => $theme_name,
110
- 'Site Name' => str_replace( ' ', '', get_bloginfo( 'name' ) ),
111
- 'Plugin Count' => count( get_option( 'active_plugins' ) ),
112
- 'Active Plugins' => $active_plugins,
113
- 'Network Active Plugins' => $network_active_plugins,
114
- 'Inactive Plugins' => $inactive_plugins,
115
- 'WordPress Version' => get_bloginfo( 'version' ),
116
- );
117
-
118
- // Send the data
119
- wp_remote_post(
120
- WPRSS_TRACKING_SERVER_URL,
121
- array(
122
- 'method' => 'POST',
123
- 'timeout' => 45,
124
- 'redirection' => 5,
125
- 'httpversion' => '1.0',
126
- 'blocking' => true,
127
- 'headers' => array(),
128
- 'body' => array(
129
- 'wprss_tracking_data' => $data,
130
- ),
131
- 'cookies' => array()
132
- )
133
- );
134
-
135
- // Set a transient that expires in 1 week. When it expires, this function will run again
136
- // Expiration: 60secs * 60mins * 24hrs * 7days = 1 week
137
- set_transient( 'wprss_tracking_transient', '-', 60 * 60 * 24 * 7 );
138
- }
139
-
140
-
141
- /**
142
- * Shows a notice that notifies the user that the data report has been sent.
143
- *
144
- * @since 1.0
145
- */
146
- function wprss_tracking_notice() {
147
- ?>
148
- <div class="updated">
149
- <?php echo wpautop( __( '<b>WP RSS Aggregator:</b> Data report sent!', WPRSS_TEXT_DOMAIN ) ) ?>
150
- </div>
151
- <?php
152
  }
1
+ <?php
2
+
3
+ if ( !defined( 'WPRSS_TRACKING_SERVER_URL' ) )
4
+ define( 'WPRSS_TRACKING_SERVER_URL', 'http://www.wprssaggregator.com/', TRUE );
5
+
6
+ if ( !defined( 'WPRSS_TRACKING_INTEVAL' ) )
7
+ define( 'WPRSS_TRACKING_INTEVAL', 'daily', TRUE );
8
+
9
+
10
+ // add_action( 'admin_init', 'wprss_send_tracking_data' );
11
+ function wprss_send_tracking_data() {
12
+
13
+ // Check the tracking option - if turned off, exit out of function
14
+ $tracking_option = wprss_get_general_setting('tracking');
15
+ if ( $tracking_option == 0 || $tracking_option == FALSE ) return;
16
+
17
+ // Get the tracking transient.
18
+ $transient = get_transient( 'wprss_tracking_transient' );
19
+ // If the transient did not expire, exit out of function
20
+ if ( $transient !== FALSE && !isset( $_GET['wprss_send_report'] ) ) return;
21
+ // If the GET parameter is set, show an admin notice
22
+ if ( isset( $_GET['wprss_send_report'] ) ) {
23
+ add_action( 'admin_notices', 'wprss_tracking_notice' );
24
+ }
25
+
26
+ // Check if running on localhost
27
+ $site_url = site_url();
28
+ $running_on_local = preg_match_all( "/(localhost|127\.0\.0\.1)/", $site_url, $matches ) > 0;
29
+ if( $running_on_local ) {
30
+ return;
31
+ }
32
+
33
+ // Get data about the plugin
34
+ $plugin_data = get_plugin_data( WPRSS_FILE_CONSTANT );
35
+
36
+ // Get the theme name
37
+ if ( function_exists( 'wp_get_theme' ) ) {
38
+ $theme_data = wp_get_theme();
39
+ $theme_name = $theme_data->Name;
40
+ } else {
41
+ $theme_data = get_theme_data( get_stylesheet_directory() . '/style.css' );
42
+ $theme_name = $theme_data['Name'];
43
+ }
44
+
45
+ // Get plugins
46
+ $plugins = get_plugins();
47
+ $active_plugins_option = get_option( 'active_plugins', array() );
48
+ // Prepare plugin arrays
49
+ $active_plugins = array();
50
+ $inactive_plugins = array();
51
+ // Loop through plugins
52
+ foreach ( $plugins as $plugins_path => $plugin_info ) {
53
+ // If plugin found in active plugins list, then add to the active_plugins array
54
+ if ( in_array( $plugins_path, $active_plugins_option ) ) {
55
+ $add_to = &$active_plugins;
56
+ }
57
+ // Otherwise add to inactive_plugins array
58
+ else {
59
+ $add_to = &$inactive_plugins;
60
+ }
61
+ // Add the plugin info to the chosen array
62
+ $add_to[] = $plugin_info['Name'] . ' v' . $plugin_info['Version'];
63
+ }
64
+
65
+ // If multisite
66
+ if ( is_multisite() ) {
67
+ // Get network plugins
68
+ $network_plugins = wp_get_active_network_plugins();
69
+ $network_active_plugins_option = get_site_option( 'active_sitewide_plugins', array() );
70
+ // Prepare plugin array
71
+ $network_active_plugins = array();
72
+ // Loop through plugins
73
+ foreach ( $network_plugins as $plugin_path ) {
74
+ // Get plugin basename
75
+ $plugin_base = plugin_basename( $plugin_path );
76
+ // If the plugin basename is found in the active network plugin list
77
+ if ( array_key_exists( $plugin_base, $network_active_plugins_option ) ) {
78
+ // Get the plugin info and add it to the plugin list
79
+ $plugin_info = get_plugin_data( $plugin_path );
80
+ $network_active_plugins[] = $plugin_info['Name'] . ' v' . $plugin_info['Version'];
81
+ }
82
+ }
83
+ } else {
84
+ // Otherwise, indicate that the site is not a multisite installation
85
+ $network_active_plugins = 'Not multisite';
86
+ }
87
+
88
+
89
+ // Detect add-ons
90
+ $addons = array();
91
+ if ( defined( 'WPRSS_C_VERSION' ) ) {
92
+ $addons[] = 'Categories';
93
+ }
94
+ if ( defined( 'WPRSS_ET_VERSION' ) ) {
95
+ $addons[] = 'Excerpts & Thumbnails';
96
+ }
97
+ if ( defined( 'WPRSS_KF_VERSION' ) ) {
98
+ $addons[] = 'Keyword Filtering';
99
+ }
100
+ if ( defined( 'WPRSS_FTP_VERSION' ) ) {
101
+ $addons[] = 'Feed to Post';
102
+ }
103
+
104
+ // Compile the data
105
+ $data = array(
106
+ 'Site URL' => base64_encode( $site_url ),
107
+ 'Plugin Version' => $plugin_data['Version'],
108
+ 'Active Add-ons' => $addons,
109
+ 'Theme Name' => $theme_name,
110
+ 'Site Name' => str_replace( ' ', '', get_bloginfo( 'name' ) ),
111
+ 'Plugin Count' => count( get_option( 'active_plugins' ) ),
112
+ 'Active Plugins' => $active_plugins,
113
+ 'Network Active Plugins' => $network_active_plugins,
114
+ 'Inactive Plugins' => $inactive_plugins,
115
+ 'WordPress Version' => get_bloginfo( 'version' ),
116
+ );
117
+
118
+ // Send the data
119
+ wp_remote_post(
120
+ WPRSS_TRACKING_SERVER_URL,
121
+ array(
122
+ 'method' => 'POST',
123
+ 'timeout' => 45,
124
+ 'redirection' => 5,
125
+ 'httpversion' => '1.0',
126
+ 'blocking' => true,
127
+ 'headers' => array(),
128
+ 'body' => array(
129
+ 'wprss_tracking_data' => $data,
130
+ ),
131
+ 'cookies' => array()
132
+ )
133
+ );
134
+
135
+ // Set a transient that expires in 1 week. When it expires, this function will run again
136
+ // Expiration: 60secs * 60mins * 24hrs * 7days = 1 week
137
+ set_transient( 'wprss_tracking_transient', '-', 60 * 60 * 24 * 7 );
138
+ }
139
+
140
+
141
+ /**
142
+ * Shows a notice that notifies the user that the data report has been sent.
143
+ *
144
+ * @since 1.0
145
+ */
146
+ function wprss_tracking_notice() {
147
+ ?>
148
+ <div class="updated">
149
+ <?php echo wpautop( __( '<b>WP RSS Aggregator:</b> Data report sent!', WPRSS_TEXT_DOMAIN ) ) ?>
150
+ </div>
151
+ <?php
152
  }
includes/admin-welcome.php CHANGED
@@ -1,168 +1,168 @@
1
- <?php
2
-
3
- /**
4
- * @todo Localize
5
- */
6
-
7
- // Exit if the page is accessed directly
8
- if ( ! defined( 'ABSPATH' ) ) exit;
9
-
10
- // The readme lib
11
- require_once( WPRSS_INC . '/readme.php' );
12
-
13
-
14
- // The tabs to be shown
15
- $tabs = array(
16
- /* 'cat' => 'Categories',
17
- 'et' => 'Excerpts &amp; Thumbnails',
18
- 'kf' => 'Keyword Filtering'*/
19
- );
20
-
21
- // Determine the tab currently being shown
22
- $tab = null;
23
- if ( isset( $_GET['tab'] ) && !empty( $_GET['tab'] ) ) {
24
- $tab = $_GET['tab'];
25
- }
26
-
27
- $settings_url = admin_url( 'edit.php?post_type=wprss_feed&page=wprss-aggregator-settings');
28
-
29
- ?>
30
-
31
- <div class="wrap about-wrap">
32
- <h1><?php printf( __( 'Welcome to WP RSS Aggregator %1$s!', WPRSS_TEXT_DOMAIN ), WPRSS_VERSION ); ?></h1>
33
- <div class="wprss-about-text">
34
- <?php _e( 'Thank you for upgrading to the latest version!', WPRSS_TEXT_DOMAIN ) ?>
35
- </div>
36
- <!-- <div class="wprss-badge">Version</div>-->
37
-
38
- <!-- TAB WRAPPER -->
39
- <h2 class="nav-tab-wrapper">
40
- <!--<a class="nav-tab <?php if ( $tab === null ) echo 'nav-tab-active'; ?>"
41
- href="<?php echo esc_url( admin_url( add_query_arg( array( 'page' => 'wprss-welcome' ), 'index.php' ) ) ); ?>">
42
- <?php _e( "What's New?", WPRSS_TEXT_DOMAIN ) ?>
43
- </a>-->
44
-
45
- <!-- SHOW ALL TABS -->
46
- <?php foreach ($tabs as $slug => $title) : ?>
47
-
48
- <a class="nav-tab <?php if ( $tab === $slug ) echo 'nav-tab-active'; ?>"
49
- href="<?php echo esc_url( admin_url( add_query_arg( array( 'page' => 'wprss-welcome', 'tab' => $slug ), 'index.php' ) ) ); ?>">
50
- <?php _e( $title, WPRSS_TEXT_DOMAIN ) ?>
51
- </a>
52
-
53
- <?php endforeach; ?>
54
-
55
- </h2>
56
-
57
- <!-- TAB CONTENT -->
58
- <?php
59
- /* Show content depending on the current tab */
60
- switch( $tab ) {
61
-
62
- // Default tab. ( when tab = null )
63
- default: ?>
64
- <div class="changelog">
65
-
66
- <!--<h2 class="about-headline-callout"><?php _e( 'Bulk Adding Feed Sources', WPRSS_TEXT_DOMAIN ) ?></h2>
67
- <div class="about-overview">
68
- <img src="<?php echo WPRSS_IMG; ?>welcome-page/bulk-add.png" />
69
- <?php echo wpautop( sprintf( __('The new bulk adding option saves you time by allowing you to enter your feed names and URLs all at once.
70
- Simply type in or paste your feed sources, and with the press of a button, your feed sources will instantly be created!
71
- Try it now from the <a href="%1$s">Import &amp; Export</a> page.', WPRSS_TEXT_DOMAIN), 'edit.php?post_type=wprss_feed&page=wprss-import-export-settings' ) ) ?>
72
- </div>
73
-
74
- <h2 class="about-headline-callout"><?php _e( 'Feed Item Blacklist', WPRSS_TEXT_DOMAIN ) ?></h2>
75
- <div class="feature-section col three-col">
76
- <div class="col-1">
77
- <img src="<?php echo WPRSS_IMG;?>welcome-page/trash-feed-item.png" />
78
- <h4><?php _e( 'Trash undesired items', WPRSS_TEXT_DOMAIN ) ?></h4>
79
- <?php echo wpautop( sprintf( __('Did a feed import an item that you do not wish to keep? Up till now, <strong>WP RSS Aggregator</strong>'
80
- . 'only allowed you to trash the item and keep it in your trash.', WPRSS_TEXT_DOMAIN) ) ) ?>
81
- </div>
82
- <div class="col-2">
83
- <img src="<?php echo WPRSS_IMG;?>welcome-page/blacklist-feed-item.png" />
84
- <h4><?php _e( 'Blacklist Trashed Items', WPRSS_TEXT_DOMAIN ) ?></h4>
85
- <?php echo wpautop( sprintf( __('Permanently deleting the item will cause it to be re-imported. '
86
- . 'Using the new <strong>Delete Permanently &amp; Blacklist</strong> option, the feed item is deleted '
87
- . 'and added to the <strong>Blacklist</strong>.', WPRSS_TEXT_DOMAIN) ) ) ?>
88
- </div>
89
- <div class="col-3 last-feature">
90
- <img src="<?php echo WPRSS_IMG;?>welcome-page/blacklist.png" />
91
- <h4><?php _e( 'The Blacklist', WPRSS_TEXT_DOMAIN ) ?></h4>
92
- <?php echo wpautop( sprintf( __('This is your list of unwanted feed item links. Any item in this list will be ignored by '
93
- . "<strong>WP RSS Aggregator</strong> in the future, meaning it won't be imported from any of your feed sources."
94
- . 'and added to the <strong>Blacklist</strong>.', WPRSS_TEXT_DOMAIN) ) ) ?>
95
- </div>
96
- </div>-->
97
-
98
- <hr/>
99
-
100
- <h3><?php _e( 'Check out our add-ons:', WPRSS_TEXT_DOMAIN ) ?></h3>
101
-
102
- <ul>
103
- <li><strong><a href="http://www.wprssaggregator.com/extension/feed-post/" target="wprss_ftp"><?php _e( 'Feed to Post', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
104
- <li><strong><a href="http://www.wprssaggregator.com/extension/excerpts-thumbnails/" target="wprss_et"><?php _e( 'Excerpts &amp; Thumbnails', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
105
- <li><strong><a href="http://www.wprssaggregator.com/extension/categories/" target="wprss_cat"><?php _e( 'Categories', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
106
- <li><strong><a href="http://www.wprssaggregator.com/extension/keyword-filtering/" target="wprss_kf"><?php _e( 'Keyword Filtering', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
107
- <li><strong><a href="http://www.wprssaggregator.com/extension/full-text-rss-feeds/" target="wprss_kf"><?php _e( 'Full Text RSS Feeds', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
108
- <li><strong><a href="http://www.wprssaggregator.com/extension/wordai/" target="wprss_ai"><?php _e( 'WordAi', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
109
- </ul>
110
- </p>
111
- <?php echo wpautop( sprintf( __( 'More information about add-ons can be found on our website <a href="%1$s">%2$s</a>', WPRSS_TEXT_DOMAIN ), 'http://www.wprssaggregator.com', 'www.wprssaggregator.com' ) ) ?>
112
-
113
- <hr/>
114
-
115
- <?php $changelog = wprss_parse_changelog() ?>
116
- <?php if ( count( $changelog ) ): foreach( $changelog as $_version => $_changes_html ): ?>
117
- <h3><?php printf( __( 'Changelog for v%1$s', WPRSS_TEXT_DOMAIN ), $_version ) ?></h3>
118
- <div class="changelog-changeset" >
119
- <?php echo $_changes_html ?>
120
- </div>
121
- <?php break; endforeach; endif; ?>
122
-
123
- <?php echo wpautop( sprintf( __( 'Need functionality not already available in core or the add-ons? You can <a href="%1$s">suggest new features</a>!', WPRSS_TEXT_DOMAIN ), 'https://trello.com/b/UJJwpvZu/wp-rss-aggregator-public-roadmap' ) ) ?>
124
-
125
- </div>
126
-
127
- <?php
128
- break;
129
-
130
- // Excerpts and Thumbnails tab
131
- case 'et': ?>
132
-
133
- <p class="about-description">
134
- <?php _e( 'Fetch RSS feed excerpts to your blog and add thumbnails! Perfect for adding some life and color to your feeds.', WPRSS_TEXT_DOMAIN ) ?>
135
- </p>
136
-
137
- <?php
138
- break;
139
-
140
- // Categories Tab
141
- case 'cat': ?>
142
-
143
- <p class="about-description">
144
- <?php _e( 'Organize your feeds into custom categories. Filter feed items by category and make custom WordPress feeds for specific categories.', WPRSS_TEXT_DOMAIN ) ?>
145
- </p>
146
-
147
- <?php
148
- break;
149
-
150
- // Keyword Filtering tab
151
- case 'kf': ?>
152
-
153
- <p class="about-description">
154
- <?php _e( 'Import and store feeds that contain specific keywords in either the title or their content. Control what gets imported to your blog.', WPRSS_TEXT_DOMAIN ) ?>
155
- </p>
156
-
157
- <?php
158
- break;
159
- }
160
- ?>
161
-
162
- <hr/>
163
-
164
- <p><a href="<?php echo $settings_url; ?>"><?php _e( 'Go to WP RSS Aggregator settings', WPRSS_TEXT_DOMAIN ) ?></a></p>
165
-
166
- </div>
167
-
168
- <?php update_option( 'wprss_pwsv', WPRSS_VERSION ); // Update the previous welcome screen version ?>
1
+ <?php
2
+
3
+ /**
4
+ * @todo Localize
5
+ */
6
+
7
+ // Exit if the page is accessed directly
8
+ if ( ! defined( 'ABSPATH' ) ) exit;
9
+
10
+ // The readme lib
11
+ require_once( WPRSS_INC . '/readme.php' );
12
+
13
+
14
+ // The tabs to be shown
15
+ $tabs = array(
16
+ /* 'cat' => 'Categories',
17
+ 'et' => 'Excerpts &amp; Thumbnails',
18
+ 'kf' => 'Keyword Filtering'*/
19
+ );
20
+
21
+ // Determine the tab currently being shown
22
+ $tab = null;
23
+ if ( isset( $_GET['tab'] ) && !empty( $_GET['tab'] ) ) {
24
+ $tab = $_GET['tab'];
25
+ }
26
+
27
+ $settings_url = admin_url( 'edit.php?post_type=wprss_feed&page=wprss-aggregator-settings');
28
+
29
+ ?>
30
+
31
+ <div class="wrap about-wrap">
32
+ <h1><?php printf( __( 'Welcome to WP RSS Aggregator %1$s!', WPRSS_TEXT_DOMAIN ), WPRSS_VERSION ); ?></h1>
33
+ <div class="wprss-about-text">
34
+ <?php _e( 'Thank you for upgrading to the latest version!', WPRSS_TEXT_DOMAIN ) ?>
35
+ </div>
36
+ <!-- <div class="wprss-badge">Version</div>-->
37
+
38
+ <!-- TAB WRAPPER -->
39
+ <h2 class="nav-tab-wrapper">
40
+ <!--<a class="nav-tab <?php if ( $tab === null ) echo 'nav-tab-active'; ?>"
41
+ href="<?php echo esc_url( admin_url( add_query_arg( array( 'page' => 'wprss-welcome' ), 'index.php' ) ) ); ?>">
42
+ <?php _e( "What's New?", WPRSS_TEXT_DOMAIN ) ?>
43
+ </a>-->
44
+
45
+ <!-- SHOW ALL TABS -->
46
+ <?php foreach ($tabs as $slug => $title) : ?>
47
+
48
+ <a class="nav-tab <?php if ( $tab === $slug ) echo 'nav-tab-active'; ?>"
49
+ href="<?php echo esc_url( admin_url( add_query_arg( array( 'page' => 'wprss-welcome', 'tab' => $slug ), 'index.php' ) ) ); ?>">
50
+ <?php _e( $title, WPRSS_TEXT_DOMAIN ) ?>
51
+ </a>
52
+
53
+ <?php endforeach; ?>
54
+
55
+ </h2>
56
+
57
+ <!-- TAB CONTENT -->
58
+ <?php
59
+ /* Show content depending on the current tab */
60
+ switch( $tab ) {
61
+
62
+ // Default tab. ( when tab = null )
63
+ default: ?>
64
+ <div class="changelog">
65
+
66
+ <!--<h2 class="about-headline-callout"><?php _e( 'Bulk Adding Feed Sources', WPRSS_TEXT_DOMAIN ) ?></h2>
67
+ <div class="about-overview">
68
+ <img src="<?php echo WPRSS_IMG; ?>welcome-page/bulk-add.png" />
69
+ <?php echo wpautop( sprintf( __('The new bulk adding option saves you time by allowing you to enter your feed names and URLs all at once.
70
+ Simply type in or paste your feed sources, and with the press of a button, your feed sources will instantly be created!
71
+ Try it now from the <a href="%1$s">Import &amp; Export</a> page.', WPRSS_TEXT_DOMAIN), 'edit.php?post_type=wprss_feed&page=wprss-import-export-settings' ) ) ?>
72
+ </div>
73
+
74
+ <h2 class="about-headline-callout"><?php _e( 'Feed Item Blacklist', WPRSS_TEXT_DOMAIN ) ?></h2>
75
+ <div class="feature-section col three-col">
76
+ <div class="col-1">
77
+ <img src="<?php echo WPRSS_IMG;?>welcome-page/trash-feed-item.png" />
78
+ <h4><?php _e( 'Trash undesired items', WPRSS_TEXT_DOMAIN ) ?></h4>
79
+ <?php echo wpautop( sprintf( __('Did a feed import an item that you do not wish to keep? Up till now, <strong>WP RSS Aggregator</strong>'
80
+ . 'only allowed you to trash the item and keep it in your trash.', WPRSS_TEXT_DOMAIN) ) ) ?>
81
+ </div>
82
+ <div class="col-2">
83
+ <img src="<?php echo WPRSS_IMG;?>welcome-page/blacklist-feed-item.png" />
84
+ <h4><?php _e( 'Blacklist Trashed Items', WPRSS_TEXT_DOMAIN ) ?></h4>
85
+ <?php echo wpautop( sprintf( __('Permanently deleting the item will cause it to be re-imported. '
86
+ . 'Using the new <strong>Delete Permanently &amp; Blacklist</strong> option, the feed item is deleted '
87
+ . 'and added to the <strong>Blacklist</strong>.', WPRSS_TEXT_DOMAIN) ) ) ?>
88
+ </div>
89
+ <div class="col-3 last-feature">
90
+ <img src="<?php echo WPRSS_IMG;?>welcome-page/blacklist.png" />
91
+ <h4><?php _e( 'The Blacklist', WPRSS_TEXT_DOMAIN ) ?></h4>
92
+ <?php echo wpautop( sprintf( __('This is your list of unwanted feed item links. Any item in this list will be ignored by '
93
+ . "<strong>WP RSS Aggregator</strong> in the future, meaning it won't be imported from any of your feed sources."
94
+ . 'and added to the <strong>Blacklist</strong>.', WPRSS_TEXT_DOMAIN) ) ) ?>
95
+ </div>
96
+ </div>-->
97
+
98
+ <hr/>
99
+
100
+ <h3><?php _e( 'Check out our add-ons:', WPRSS_TEXT_DOMAIN ) ?></h3>
101
+
102
+ <ul>
103
+ <li><strong><a href="http://www.wprssaggregator.com/extension/feed-post/" target="wprss_ftp"><?php _e( 'Feed to Post', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
104
+ <li><strong><a href="http://www.wprssaggregator.com/extension/excerpts-thumbnails/" target="wprss_et"><?php _e( 'Excerpts &amp; Thumbnails', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
105
+ <li><strong><a href="http://www.wprssaggregator.com/extension/categories/" target="wprss_cat"><?php _e( 'Categories', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
106
+ <li><strong><a href="http://www.wprssaggregator.com/extension/keyword-filtering/" target="wprss_kf"><?php _e( 'Keyword Filtering', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
107
+ <li><strong><a href="http://www.wprssaggregator.com/extension/full-text-rss-feeds/" target="wprss_kf"><?php _e( 'Full Text RSS Feeds', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
108
+ <li><strong><a href="http://www.wprssaggregator.com/extension/wordai/" target="wprss_ai"><?php _e( 'WordAi', WPRSS_TEXT_DOMAIN ); ?></a></strong></li>
109
+ </ul>
110
+ </p>
111
+ <?php echo wpautop( sprintf( __( 'More information about add-ons can be found on our website <a href="%1$s">%2$s</a>', WPRSS_TEXT_DOMAIN ), 'http://www.wprssaggregator.com', 'www.wprssaggregator.com' ) ) ?>
112
+
113
+ <hr/>
114
+
115
+ <?php $changelog = wprss_parse_changelog() ?>
116
+ <?php if ( count( $changelog ) ): foreach( $changelog as $_version => $_changes_html ): ?>
117
+ <h3><?php printf( __( 'Changelog for v%1$s', WPRSS_TEXT_DOMAIN ), $_version ) ?></h3>
118
+ <div class="changelog-changeset" >
119
+ <?php echo $_changes_html ?>
120
+ </div>
121
+ <?php break; endforeach; endif; ?>
122
+
123
+ <?php echo wpautop( sprintf( __( 'Need functionality not already available in core or the add-ons? You can <a href="%1$s">suggest new features</a>!', WPRSS_TEXT_DOMAIN ), 'https://trello.com/b/UJJwpvZu/wp-rss-aggregator-public-roadmap' ) ) ?>
124
+
125
+ </div>
126
+
127
+ <?php
128
+ break;
129
+
130
+ // Excerpts and Thumbnails tab
131
+ case 'et': ?>
132
+
133
+ <p class="about-description">
134
+ <?php _e( 'Fetch RSS feed excerpts to your blog and add thumbnails! Perfect for adding some life and color to your feeds.', WPRSS_TEXT_DOMAIN ) ?>
135
+ </p>
136
+
137
+ <?php
138
+ break;
139
+
140
+ // Categories Tab
141
+ case 'cat': ?>
142
+
143
+ <p class="about-description">
144
+ <?php _e( 'Organize your feeds into custom categories. Filter feed items by category and make custom WordPress feeds for specific categories.', WPRSS_TEXT_DOMAIN ) ?>
145
+ </p>
146
+
147
+ <?php
148
+ break;
149
+
150
+ // Keyword Filtering tab
151
+ case 'kf': ?>
152
+
153
+ <p class="about-description">
154
+ <?php _e( 'Import and store feeds that contain specific keywords in either the title or their content. Control what gets imported to your blog.', WPRSS_TEXT_DOMAIN ) ?>
155
+ </p>
156
+
157
+ <?php
158
+ break;
159
+ }
160
+ ?>
161
+
162
+ <hr/>
163
+
164
+ <p><a href="<?php echo $settings_url; ?>"><?php _e( 'Go to WP RSS Aggregator settings', WPRSS_TEXT_DOMAIN ) ?></a></p>
165
+
166
+ </div>
167
+
168
+ <?php update_option( 'wprss_pwsv', WPRSS_VERSION ); // Update the previous welcome screen version ?>
includes/admin.php CHANGED
@@ -1,144 +1,144 @@
1
- <?php
2
- /**
3
- * Plugin administration related functions
4
- *
5
- * @package WPRSSAggregator
6
- */
7
-
8
- add_action( 'admin_head', 'wprss_custom_post_type_icon' );
9
- /**
10
- * Custom Post Type Icon for Admin Menu & Post Screen
11
- * @since 2.0
12
- */
13
- function wprss_custom_post_type_icon() {
14
- ?>
15
- <style>
16
- /* Post Screen - 32px */
17
- .icon32-posts-wprss_feed {
18
- background: transparent url( <?php echo WPRSS_IMG . 'icon-adminpage32.png'; ?> ) no-repeat left top !important;
19
- }
20
- /* Post Screen - 32px */
21
- .icon32-posts-wprss_feed_item {
22
- background: transparent url( <?php echo WPRSS_IMG . 'icon-adminpage32.png'; ?> ) no-repeat left top !important;
23
- }
24
- </style>
25
- <?php }
26
-
27
-
28
- add_action( 'admin_menu', 'wprss_register_menu_pages' );
29
- /**
30
- * Register menu and submenus
31
- * @since 2.0
32
- */
33
-
34
- // Add the admin options pages as submenus to the Feed CPT
35
- function wprss_register_menu_pages() {
36
- global $submenu;
37
- // Uncomment line below to hide "Add New" link from menu
38
- // unset( $submenu['edit.php?post_type=wprss_feed'][10] );
39
- // create submenu items
40
- add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Export & Import Settings', WPRSS_TEXT_DOMAIN ), __( 'Import & Export', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings' ), 'wprss-import-export-settings', 'wprss_import_export_settings_page_display' );
41
- add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'WP RSS Aggregator Settings', WPRSS_TEXT_DOMAIN ), __( 'Settings', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings' ), 'wprss-aggregator-settings', 'wprss_settings_page_display' );
42
- add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Debugging', WPRSS_TEXT_DOMAIN ), __( 'Debugging', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings'), 'wprss-debugging', 'wprss_debugging_page_display' );
43
- add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Add-Ons', WPRSS_TEXT_DOMAIN ), __( 'Add-Ons', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings'), 'wprss-addons', 'wprss_addons_page_display' );
44
- add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Help & Support', WPRSS_TEXT_DOMAIN ), __( 'Help & Support', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings'), 'wprss-help', 'wprss_help_page_display' );
45
- }
46
-
47
- add_filter('admin_body_class', 'wprss_base_admin_body_class');
48
- /**
49
- * Set body class for admin screens
50
- * http://www.kevinleary.net/customizing-wordpress-admin-css-javascript/
51
- * @since 2.0
52
- */
53
- function wprss_base_admin_body_class( $classes )
54
- {
55
- // Current action
56
- if ( is_admin() && isset($_GET['action']) ) {
57
- $classes .= 'action-'.$_GET['action'];
58
- }
59
- // Current post ID
60
- if ( is_admin() && isset($_GET['post']) ) {
61
- $classes .= ' ';
62
- $classes .= 'post-'.$_GET['post'];
63
- }
64
- // New post type & listing page
65
- if ( isset($_GET['post_type']) ) $post_type = $_GET['post_type'];
66
- if ( isset($post_type) ) {
67
- $classes .= ' ';
68
- $classes .= 'post-type-'.$post_type;
69
- }
70
- // Editting a post type
71
- if ( isset( $_GET['post'] ) ) {
72
- $post_query = $_GET['post'];
73
- }
74
- if ( isset($post_query) ) {
75
- $current_post_edit = get_post($post_query);
76
- $current_post_type = $current_post_edit->post_type;
77
- if ( !empty($current_post_type) ) {
78
- $classes .= ' ';
79
- $classes .= 'post-type-'.$current_post_type;
80
- }
81
- }
82
- // Return the $classes array
83
- return $classes;
84
- }
85
-
86
-
87
- /**
88
- * Change title on wprss_feed post type screen
89
- *
90
- * @since 2.0
91
- * @return void
92
- */
93
- function wprss_change_title_text() {
94
- return __( 'Name this feed (e.g. WP Mayor)', WPRSS_TEXT_DOMAIN );
95
- }
96
-
97
-
98
- add_filter( 'plugin_action_links', 'wprss_plugin_action_links', 10, 2 );
99
- /**
100
- * Add Settings action link in plugin listing
101
- *
102
- * @since 3.0
103
- * @param array $action_links
104
- * @param string $plugin_file
105
- * @return array
106
- */
107
- function wprss_plugin_action_links( $action_links, $plugin_file ) {
108
- // check to make sure we are on the correct plugin
109
- if ( $plugin_file == 'wp-rss-aggregator/wp-rss-aggregator.php' ) {
110
- // the anchor tag and href to the URLs we want.
111
- $settings_link = '<a href="' . admin_url() . 'edit.php?post_type=wprss_feed&page=wprss-aggregator-settings">' . __( 'Settings', WPRSS_TEXT_DOMAIN ) . '</a>';
112
- $docs_link = '<a href="http://www.wprssaggregator.com/documentation/">' . __( 'Documentation', WPRSS_TEXT_DOMAIN ) . '</a>';
113
- // add the links to the beginning of the list
114
- array_unshift( $action_links, $settings_link, $docs_link );
115
- }
116
- return $action_links;
117
- }
118
-
119
-
120
-
121
- if ( is_main_site() ) {
122
- add_action( 'admin_notices', 'wprss_notify_inactive_licenses' );
123
- }
124
- /**
125
- * Shows a notification that tells the user that there are saved licenses that have not been activated.
126
- *
127
- * @since 3.8.1
128
- */
129
- function wprss_notify_inactive_licenses() {
130
- // Check if a transient to show the notice is set
131
- $transient = get_transient( 'wprss_notify_inactive_licenses' );
132
- // If it is not set, then do nothing and return
133
- if ( $transient === FALSE ) return;
134
- // If it is set, delete it
135
- delete_transient( 'wprss_notify_inactive_licenses' );
136
-
137
- // Show the notice ?>
138
- <div class="wprss-license-notice updated">
139
- <?php echo wpautop( sprintf( __('<b>WP RSS Aggregator</b>:'
140
- . ' There are saved licenses that have not yet been activated. Go to the'
141
- . ' <a href="%1$s">License page</a> to activate them.', WPRSS_TEXT_DOMAIN ), admin_url() . 'edit.php?post_type=wprss_feed&page=wprss-aggregator-settings&tab=licenses_settings' ) ) ?>
142
- </div>
143
- <?php
144
  }
1
+ <?php
2
+ /**
3
+ * Plugin administration related functions
4
+ *
5
+ * @package WPRSSAggregator
6
+ */
7
+
8
+ add_action( 'admin_head', 'wprss_custom_post_type_icon' );
9
+ /**
10
+ * Custom Post Type Icon for Admin Menu & Post Screen
11
+ * @since 2.0
12
+ */
13
+ function wprss_custom_post_type_icon() {
14
+ ?>
15
+ <style>
16
+ /* Post Screen - 32px */
17
+ .icon32-posts-wprss_feed {
18
+ background: transparent url( <?php echo WPRSS_IMG . 'icon-adminpage32.png'; ?> ) no-repeat left top !important;
19
+ }
20
+ /* Post Screen - 32px */
21
+ .icon32-posts-wprss_feed_item {
22
+ background: transparent url( <?php echo WPRSS_IMG . 'icon-adminpage32.png'; ?> ) no-repeat left top !important;
23
+ }
24
+ </style>
25
+ <?php }
26
+
27
+
28
+ add_action( 'admin_menu', 'wprss_register_menu_pages' );
29
+ /**
30
+ * Register menu and submenus
31
+ * @since 2.0
32
+ */
33
+
34
+ // Add the admin options pages as submenus to the Feed CPT
35
+ function wprss_register_menu_pages() {
36
+ global $submenu;
37
+ // Uncomment line below to hide "Add New" link from menu
38
+ // unset( $submenu['edit.php?post_type=wprss_feed'][10] );
39
+ // create submenu items
40
+ add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Export & Import Settings', WPRSS_TEXT_DOMAIN ), __( 'Import & Export', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings' ), 'wprss-import-export-settings', 'wprss_import_export_settings_page_display' );
41
+ add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'WP RSS Aggregator Settings', WPRSS_TEXT_DOMAIN ), __( 'Settings', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings' ), 'wprss-aggregator-settings', 'wprss_settings_page_display' );
42
+ add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Debugging', WPRSS_TEXT_DOMAIN ), __( 'Debugging', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings'), 'wprss-debugging', 'wprss_debugging_page_display' );
43
+ add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Add-Ons', WPRSS_TEXT_DOMAIN ), __( 'Add-Ons', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings'), 'wprss-addons', 'wprss_addons_page_display' );
44
+ add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Help & Support', WPRSS_TEXT_DOMAIN ), __( 'Help & Support', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings'), 'wprss-help', 'wprss_help_page_display' );
45
+ }
46
+
47
+ add_filter('admin_body_class', 'wprss_base_admin_body_class');
48
+ /**
49
+ * Set body class for admin screens
50
+ * http://www.kevinleary.net/customizing-wordpress-admin-css-javascript/
51
+ * @since 2.0
52
+ */
53
+ function wprss_base_admin_body_class( $classes )
54
+ {
55
+ // Current action
56
+ if ( is_admin() && isset($_GET['action']) ) {
57
+ $classes .= 'action-'.$_GET['action'];
58
+ }
59
+ // Current post ID
60
+ if ( is_admin() && isset($_GET['post']) ) {
61
+ $classes .= ' ';
62
+ $classes .= 'post-'.$_GET['post'];
63
+ }
64
+ // New post type & listing page
65
+ if ( isset($_GET['post_type']) ) $post_type = $_GET['post_type'];
66
+ if ( isset($post_type) ) {
67
+ $classes .= ' ';
68
+ $classes .= 'post-type-'.$post_type;
69
+ }
70
+ // Editting a post type
71
+ if ( isset( $_GET['post'] ) ) {
72
+ $post_query = $_GET['post'];
73
+ }
74
+ if ( isset($post_query) ) {
75
+ $current_post_edit = get_post($post_query);
76
+ $current_post_type = $current_post_edit->post_type;
77
+ if ( !empty($current_post_type) ) {
78
+ $classes .= ' ';
79
+ $classes .= 'post-type-'.$current_post_type;
80
+ }
81
+ }
82
+ // Return the $classes array
83
+ return $classes;
84
+ }
85
+
86
+
87
+ /**
88
+ * Change title on wprss_feed post type screen
89
+ *
90
+ * @since 2.0
91
+ * @return void
92
+ */
93
+ function wprss_change_title_text() {
94
+ return __( 'Name this feed (e.g. WP Mayor)', WPRSS_TEXT_DOMAIN );
95
+ }
96
+
97
+
98
+ add_filter( 'plugin_action_links', 'wprss_plugin_action_links', 10, 2 );
99
+ /**
100
+ * Add Settings action link in plugin listing
101
+ *
102
+ * @since 3.0
103
+ * @param array $action_links
104
+ * @param string $plugin_file
105
+ * @return array
106
+ */
107
+ function wprss_plugin_action_links( $action_links, $plugin_file ) {
108
+ // check to make sure we are on the correct plugin
109
+ if ( $plugin_file == 'wp-rss-aggregator/wp-rss-aggregator.php' ) {
110
+ // the anchor tag and href to the URLs we want.
111
+ $settings_link = '<a href="' . admin_url() . 'edit.php?post_type=wprss_feed&page=wprss-aggregator-settings">' . __( 'Settings', WPRSS_TEXT_DOMAIN ) . '</a>';
112
+ $docs_link = '<a href="http://www.wprssaggregator.com/documentation/">' . __( 'Documentation', WPRSS_TEXT_DOMAIN ) . '</a>';
113
+ // add the links to the beginning of the list
114
+ array_unshift( $action_links, $settings_link, $docs_link );
115
+ }
116
+ return $action_links;
117
+ }
118
+
119
+
120
+
121
+ if ( is_main_site() ) {
122
+ add_action( 'admin_notices', 'wprss_notify_inactive_licenses' );
123
+ }
124
+ /**
125
+ * Shows a notification that tells the user that there are saved licenses that have not been activated.
126
+ *
127
+ * @since 3.8.1
128
+ */
129
+ function wprss_notify_inactive_licenses() {
130
+ // Check if a transient to show the notice is set
131
+ $transient = get_transient( 'wprss_notify_inactive_licenses' );
132
+ // If it is not set, then do nothing and return
133
+ if ( $transient === FALSE ) return;
134
+ // If it is set, delete it
135
+ delete_transient( 'wprss_notify_inactive_licenses' );
136
+
137
+ // Show the notice ?>
138
+ <div class="wprss-license-notice updated">
139
+ <?php echo wpautop( sprintf( __('<b>WP RSS Aggregator</b>:'
140
+ . ' There are saved licenses that have not yet been activated. Go to the'
141
+ . ' <a href="%1$s">License page</a> to activate them.', WPRSS_TEXT_DOMAIN ), admin_url() . 'edit.php?post_type=wprss_feed&page=wprss-aggregator-settings&tab=licenses_settings' ) ) ?>
142
+ </div>
143
+ <?php
144
  }
includes/cpt-feeds.php CHANGED
@@ -1,101 +1,101 @@
1
- <?php
2
-
3
- add_action( 'wp_head', 'wprss_cpt_feeds' );
4
- /**
5
- * Adds Link tags to the head of the page, for CPTs' feeds.
6
- */
7
- function wprss_cpt_feeds() {
8
- // Get all post types
9
- $post_types = get_post_types(array(
10
- 'public' => true,
11
- '_builtin' => false
12
- ));
13
-
14
- // If current page is archive page for a particular post type
15
- if ( is_post_type_archive() ) {
16
- // Remove post type from the post types list
17
- unset( $post_types[ get_post_type() ] );
18
- }
19
-
20
- // Filter which post types to use
21
- // False: none
22
- // True: all
23
- // Array: particular post types
24
- // String: Single post type
25
- $post_type_feeds = apply_filters( 'wprss_cpt_feeds', FALSE );
26
- switch( gettype( $post_type_feeds ) ) {
27
- // If it's a boolean ...
28
- case 'boolean':
29
- // If it is FALSE, exit function. Do nothing. Simply.
30
- if ( $post_type_feeds === FALSE ) return;
31
- // Otherwise, if TRUE, no further action is needed.
32
- break;
33
- // If it's a string ...
34
- case 'string':
35
- // If the post type does not exist, stop
36
- if ( !isset( $post_types[ $post_type_feeds ] ) ) return;
37
- // Otherwise, only use this post type
38
- $single = $post_types[ $post_type_feeds ];
39
- $post_types = array( $single => $single );
40
- break;
41
- // If it's an array ...
42
- case 'array':
43
- $post_types = array_intersect($post_types, $post_type_feeds);
44
- break;
45
- // If any other type, stop.
46
- default: return;
47
- }
48
-
49
- // Get only the values of the post types
50
- $post_types = array_values( $post_types );
51
-
52
- // Get the site name and RSS feed URL, parsed as an array
53
- $siteName = get_bloginfo("name");
54
- $feedURL = parse_url( get_bloginfo( 'rss2_url' ) );
55
-
56
- // Foreach post type
57
- foreach ( $post_types as $i => $post_type ) {
58
- // Get its RSS feed URL
59
- $feed = get_post_type_archive_feed_link( $post_type );
60
-
61
- // If it doesnt have one, use the interal WP feed URL using the post_type query arg
62
- if ( $feed === '' || !is_string( $feed ) ) {
63
- // Start with the feed URL of the site
64
- $feed = $feedURL;
65
- // If there are no query args, set to an emprty string
66
- if ( !isset( $feed['query'] ) )
67
- $feed['query'] = '';
68
- // If the query is not empty, we need to add an ampersand
69
- if ( strlen( $feed['query'] ) > 0 )
70
- $feed['query'] .= '&';
71
- // Add the post_type query arg
72
- $feed['query'] .= "post_type=$post_type";
73
- // Unparse the URL array into a string
74
- $feed = unparse_url( $feed );
75
- }
76
-
77
- // Get the Post Type Pretty Name
78
- $obj = get_post_type_object( $post_type );
79
- $name = $obj->labels->name;
80
-
81
- // Print the <link> tag
82
- $feedname = sprintf( __( '%1$s &raquo; %2$s Feed', WPRSS_TEXT_DOMAIN ), $siteName, $name );
83
- printf( __( '<link rel="%1$s" type="%2$s" title="%3$s" href="%4$s" />'."\n", WPRSS_TEXT_DOMAIN ),"alternate","application/rss+xml", $feedname, $feed );
84
- }
85
- }
86
-
87
-
88
- if ( !function_exists('unparse_url') ) {
89
- function unparse_url( $parsed_url ) {
90
- $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
91
- $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
92
- $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
93
- $user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
94
- $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
95
- $pass = ($user || $pass) ? "$pass@" : '';
96
- $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
97
- $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
98
- $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
99
- return "$scheme$user$pass$host$port$path$query$fragment";
100
- }
101
  }
1
+ <?php
2
+
3
+ add_action( 'wp_head', 'wprss_cpt_feeds' );
4
+ /**
5
+ * Adds Link tags to the head of the page, for CPTs' feeds.
6
+ */
7
+ function wprss_cpt_feeds() {
8
+ // Get all post types
9
+ $post_types = get_post_types(array(
10
+ 'public' => true,
11
+ '_builtin' => false
12
+ ));
13
+
14
+ // If current page is archive page for a particular post type
15
+ if ( is_post_type_archive() ) {
16
+ // Remove post type from the post types list
17
+ unset( $post_types[ get_post_type() ] );
18
+ }
19
+
20
+ // Filter which post types to use
21
+ // False: none
22
+ // True: all
23
+ // Array: particular post types
24
+ // String: Single post type
25
+ $post_type_feeds = apply_filters( 'wprss_cpt_feeds', FALSE );
26
+ switch( gettype( $post_type_feeds ) ) {
27
+ // If it's a boolean ...
28
+ case 'boolean':
29
+ // If it is FALSE, exit function. Do nothing. Simply.
30
+ if ( $post_type_feeds === FALSE ) return;
31
+ // Otherwise, if TRUE, no further action is needed.
32
+ break;
33
+ // If it's a string ...
34
+ case 'string':
35
+ // If the post type does not exist, stop
36
+ if ( !isset( $post_types[ $post_type_feeds ] ) ) return;
37
+ // Otherwise, only use this post type
38
+ $single = $post_types[ $post_type_feeds ];
39
+ $post_types = array( $single => $single );
40
+ break;
41
+ // If it's an array ...
42
+ case 'array':
43
+ $post_types = array_intersect($post_types, $post_type_feeds);
44
+ break;
45
+ // If any other type, stop.
46
+ default: return;
47
+ }
48
+
49
+ // Get only the values of the post types
50
+ $post_types = array_values( $post_types );
51
+
52
+ // Get the site name and RSS feed URL, parsed as an array
53
+ $siteName = get_bloginfo("name");
54
+ $feedURL = parse_url( get_bloginfo( 'rss2_url' ) );
55
+
56
+ // Foreach post type
57
+ foreach ( $post_types as $i => $post_type ) {
58
+ // Get its RSS feed URL
59
+ $feed = get_post_type_archive_feed_link( $post_type );
60
+
61
+ // If it doesnt have one, use the interal WP feed URL using the post_type query arg
62
+ if ( $feed === '' || !is_string( $feed ) ) {
63
+ // Start with the feed URL of the site
64
+ $feed = $feedURL;
65
+ // If there are no query args, set to an emprty string
66
+ if ( !isset( $feed['query'] ) )
67
+ $feed['query'] = '';
68
+ // If the query is not empty, we need to add an ampersand
69
+ if ( strlen( $feed['query'] ) > 0 )
70
+ $feed['query'] .= '&';
71
+ // Add the post_type query arg
72
+ $feed['query'] .= "post_type=$post_type";
73
+ // Unparse the URL array into a string
74
+ $feed = unparse_url( $feed );
75
+ }
76
+
77
+ // Get the Post Type Pretty Name
78
+ $obj = get_post_type_object( $post_type );
79
+ $name = $obj->labels->name;
80
+
81
+ // Print the <link> tag
82
+ $feedname = sprintf( __( '%1$s &raquo; %2$s Feed', WPRSS_TEXT_DOMAIN ), $siteName, $name );
83
+ printf( __( '<link rel="%1$s" type="%2$s" title="%3$s" href="%4$s" />'."\n", WPRSS_TEXT_DOMAIN ),"alternate","application/rss+xml", $feedname, $feed );
84
+ }
85
+ }
86
+
87
+
88
+ if ( !function_exists('unparse_url') ) {
89
+ function unparse_url( $parsed_url ) {
90
+ $scheme = isset($parsed_url['scheme']) ? $parsed_url['scheme'] . '://' : '';
91
+ $host = isset($parsed_url['host']) ? $parsed_url['host'] : '';
92
+ $port = isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '';
93
+ $user = isset($parsed_url['user']) ? $parsed_url['user'] : '';
94
+ $pass = isset($parsed_url['pass']) ? ':' . $parsed_url['pass'] : '';
95
+ $pass = ($user || $pass) ? "$pass@" : '';
96
+ $path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
97
+ $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
98
+ $fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
99
+ return "$scheme$user$pass$host$port$path$query$fragment";
100
+ }
101
  }
includes/custom-feed.php CHANGED
@@ -1,168 +1,168 @@
1
- <?php
2
- /**
3
- * Function to create a custom feed with the latest imported feed items
4
- *
5
- * @package WP RSS Aggregator
6
- */
7
-
8
-
9
- add_action( 'init', 'wprss_addfeed_add_feed' );
10
- /**
11
- * Adds the custom feed, as specified by the user in the general settings.
12
- *
13
- * @since 3.3
14
- */
15
- function wprss_addfeed_add_feed() {
16
- $general_settings = get_option( 'wprss_settings_general', 'wprss' );
17
- if ( !empty( $general_settings ) && isset( $general_settings['custom_feed_url'] ) && !empty( $general_settings['custom_feed_url'] ) ) {
18
- $url = $general_settings['custom_feed_url'];
19
- }
20
- else {
21
- $url = $general_settings['custom_feed_url'] = 'wprss';
22
- update_option( 'wprss_settings_general', $general_settings );
23
- }
24
-
25
- // Add the feed
26
- add_feed( $url, 'wprss_addfeed_do_feed' );
27
-
28
- // Whether or not the feed is already registered or not
29
- $registered = FALSE;
30
-
31
- // Get all registered rewrite rules
32
- $rules = get_option( 'rewrite_rules' );
33
-
34
- // If no rules exist, then it is not registered
35
- if ( !is_array( $rules ) ) {
36
- $registered = FALSE;
37
- }
38
- // If there are exisiting rules
39
- else {
40
- // Get all the array keys that match the given pattern
41
- // The resulting array will only contain the second part of each matching key ( $matches[1] )
42
- $feeds = array_keys( $rules, 'index.php?&feed=$matches[1]' );
43
- // Check if the rewrite rule for the custom feed is already registered
44
- foreach( $feeds as $feed ) {
45
- if ( strpos( $feed, $url ) !== FALSE ) {
46
- $registered = TRUE;
47
- }
48
- }
49
- }
50
-
51
- // If not registered, flush the rewrite rules
52
- if ( ! $registered ) {
53
- flush_rewrite_rules();
54
- }
55
-
56
- }
57
-
58
-
59
- /**
60
- * Generate the feed
61
- *
62
- * @since 3.3
63
- */
64
- function wprss_addfeed_do_feed( $in ) {
65
-
66
- // Prepare the post query
67
- /*
68
- $wprss_custom_feed_query = apply_filters(
69
- 'wprss_custom_feed_query',
70
- array(
71
- 'post_type' => 'wprss_feed_item',
72
- 'post_status' => 'publish',
73
- 'cache_results' => false, // disable caching
74
- )
75
-
76
- );*/
77
- $wprss_custom_feed_query = wprss_get_feed_items_query(
78
- apply_filters(
79
- 'wprss_custom_feed_query',
80
- array(
81
- 'get-args' => TRUE, // Get the query args instead of the query object
82
- 'no-paged' => TRUE, // ignore pagination
83
- 'feed_limit' => 0, // ignore limit
84
- )
85
- )
86
- );
87
-
88
- // Suppress caching
89
- $wprss_custom_feed_query['cache_results'] = FALSE;
90
-
91
- // Get options
92
- $options = get_option( 'wprss_settings_general' );
93
- if ( $options !== FALSE ) {
94
- // If options exist, get the limit
95
- $limit = $options['custom_feed_limit'];
96
- if ( $limit !== FALSE ) {
97
- // if limit exists, set the query limit
98
- $wprss_custom_feed_query['posts_per_page'] = $limit;
99
- }
100
- }
101
-
102
- // Submit the query to get latest feed items
103
- query_posts( $wprss_custom_feed_query );
104
-
105
- $custom_feed_title = wprss_get_general_setting( 'custom_feed_title' );
106
-
107
- $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
108
- header( "$protocol 200 OK" );
109
- // Send content header and start ATOM output
110
- header('Content-Type: application/rss+xml');
111
- // Disabling caching
112
- header('Cache-Control: no-cache, no-store, must-revalidate'); // HTTP 1.1.
113
- header('Pragma: no-cache'); // HTTP 1.0.
114
- header('Expires: 0'); // Proxies.
115
- echo '<?xml version="1.0" encoding="' . get_option('blog_charset') . '"?>';
116
- ?>
117
-
118
- <rss version="2.0"
119
- xmlns:content="http://purl.org/rss/1.0/modules/content/"
120
- xmlns:wfw="http://wellformedweb.org/CommentAPI/"
121
- xmlns:dc="http://purl.org/dc/elements/1.1/"
122
- xmlns:atom="http://www.w3.org/2005/Atom"
123
- xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
124
- xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
125
- xmlns:media="http://search.yahoo.com/mrss/" >
126
- <channel>
127
- <title><?php echo $custom_feed_title; ?></title>
128
- <description></description>
129
- <link><?php echo get_site_url(); ?></link>
130
- <atom:link href="<?php echo $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] ?>" rel="self" type="application/rss+xml" />
131
- <?php // Start the Loop
132
- while ( have_posts() ) : the_post();
133
- $source = get_post_meta( get_the_ID(), 'wprss_feed_id', TRUE );
134
- $permalink = get_post_meta( get_the_ID(), 'wprss_item_permalink', true );
135
- $content = apply_filters( 'wprss_custom_feed_item_content', get_the_content() );
136
- ?>
137
- <item>
138
- <title><![CDATA[<?php the_title_rss(); ?>]]></title>
139
- <link><?php echo $permalink; ?></link>
140
- <guid isPermaLink="true"><?php echo $permalink; ?></guid>
141
- <pubDate><?php echo get_post_time( DATE_RSS ); ?></pubDate>
142
- <description><![CDATA[<?php echo $content; ?>]]></description>
143
- <content:encoded><![CDATA[<?php echo $content; ?>]]></content:encoded>
144
- <source url="<?php echo esc_attr(get_post_meta( $source, 'wprss_url', TRUE )); ?>"><?php echo get_the_title( $source ); ?></source>
145
- <?php do_action( 'wprss_custom_feed_entry', get_the_ID() ); ?>
146
- </item>
147
- <?php
148
- endwhile; // END OF LOOP
149
- ?>
150
- </channel>
151
- </rss>
152
- <?php
153
- }
154
-
155
-
156
- add_filter( 'post_limits', 'wprss_custom_feed_limits' );
157
- /**
158
- * Set a different limit to our custom feeds
159
- *
160
- * @since 3.3
161
- */
162
- function wprss_custom_feed_limits( $limit ) {
163
- if ( is_feed( ) ) {
164
- // return 'LIMIT 0, 3';
165
- return $limit;
166
- }
167
- return $limit;
168
  }
1
+ <?php
2
+ /**
3
+ * Function to create a custom feed with the latest imported feed items
4
+ *
5
+ * @package WP RSS Aggregator
6
+ */
7
+
8
+
9
+ add_action( 'init', 'wprss_addfeed_add_feed' );
10
+ /**
11
+ * Adds the custom feed, as specified by the user in the general settings.
12
+ *
13
+ * @since 3.3
14
+ */
15
+ function wprss_addfeed_add_feed() {
16
+ $general_settings = get_option( 'wprss_settings_general', 'wprss' );
17
+ if ( !empty( $general_settings ) && isset( $general_settings['custom_feed_url'] ) && !empty( $general_settings['custom_feed_url'] ) ) {
18
+ $url = $general_settings['custom_feed_url'];
19
+ }
20
+ else {
21
+ $url = $general_settings['custom_feed_url'] = 'wprss';
22
+ update_option( 'wprss_settings_general', $general_settings );
23
+ }
24
+
25
+ // Add the feed
26
+ add_feed( $url, 'wprss_addfeed_do_feed' );
27
+
28
+ // Whether or not the feed is already registered or not
29
+ $registered = FALSE;
30
+
31
+ // Get all registered rewrite rules
32
+ $rules = get_option( 'rewrite_rules' );
33
+
34
+ // If no rules exist, then it is not registered
35
+ if ( !is_array( $rules ) ) {
36
+ $registered = FALSE;
37
+ }
38
+ // If there are exisiting rules
39
+ else {
40
+ // Get all the array keys that match the given pattern
41
+ // The resulting array will only contain the second part of each matching key ( $matches[1] )
42
+ $feeds = array_keys( $rules, 'index.php?&feed=$matches[1]' );
43
+ // Check if the rewrite rule for the custom feed is already registered
44
+ foreach( $feeds as $feed ) {
45
+ if ( strpos( $feed, $url ) !== FALSE ) {
46
+ $registered = TRUE;
47
+ }
48
+ }
49
+ }
50
+
51
+ // If not registered, flush the rewrite rules
52
+ if ( ! $registered ) {
53
+ flush_rewrite_rules();
54
+ }
55
+
56
+ }
57
+
58
+
59
+ /**
60
+ * Generate the feed
61
+ *
62
+ * @since 3.3
63
+ */
64
+ function wprss_addfeed_do_feed( $in ) {
65
+
66
+ // Prepare the post query
67
+ /*
68
+ $wprss_custom_feed_query = apply_filters(
69
+ 'wprss_custom_feed_query',
70
+ array(
71
+ 'post_type' => 'wprss_feed_item',
72
+ 'post_status' => 'publish',
73
+ 'cache_results' => false, // disable caching
74
+ )
75
+
76
+ );*/
77
+ $wprss_custom_feed_query = wprss_get_feed_items_query(
78
+ apply_filters(
79
+ 'wprss_custom_feed_query',
80
+ array(
81
+ 'get-args' => TRUE, // Get the query args instead of the query object
82
+ 'no-paged' => TRUE, // ignore pagination
83
+ 'feed_limit' => 0, // ignore limit
84
+ )
85
+ )
86
+ );
87
+
88
+ // Suppress caching
89
+ $wprss_custom_feed_query['cache_results'] = FALSE;
90
+
91
+ // Get options
92
+ $options = get_option( 'wprss_settings_general' );
93
+ if ( $options !== FALSE ) {
94
+ // If options exist, get the limit
95
+ $limit = $options['custom_feed_limit'];
96
+ if ( $limit !== FALSE ) {
97
+ // if limit exists, set the query limit
98
+ $wprss_custom_feed_query['posts_per_page'] = $limit;
99
+ }
100
+ }
101
+
102
+ // Submit the query to get latest feed items
103
+ query_posts( $wprss_custom_feed_query );
104
+
105
+ $custom_feed_title = wprss_get_general_setting( 'custom_feed_title' );
106
+
107
+ $protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
108
+ header( "$protocol 200 OK" );
109
+ // Send content header and start ATOM output
110
+ header('Content-Type: application/rss+xml');
111
+ // Disabling caching
112
+ header('Cache-Control: no-cache, no-store, must-revalidate'); // HTTP 1.1.
113
+ header('Pragma: no-cache'); // HTTP 1.0.
114
+ header('Expires: 0'); // Proxies.
115
+ echo '<?xml version="1.0" encoding="' . get_option('blog_charset') . '"?>';
116
+ ?>
117
+
118
+ <rss version="2.0"
119
+ xmlns:content="http://purl.org/rss/1.0/modules/content/"
120
+ xmlns:wfw="http://wellformedweb.org/CommentAPI/"
121
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
122
+ xmlns:atom="http://www.w3.org/2005/Atom"
123
+ xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
124
+ xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
125
+ xmlns:media="http://search.yahoo.com/mrss/" >
126
+ <channel>
127
+ <title><?php echo $custom_feed_title; ?></title>
128
+ <description></description>
129
+ <link><?php echo get_site_url(); ?></link>
130
+ <atom:link href="<?php echo $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] ?>" rel="self" type="application/rss+xml" />
131
+ <?php // Start the Loop
132
+ while ( have_posts() ) : the_post();
133
+ $source = get_post_meta( get_the_ID(), 'wprss_feed_id', TRUE );
134
+ $permalink = get_post_meta( get_the_ID(), 'wprss_item_permalink', true );
135
+ $content = apply_filters( 'wprss_custom_feed_item_content', get_the_content() );
136
+ ?>
137
+ <item>
138
+ <title><![CDATA[<?php the_title_rss(); ?>]]></title>
139
+ <link><?php echo $permalink; ?></link>
140
+ <guid isPermaLink="true"><?php echo $permalink; ?></guid>
141
+ <pubDate><?php echo get_post_time( DATE_RSS ); ?></pubDate>
142
+ <description><![CDATA[<?php echo $content; ?>]]></description>
143
+ <content:encoded><![CDATA[<?php echo $content; ?>]]></content:encoded>
144
+ <source url="<?php echo esc_attr(get_post_meta( $source, 'wprss_url', TRUE )); ?>"><?php echo get_the_title( $source ); ?></source>
145
+ <?php do_action( 'wprss_custom_feed_entry', get_the_ID() ); ?>
146
+ </item>
147
+ <?php
148
+ endwhile; // END OF LOOP
149
+ ?>
150
+ </channel>
151
+ </rss>
152
+ <?php
153
+ }
154
+
155
+
156
+ add_filter( 'post_limits', 'wprss_custom_feed_limits' );
157
+ /**
158
+ * Set a different limit to our custom feeds
159
+ *
160
+ * @since 3.3
161
+ */
162
+ function wprss_custom_feed_limits( $limit ) {
163
+ if ( is_feed( ) ) {
164
+ // return 'LIMIT 0, 3';
165
+ return $limit;
166
+ }
167
+ return $limit;
168
  }
includes/deprecated-functions.php CHANGED
@@ -1,83 +1,83 @@
1
- <?php
2
-
3
- /**
4
- * Fetches feed items from sources provided
5
- * DEPRECATED - JUST FOR REFERENCE
6
- *
7
- * @since 2.0
8
- * @deprecated 3.0
9
- */
10
- /*function wprss_fetch_all_feed_items( ) {
11
-
12
- // Get all feed sources
13
- $feed_sources = new WP_Query( array(
14
- 'post_type' => 'wprss_feed',
15
- 'post_status' => 'publish',
16
- 'posts_per_page' => -1,
17
- ) );
18
-
19
- if( $feed_sources->have_posts() ) {
20
- /* Start by getting one feed source, we will cycle through them one by one,
21
- fetching feed items and adding them to the database in each pass */
22
- /* while ( $feed_sources->have_posts() ) {
23
- $feed_sources->the_post();
24
-
25
- $feed_ID = get_the_ID();
26
- $feed_url = get_post_meta( get_the_ID(), 'wprss_url', true );
27
-
28
- // Use the URL custom field to fetch the feed items for this source
29
- if( !empty( $feed_url ) ) {
30
-
31
- add_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_return_7200' );
32
- //$feed = fetch_feed( $feed_url );
33
- $feed = wprss_fetch_feed( $feed_url, $feed_ID );
34
- remove_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_return_7200' );
35
-
36
- // $feed->strip_htmltags( array_merge( $feed->strip_htmltags, array('h1', 'a', 'img') ) );
37
-
38
- if ( !is_wp_error( $feed ) ) {
39
- // Figure out how many total items there are, but limit it to 10.
40
- $maxitems = $feed->get_item_quantity(10);
41
-
42
- // Build an array of all the items, starting with element 0 (first element).
43
- $items = $feed->get_items( 0, $maxitems );
44
- }
45
- else { return; }
46
- }
47
-
48
- if ( ! empty( $items ) ) {
49
- // Gather the permalinks of existing feed item's related to this feed source
50
- global $wpdb;
51
- $existing_permalinks = $wpdb->get_col(
52
- "SELECT meta_value
53
- FROM $wpdb->postmeta
54
- WHERE meta_key = 'wprss_item_permalink'
55
- AND post_id IN ( SELECT post_id FROM $wpdb->postmeta WHERE meta_value = $feed_ID)
56
- ");
57
-
58
- foreach ( $items as $item ) {
59
- // Check if newly fetched item already present in existing feed item item,
60
- // if not insert it into wp_posts and insert post meta.
61
- if ( ! ( in_array( $item->get_permalink(), $existing_permalinks ) ) ) {
62
- // Create post object
63
- $feed_item = array(
64
- 'post_title' => $item->get_title(),
65
- 'post_content' => '',
66
- 'post_status' => 'publish',
67
- 'post_type' => 'wprss_feed_item'
68
- );
69
- $inserted_ID = wp_insert_post( $feed_item );
70
- wprss_items_create_post_meta( $inserted_ID, $item, $feed_ID );
71
- } //end if
72
- } //end foreach
73
- } // end if
74
- } // end $feed_sources while loop
75
- wp_reset_postdata(); // Restore the $post global to the current post in the main query
76
- // } // end if
77
- } // end if
78
- }
79
-
80
- // For testing query speed
81
- // $time_start = microtime( true );
82
- // wp_die(number_format( microtime( true ) - $time_start, 10 ));
83
-
1
+ <?php
2
+
3
+ /**
4
+ * Fetches feed items from sources provided
5
+ * DEPRECATED - JUST FOR REFERENCE
6
+ *
7
+ * @since 2.0
8
+ * @deprecated 3.0
9
+ */
10
+ /*function wprss_fetch_all_feed_items( ) {
11
+
12
+ // Get all feed sources
13
+ $feed_sources = new WP_Query( array(
14
+ 'post_type' => 'wprss_feed',
15
+ 'post_status' => 'publish',
16
+ 'posts_per_page' => -1,
17
+ ) );
18
+
19
+ if( $feed_sources->have_posts() ) {
20
+ /* Start by getting one feed source, we will cycle through them one by one,
21
+ fetching feed items and adding them to the database in each pass */
22
+ /* while ( $feed_sources->have_posts() ) {
23
+ $feed_sources->the_post();
24
+
25
+ $feed_ID = get_the_ID();
26
+ $feed_url = get_post_meta( get_the_ID(), 'wprss_url', true );
27
+
28
+ // Use the URL custom field to fetch the feed items for this source
29
+ if( !empty( $feed_url ) ) {
30
+
31
+ add_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_return_7200' );
32
+ //$feed = fetch_feed( $feed_url );
33
+ $feed = wprss_fetch_feed( $feed_url, $feed_ID );
34
+ remove_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_return_7200' );
35
+
36
+ // $feed->strip_htmltags( array_merge( $feed->strip_htmltags, array('h1', 'a', 'img') ) );
37
+
38
+ if ( !is_wp_error( $feed ) ) {
39
+ // Figure out how many total items there are, but limit it to 10.
40
+ $maxitems = $feed->get_item_quantity(10);
41
+
42
+ // Build an array of all the items, starting with element 0 (first element).
43
+ $items = $feed->get_items( 0, $maxitems );
44
+ }
45
+ else { return; }
46
+ }
47
+
48
+ if ( ! empty( $items ) ) {
49
+ // Gather the permalinks of existing feed item's related to this feed source
50
+ global $wpdb;
51
+ $existing_permalinks = $wpdb->get_col(
52
+ "SELECT meta_value
53
+ FROM $wpdb->postmeta
54
+ WHERE meta_key = 'wprss_item_permalink'
55
+ AND post_id IN ( SELECT post_id FROM $wpdb->postmeta WHERE meta_value = $feed_ID)
56
+ ");
57
+
58
+ foreach ( $items as $item ) {
59
+ // Check if newly fetched item already present in existing feed item item,
60
+ // if not insert it into wp_posts and insert post meta.
61
+ if ( ! ( in_array( $item->get_permalink(), $existing_permalinks ) ) ) {
62
+ // Create post object
63
+ $feed_item = array(
64
+ 'post_title' => $item->get_title(),
65
+ 'post_content' => '',
66
+ 'post_status' => 'publish',
67
+ 'post_type' => 'wprss_feed_item'
68
+ );
69
+ $inserted_ID = wp_insert_post( $feed_item );
70
+ wprss_items_create_post_meta( $inserted_ID, $item, $feed_ID );
71
+ } //end if
72
+ } //end foreach
73
+ } // end if
74
+ } // end $feed_sources while loop
75
+ wp_reset_postdata(); // Restore the $post global to the current post in the main query
76
+ // } // end if
77
+ } // end if
78
+ }
79
+
80
+ // For testing query speed
81
+ // $time_start = microtime( true );
82
+ // wp_die(number_format( microtime( true ) - $time_start, 10 ));
83
+
includes/fallback-mbstring.php ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * A utility class that provides compatibility padding for multibyte string
5
+ * functionality.
6
+ *
7
+ * Taken mostly from {@link https://doc.wikimedia.org/mediawiki-core/master/php/Fallback_8php_source.html here}
8
+ *
9
+ * @since 4.7
10
+ */
11
+ class WPRSS_MBString {
12
+
13
+
14
+ /**
15
+ * @since 4.7
16
+ * @param string $str
17
+ * @param int $start
18
+ * @param int|string $count
19
+ * @return string
20
+ */
21
+ public static function mb_substr( $str, $start, $count = 'end' ) {
22
+ if ( function_exists( 'mb_substr' ) ) {
23
+ return mb_substr( $str, $start );
24
+ }
25
+
26
+ if ( $start != 0 ) {
27
+ $split = self::mb_substr_split_unicode( $str, intval( $start ) );
28
+ $str = substr( $str, $split );
29
+ }
30
+
31
+ if ( $count !== 'end' ) {
32
+ $split = self::mb_substr_split_unicode( $str, intval( $count ) );
33
+ $str = substr( $str, 0, $split );
34
+ }
35
+
36
+ return $str;
37
+ }
38
+
39
+
40
+ /**
41
+ * @since 4.7
42
+ * @param string $str
43
+ * @param int $splitPos
44
+ * @return int
45
+ */
46
+ public static function mb_substr_split_unicode( $str, $splitPos ) {
47
+ if ( $splitPos == 0 ) {
48
+ return 0;
49
+ }
50
+
51
+ $byteLen = strlen( $str );
52
+
53
+ if ( $splitPos > 0 ) {
54
+ if ( $splitPos > 256 ) {
55
+ // Optimize large string offsets by skipping ahead N bytes.
56
+ // This will cut out most of our slow time on Latin-based text,
57
+ // and 1/2 to 1/3 on East European and Asian scripts.
58
+ $bytePos = $splitPos;
59
+ while ( $bytePos < $byteLen && $str[$bytePos] >= "\x80" && $str[$bytePos] < "\xc0" ) {
60
+ ++$bytePos;
61
+ }
62
+ $charPos = mb_strlen( substr( $str, 0, $bytePos ) );
63
+ } else {
64
+ $charPos = 0;
65
+ $bytePos = 0;
66
+ }
67
+
68
+ while ( $charPos++ < $splitPos ) {
69
+ ++$bytePos;
70
+ // Move past any tail bytes
71
+ while ( $bytePos < $byteLen && $str[$bytePos] >= "\x80" && $str[$bytePos] < "\xc0" ) {
72
+ ++$bytePos;
73
+ }
74
+ }
75
+ } else {
76
+ $splitPosX = $splitPos + 1;
77
+ $charPos = 0; // relative to end of string; we don't care about the actual char position here
78
+ $bytePos = $byteLen;
79
+ while ( $bytePos > 0 && $charPos-- >= $splitPosX ) {
80
+ --$bytePos;
81
+ // Move past any tail bytes
82
+ while ( $bytePos > 0 && $str[$bytePos] >= "\x80" && $str[$bytePos] < "\xc0" ) {
83
+ --$bytePos;
84
+ }
85
+ }
86
+ }
87
+
88
+ return $bytePos;
89
+ }
90
+
91
+
92
+ /**
93
+ * @since 4.7
94
+ * @param string $str
95
+ * @return int
96
+ */
97
+ public static function mb_strlen( $str, $enc = '' ) {
98
+ if ( function_exists( 'mb_strlen' ) ) {
99
+ return mb_strlen( $str );
100
+ }
101
+
102
+ $counts = count_chars( $str );
103
+ $total = 0;
104
+
105
+ // Count ASCII bytes
106
+ for ( $i = 0; $i < 0x80; $i++ ) {
107
+ $total += $counts[$i];
108
+ }
109
+
110
+ // Count multibyte sequence heads
111
+ for ( $i = 0xc0; $i < 0xff; $i++ ) {
112
+ $total += $counts[$i];
113
+ }
114
+ return $total;
115
+ }
116
+
117
+
118
+ /**
119
+ * @since 4.7
120
+ * @param string $haystack
121
+ * @param string $needle
122
+ * @param int $offset
123
+ * @return int|boolean
124
+ */
125
+ public static function mb_strpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
126
+ if ( function_exists( 'mb_strpos' ) ) {
127
+ return mb_strpos( $haystack, $needle, $offset );
128
+ }
129
+
130
+ $needle = preg_quote( $needle, '/' );
131
+
132
+ $ar = array();
133
+ preg_match( '/' . $needle . '/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset );
134
+
135
+ if ( isset( $ar[0][1] ) ) {
136
+ return $ar[0][1];
137
+ } else {
138
+ return false;
139
+ }
140
+ }
141
+
142
+
143
+ /**
144
+ * @since 4.7
145
+ * @param string $haystack
146
+ * @param string $needle
147
+ * @param int $offset
148
+ * @return int|boolean
149
+ */
150
+ public static function mb_stripos( $haystack, $needle, $offset = 0, $encoding = '' ) {
151
+ if ( function_exists( 'mb_stripos' ) ) {
152
+ return mb_stripos( $haystack, $needle, $offset );
153
+ }
154
+
155
+
156
+ $needle = preg_quote( $needle, '/' );
157
+
158
+ $ar = array();
159
+ preg_match( '/' . $needle . '/ui', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset );
160
+
161
+ if ( isset( $ar[0][1] ) ) {
162
+ return $ar[0][1];
163
+ } else {
164
+ return false;
165
+ }
166
+ }
167
+
168
+
169
+ /**
170
+ * @since 4.7
171
+ * @param string $haystack
172
+ * @param string $needle
173
+ * @param int $offset
174
+ * @return int|boolean
175
+ */
176
+ public static function mb_strrpos( $haystack, $needle, $offset = 0, $encoding = '' ) {
177
+ if ( function_exists( 'mb_strrpos' ) ) {
178
+ return mb_strrpos( $haystack, $needle, $offset );
179
+ }
180
+
181
+ $needle = preg_quote( $needle, '/' );
182
+
183
+ $ar = array();
184
+ preg_match_all( '/' . $needle . '/u', $haystack, $ar, PREG_OFFSET_CAPTURE, $offset );
185
+
186
+ if ( isset( $ar[0] ) && count( $ar[0] ) > 0 && isset( $ar[0][count( $ar[0] ) - 1][1] ) ) {
187
+ return $ar[0][count( $ar[0] ) - 1][1];
188
+ } else {
189
+ return false;
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Lowercase a UTF-8 string.
195
+ * This supports accented letters, but nothing more.
196
+ * Taken from {@link https://github.com/drupal/drupal/blob/9.x/core/includes/unicode.inc#L432 here}.
197
+ *
198
+ * @since 4.7
199
+ * @param $text The string to run the operation on.
200
+ * @return string The string in lowercase.
201
+ */
202
+ public function mb_strtolower( $text ) {
203
+ if ( function_exists( 'mb_strtolower' ) )
204
+ return mb_strtolower( $text );
205
+
206
+ // Use C-locale for ASCII-only lowercase
207
+ $text = strtolower( $text );
208
+ // Case flip Latin-1 accented letters
209
+ $text = preg_replace_callback( '/\xC3[\x80-\x96\x98-\x9E]/', array( __CLASS__, '_unicode_caseflip' ), $text );
210
+ return $text;
211
+ }
212
+
213
+
214
+ /**
215
+ * Flips U+C0-U+DE to U+E0-U+FD and back.
216
+ *
217
+ * @since 4.7
218
+ * @param $matches An array of matches.
219
+ * @return array The Latin-1 version of the array of matches.
220
+ * @see mb_strtolower()
221
+ */
222
+ public function _unicode_caseflip( $matches ) {
223
+ return $matches[0][0] . chr( ord( $matches[0][1] ) ^ 32 );
224
+ }
225
+
226
+ }
includes/feed-access.php ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+
4
+ /**
5
+ * Centralizes control over resource fetching.
6
+ *
7
+ * @since 4.7
8
+ */
9
+ class WPRSS_Feed_Access {
10
+
11
+ protected static $_instance;
12
+
13
+ protected $_certificate_file_path;
14
+
15
+ const SETTING_KEY_CERTIFICATE_PATH = 'certificate-path';
16
+
17
+ /**
18
+ * @since 4.7
19
+ * @return WPRSS_Feed_Access The singleton instance of this class.
20
+ */
21
+ public static function instance() {
22
+ if ( is_null( self::$_instance ) ) {
23
+ $class_name = __CLASS__;
24
+ self::$_instance = new $class_name;
25
+ }
26
+
27
+ return self::$_instance;
28
+ }
29
+
30
+
31
+ public function __construct() {
32
+ $this->_construct();
33
+ }
34
+
35
+
36
+ /**
37
+ * The parameter-less constructor.
38
+ *
39
+ * @since 4.7
40
+ */
41
+ protected function _construct() {
42
+ add_action( 'wp_feed_options', array( $this, 'set_feed_options' ), 10, 2 );
43
+ add_action( 'wprss_settings_array', array( $this, 'add_settings' ) );
44
+ add_action( 'wprss_default_settings_general', array( $this, 'add_default_settings' ) );
45
+ }
46
+
47
+
48
+ /**
49
+ * Sets the path to the certificate, which will be used by WPRSS to fetch remote content.
50
+ *
51
+ * @since 4.7
52
+ * @param string $path Absolute path to the certificate file.
53
+ * @return \WPRSS_Feed_Access This instance.
54
+ */
55
+ public function set_certificate_file_path( $path ) {
56
+ $this->_certificate_file_path = $path;
57
+ return $this;
58
+ }
59
+
60
+
61
+ /**
62
+ * Gets the path to the certificate, which will be used by WPRSS to fetch remote content.
63
+ *
64
+ * @since 4.7
65
+ * @see get_certificate_path_setting()
66
+ * @return string Absolute path to the certificate file. By default will use the option.
67
+ */
68
+ public function get_certificate_file_path() {
69
+ if ( empty( $this->_certificate_file_path ) )
70
+ $this->_certificate_file_path = $this->get_certificate_path_setting();
71
+
72
+ return $this->_certificate_file_path;
73
+ }
74
+
75
+
76
+ /**
77
+ * Gets the value of the option that stores the path to the certificate file.
78
+ * Relative paths will be converted to absolute, as if relative to WP root.
79
+ *
80
+ * @since 4.7
81
+ * @return string Absolute path to the certificate file.
82
+ */
83
+ public function get_certificate_path_setting() {
84
+ $path = wprss_get_general_setting( self::SETTING_KEY_CERTIFICATE_PATH );
85
+
86
+ if ( empty( $path ) )
87
+ return $path;
88
+
89
+ if ( !path_is_absolute( $path ) )
90
+ $path = ABSPATH . $path;
91
+
92
+ return $path;
93
+ }
94
+
95
+
96
+ /**
97
+ * This happens before feed initialization, but before the tags to be stripped are set.
98
+ * Handles the `wp_feed_options` action.
99
+ * To modify these tags, use the `wprss_feed_tags_to_strip` filter.
100
+ *
101
+ * @since 4.7
102
+ * @param SimplePie $feed The instance of the object that represents the feed to be fetched.
103
+ * @param string $url The URL, from which the feed is going to be fetched.
104
+ */
105
+ public function set_feed_options( $feed, $url ) {
106
+ $file = new WPRSS_SimplePie_File;
107
+ $feed->set_file_class( 'WPRSS_SimplePie_File' );
108
+ WPRSS_SimplePie_File::set_default_certificate_file_path( $this->get_certificate_file_path() );
109
+ }
110
+
111
+
112
+ /**
113
+ * Implements a `wprss_settings_array` filter.
114
+ *
115
+ * @since 4.7
116
+ * @param array $settings The current settings array, where 1st dimension is secion code, 2nd is setting code, 3rd is setting option(s).
117
+ * @return array The new settings array.
118
+ */
119
+ public function add_settings( $settings ) {
120
+ $settings['general'][ self::SETTING_KEY_CERTIFICATE_PATH ] = array(
121
+ 'label' => __( 'Certificate Path', WPRSS_TEXT_DOMAIN ),
122
+ 'callback' => array( $this, 'render_certificate_path_setting' )
123
+ );
124
+
125
+ return $settings;
126
+ }
127
+
128
+
129
+ /**
130
+ * @since 4.7
131
+ * @param array $settings The array of settings, where key is
132
+ * @return array The new array of default settings
133
+ */
134
+ public function add_default_settings( $settings ) {
135
+ $settings[ self::SETTING_KEY_CERTIFICATE_PATH ] = implode( '/', array( WPINC, 'certificates', 'ca-bundle.crt' ) );
136
+
137
+ return $settings;
138
+ }
139
+
140
+
141
+ /**
142
+ * Renders the setting field for the certificate path.
143
+ *
144
+ * @since 4.7
145
+ * @see wprss_admin_init
146
+ * @param array $field Data of this field.
147
+ */
148
+ public function render_certificate_path_setting( $field ) {
149
+ $feed_limit = wprss_get_general_setting( $field['field_id'] );
150
+ ?>
151
+ <input id="<?php echo $field['field_id'] ?>" name="wprss_settings_general[<?php echo $field['field_id'] ?>]" type="text" value="<?php echo $feed_limit ?>" />
152
+ <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
153
+ }
154
+ }
155
+
156
+ // Initialize
157
+ WPRSS_Feed_Access::instance();
158
+
159
+
160
+ /**
161
+ * A padding layer used to give WPRSS more control over fetching of feed resources.
162
+ * @since 4.7
163
+ */
164
+ class WPRSS_SimplePie_File extends SimplePie_File {
165
+
166
+ protected static $_default_certificate_file_path;
167
+ protected $_certificate_file_path;
168
+
169
+
170
+ /**
171
+ * Copied from {@see SimplePie_File#__construct()}.
172
+ * Adds call to {@see _before_curl_exec()}.
173
+ *
174
+ * @since 4.7
175
+ */
176
+ public function __construct( $url, $timeout = 10, $redirects = 5, $headers = null, $useragent = null, $force_fsockopen = false ) {
177
+ if ( class_exists( 'idna_convert' ) ) {
178
+ $idn = new idna_convert();
179
+ $parsed = SimplePie_Misc::parse_url( $url );
180
+ $url = SimplePie_Misc::compress_parse_url( $parsed['scheme'], $idn->encode( $parsed['authority'] ), $parsed['path'], $parsed['query'], $parsed['fragment'] );
181
+ }
182
+ $this->url = $url;
183
+ $this->useragent = $useragent;
184
+ if ( preg_match( '/^http(s)?:\/\//i', $url ) ) {
185
+ if ( $useragent === null ) {
186
+ $useragent = ini_get( 'user_agent' );
187
+ $this->useragent = $useragent;
188
+ }
189
+ if ( !is_array( $headers ) ) {
190
+ $headers = array();
191
+ }
192
+ if ( !$force_fsockopen && function_exists( 'curl_exec' ) ) {
193
+ $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_CURL;
194
+ $fp = curl_init();
195
+ $headers2 = array();
196
+ foreach ( $headers as $key => $value ) {
197
+ $headers2[] = "$key: $value";
198
+ }
199
+ if ( version_compare( SimplePie_Misc::get_curl_version(), '7.10.5', '>=' ) ) {
200
+ curl_setopt( $fp, CURLOPT_ENCODING, '' );
201
+ }
202
+ curl_setopt( $fp, CURLOPT_URL, $url );
203
+ curl_setopt( $fp, CURLOPT_HEADER, 1 );
204
+ curl_setopt( $fp, CURLOPT_RETURNTRANSFER, 1 );
205
+ curl_setopt( $fp, CURLOPT_TIMEOUT, $timeout );
206
+ curl_setopt( $fp, CURLOPT_CONNECTTIMEOUT, $timeout );
207
+ curl_setopt( $fp, CURLOPT_REFERER, $url );
208
+ curl_setopt( $fp, CURLOPT_USERAGENT, $useragent );
209
+ curl_setopt( $fp, CURLOPT_HTTPHEADER, $headers2 );
210
+ if ( !ini_get( 'open_basedir' ) && !ini_get( 'safe_mode' ) && version_compare( SimplePie_Misc::get_curl_version(), '7.15.2', '>=' ) ) {
211
+ curl_setopt( $fp, CURLOPT_FOLLOWLOCATION, 1 );
212
+ curl_setopt( $fp, CURLOPT_MAXREDIRS, $redirects );
213
+ }
214
+
215
+ $this->_before_curl_exec( $fp, $url );
216
+
217
+ $this->headers = curl_exec( $fp );
218
+ if ( curl_errno( $fp ) === 23 || curl_errno( $fp ) === 61 ) {
219
+ curl_setopt( $fp, CURLOPT_ENCODING, 'none' );
220
+ $this->headers = curl_exec( $fp );
221
+ }
222
+ if ( curl_errno( $fp ) ) {
223
+ $this->error = 'cURL error ' . curl_errno( $fp ) . ': ' . curl_error( $fp );
224
+ $this->success = false;
225
+ } else {
226
+ $info = curl_getinfo( $fp );
227
+ curl_close( $fp );
228
+ $this->headers = explode( "\r\n\r\n", $this->headers, $info['redirect_count'] + 1 );
229
+ $this->headers = array_pop( $this->headers );
230
+ $parser = new SimplePie_HTTP_Parser( $this->headers );
231
+ if ( $parser->parse() ) {
232
+ $this->headers = $parser->headers;
233
+ $this->body = $parser->body;
234
+ $this->status_code = $parser->status_code;
235
+ if ( (in_array( $this->status_code, array( 300, 301, 302, 303, 307 ) ) || $this->status_code > 307 && $this->status_code < 400) && isset( $this->headers['location'] ) && $this->redirects < $redirects ) {
236
+ $this->redirects++;
237
+ $location = SimplePie_Misc::absolutize_url( $this->headers['location'], $url );
238
+ return $this->__construct( $location, $timeout, $redirects, $headers, $useragent, $force_fsockopen );
239
+ }
240
+ }
241
+ }
242
+ } else {
243
+ $this->method = SIMPLEPIE_FILE_SOURCE_REMOTE | SIMPLEPIE_FILE_SOURCE_FSOCKOPEN;
244
+ $url_parts = parse_url( $url );
245
+ $socket_host = $url_parts['host'];
246
+ if ( isset( $url_parts['scheme'] ) && strtolower( $url_parts['scheme'] ) === 'https' ) {
247
+ $socket_host = "ssl://{$url_parts['host']}";
248
+ $url_parts['port'] = 443;
249
+ }
250
+ if ( !isset( $url_parts['port'] ) ) {
251
+ $url_parts['port'] = 80;
252
+ }
253
+ $fp = @fsockopen( $socket_host, $url_parts['port'], $errno, $errstr, $timeout );
254
+ if ( !$fp ) {
255
+ $this->error = 'fsockopen error: ' . $errstr;
256
+ $this->success = false;
257
+ } else {
258
+ stream_set_timeout( $fp, $timeout );
259
+ if ( isset( $url_parts['path'] ) ) {
260
+ if ( isset( $url_parts['query'] ) ) {
261
+ $get = "{$url_parts['path']}?{$url_parts['query']}";
262
+ } else {
263
+ $get = $url_parts['path'];
264
+ }
265
+ } else {
266
+ $get = '/';
267
+ }
268
+ $out = "GET $get HTTP/1.1\r\n";
269
+ $out .= "Host: {$url_parts['host']}\r\n";
270
+ $out .= "User-Agent: $useragent\r\n";
271
+ if ( extension_loaded( 'zlib' ) ) {
272
+ $out .= "Accept-Encoding: x-gzip,gzip,deflate\r\n";
273
+ }
274
+
275
+ if ( isset( $url_parts['user'] ) && isset( $url_parts['pass'] ) ) {
276
+ $out .= "Authorization: Basic " . base64_encode( "{$url_parts['user']}:{$url_parts['pass']}" ) . "\r\n";
277
+ }
278
+ foreach ( $headers as $key => $value ) {
279
+ $out .= "$key: $value\r\n";
280
+ }
281
+ $out .= "Connection: Close\r\n\r\n";
282
+ fwrite( $fp, $out );
283
+
284
+ $info = stream_get_meta_data( $fp );
285
+
286
+ $this->headers = '';
287
+ while ( !$info['eof'] && !$info['timed_out'] ) {
288
+ $this->headers .= fread( $fp, 1160 );
289
+ $info = stream_get_meta_data( $fp );
290
+ }
291
+ if ( !$info['timed_out'] ) {
292
+ $parser = new SimplePie_HTTP_Parser( $this->headers );
293
+ if ( $parser->parse() ) {
294
+ $this->headers = $parser->headers;
295
+ $this->body = $parser->body;
296
+ $this->status_code = $parser->status_code;
297
+ if ( (in_array( $this->status_code, array( 300, 301, 302, 303, 307 ) ) || $this->status_code > 307 && $this->status_code < 400) && isset( $this->headers['location'] ) && $this->redirects < $redirects ) {
298
+ $this->redirects++;
299
+ $location = SimplePie_Misc::absolutize_url( $this->headers['location'], $url );
300
+ return $this->__construct( $location, $timeout, $redirects, $headers, $useragent, $force_fsockopen );
301
+ }
302
+ if ( isset( $this->headers['content-encoding'] ) ) {
303
+ // Hey, we act dumb elsewhere, so let's do that here too
304
+ switch ( strtolower( trim( $this->headers['content-encoding'], "\x09\x0A\x0D\x20" ) ) ) {
305
+ case 'gzip':
306
+ case 'x-gzip':
307
+ $decoder = new SimplePie_gzdecode( $this->body );
308
+ if ( !$decoder->parse() ) {
309
+ $this->error = 'Unable to decode HTTP "gzip" stream';
310
+ $this->success = false;
311
+ } else {
312
+ $this->body = $decoder->data;
313
+ }
314
+ break;
315
+
316
+ case 'deflate':
317
+ if ( ($decompressed = gzinflate( $this->body )) !== false ) {
318
+ $this->body = $decompressed;
319
+ } else if ( ($decompressed = gzuncompress( $this->body )) !== false ) {
320
+ $this->body = $decompressed;
321
+ } else if ( function_exists( 'gzdecode' ) && ($decompressed = gzdecode( $this->body )) !== false ) {
322
+ $this->body = $decompressed;
323
+ } else {
324
+ $this->error = 'Unable to decode HTTP "deflate" stream';
325
+ $this->success = false;
326
+ }
327
+ break;
328
+
329
+ default:
330
+ $this->error = 'Unknown content coding';
331
+ $this->success = false;
332
+ }
333
+ }
334
+ }
335
+ } else {
336
+ $this->error = 'fsocket timed out';
337
+ $this->success = false;
338
+ }
339
+ fclose( $fp );
340
+ }
341
+ }
342
+ } else {
343
+ $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
344
+ if ( !$this->body = file_get_contents( $url ) ) {
345
+ $this->error = 'file_get_contents could not read the file';
346
+ $this->success = false;
347
+ }
348
+ }
349
+ }
350
+
351
+
352
+ /**
353
+ * Additional preparation of the curl request.
354
+ * Sets the {@link CURLOPT_CAINFO http://php.net/manual/en/function.curl-setopt.php}
355
+ * cURL option to a value determined by {@see get_default_certificate_file_path}.
356
+ * If the value is empty, leaves it as is.
357
+ *
358
+ * @since 4.7
359
+ * @param resource $fp Pointer to a resource created by {@see curl_init()}.
360
+ * @param string $url The URL, to which the cURL request is being made.
361
+ * @return \WPRSS_SimplePie_File This instance.
362
+ */
363
+ protected function _before_curl_exec( $fp, $url ) {
364
+ if ( ($ca_path = self::get_default_certificate_file_path()) && !empty( $ca_path ) ) {
365
+ $this->_certificate_file_path = $ca_path;
366
+ curl_setopt( $fp, CURLOPT_CAINFO, $this->_certificate_file_path );
367
+ }
368
+
369
+ return $this;
370
+ }
371
+
372
+
373
+ /**
374
+ * Gets the path to the certificate, which will be used by this instance
375
+ * to fetch remote content.
376
+ *
377
+ * @since 4.7
378
+ * @return string Path to the certificate file.
379
+ */
380
+ public function get_certificate_file_path() {
381
+ return $this->_certificate_file_path;
382
+ }
383
+
384
+
385
+ /**
386
+ * Gets the path to the certificate file, which will be used by future
387
+ * instances of this class.
388
+ *
389
+ * @since 4.7
390
+ * @return string Path to the certificate file.
391
+ */
392
+ public static function get_default_certificate_file_path() {
393
+ return self::$_default_certificate_file_path;
394
+ }
395
+
396
+
397
+ /**
398
+ * Sets the path to the certificate file.
399
+ * This path will be used by future instances of this class.
400
+ *
401
+ * @since 4.7
402
+ * @param string $path The path to the certificate file.
403
+ */
404
+ public static function set_default_certificate_file_path( $path ) {
405
+ self::$_default_certificate_file_path = $path;
406
+ }
407
+
408
+ }
includes/feed-blacklist.php CHANGED
@@ -1,353 +1,353 @@
1
- <?php
2
-
3
- /**
4
- * This file contains all functions relating to the blacklisting of
5
- * imported feeds items.
6
- *
7
- * Blacklisting a feed item is in essence nothing more than a saved list
8
- * of feed items. When a feed item is imported, its normalized permalink
9
- * is tested against this list, and if found, the feed item is not
10
- * imported. Admins can add items to the blacklist, to prevent them
11
- * from being imported again.
12
- *
13
- * @package WP RSS Aggregator
14
- * @since 4.4
15
- */
16
-
17
-
18
- // Check if the 'blacklist' GET param is set
19
- add_action( 'admin_init', 'wprss_check_if_blacklist_item' );
20
- // Checks if the transient is set to show the notice
21
- add_action( 'admin_init', 'wprss_check_notice_transient' );
22
- // Register custom post type
23
- add_action( 'init', 'wprss_blacklist_cpt' );
24
- // Add the row actions to the targetted post type
25
- add_filter( 'post_row_actions', 'wprss_blacklist_row_actions', 10, 1 );
26
- // Check if deleting a blacklist item, from the GET parameter
27
- add_action( 'admin_init', 'wprss_check_if_blacklist_delete' );
28
- // Changes the wprss_blacklist table columns
29
- add_filter( 'manage_wprss_blacklist_posts_columns', 'wprss_blacklist_columns');
30
- // Prints the table data for each blacklist entry
31
- add_action( 'manage_wprss_blacklist_posts_custom_column' , 'wprss_blacklist_table_contents', 10, 2 );
32
- // Changes the wprss_blacklist bulk actions
33
- add_filter('bulk_actions-edit-wprss_blacklist','wprss_blacklist_bulk_actions', 5, 1 );
34
-
35
-
36
- /**
37
- * Retrieves the blacklisted items.
38
- *
39
- * @since 4.4
40
- * @return array An associative array of blacklisted item, each entry
41
- * having the key as the permalink, and the value as the title.
42
- */
43
- function wprss_get_blacklist() {
44
- // Get the option
45
- $blacklist_option = get_option('wprss_blacklist');
46
- // If the option does not exist
47
- if ( $blacklist_option === FALSE || !is_array( $blacklist_option ) ) {
48
- // create it
49
- update_option( 'wprss_blacklist', array() );
50
- $blacklist_option = array();
51
- }
52
- return $blacklist_option;
53
- }
54
-
55
-
56
- /**
57
- * Creates a blacklist entry for the given feed item.
58
- *
59
- * @since 4.4
60
- * @param int|string The ID of the feed item to add to the blacklist
61
- */
62
- function wprss_blacklist_item( $ID ) {
63
- // Return if feed item is null
64
- if ( is_null( $ID ) ) return;
65
-
66
- // Get the feed item data
67
- $item_title = get_the_title( $ID );
68
- $item_permalink = get_post_meta( $ID, 'wprss_item_permalink', TRUE );
69
- // If not an imported item, stop
70
- if ( $item_permalink === '' ) {
71
- wprss_log_obj( 'An item being blacklisted was ignored for not being an imported item', $ID, null, WPRSS_LOG_LEVEL_INFO );
72
- return;
73
- }
74
- // Prepare the data for blacklisting
75
- $title = apply_filters( 'wprss_blacklist_title', trim( $item_title ) );
76
- $permalink = apply_filters( 'wprss_blacklist_permalink', trim( $item_permalink ) );
77
-
78
- // Get the blacklisted items
79
- $blacklist = wprss_get_blacklist();
80
- // Add the item to the blacklist
81
- $blacklist[ $permalink ] = $title;
82
-
83
- // Delete the item
84
- wp_delete_post( $ID, TRUE );
85
-
86
- // Add the blacklisted item
87
- $id = wp_insert_post(array(
88
- 'post_title' => $title,
89
- 'post_type' => 'wprss_blacklist',
90
- 'post_status' => 'publish'
91
- ));
92
- update_post_meta( $id, 'wprss_permalink', $permalink );
93
- }
94
-
95
-
96
- /**
97
- * Determines whether the given item is blacklist.
98
- *
99
- * @since 4.4
100
- * @param string $permalink The permalink to look for in the saved option
101
- * @return bool TRUE if the permalink is found, FALSE otherwise.
102
- */
103
- function wprss_is_blacklisted( $permalink ) {
104
- // Query the blacklist entries, for an item with the given permalink
105
- $query = new WP_Query(array(
106
- 'post_type' => 'wprss_blacklist',
107
- 'meta_key' => 'wprss_permalink',
108
- 'meta_value' => $permalink
109
- ));
110
- // Return TRUE if the query returned a result, FALSE otherwise
111
- return $query->have_posts();
112
- }
113
-
114
-
115
- /**
116
- * Check if the 'blacklist' GET param is set, and prepare to blacklist
117
- * the item.
118
- *
119
- * @since 4.4
120
- */
121
- function wprss_check_if_blacklist_item() {
122
- // If the GET param is not set, do nothing. Return.
123
- if ( empty( $_GET['wprss_blacklist'] ) ) return;
124
-
125
- // Get the ID from the GET param
126
- $ID = $_GET['wprss_blacklist'];
127
- // If the post does not exist, stop. Show a message
128
- if ( get_post($ID) === NULL ) {
129
- wp_die( __( 'The item you are trying to blacklist does not exist', WPRSS_TEXT_DOMAIN ) );
130
- }
131
-
132
- // If the post type is not correct,
133
- if ( get_post_meta( $ID, 'wprss_item_permalink', TRUE ) === '' || get_post_status( $ID ) !== 'trash' ) {
134
- wp_die( __( 'The item you are trying to blacklist is not valid!', WPRSS_TEXT_DOMAIN ) );
135
- }
136
-
137
- check_admin_referer( 'blacklist-item-' . $ID, 'wprss_blacklist_item' );
138
- wprss_blacklist_item( $ID );
139
-
140
- // Get the current post type for the current page
141
- $post_type = isset( $_GET['post_type'] )? $_GET['post_type'] : 'post';
142
- // Check the current page, and generate the URL query string for the page
143
- $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
144
- // Set the notice transient
145
- set_transient( 'wprss_item_blacklist_notice', 'true' );
146
- // Refresh the page without the GET parameter
147
- wp_redirect( admin_url( "edit.php?post_type=$post_type&post_status=trash" . $paged ) );
148
- exit();
149
- }
150
-
151
-
152
- /**
153
- * Checks if the transient for the blacklist notice is set, and shows the notice
154
- * if it is set.
155
- */
156
- function wprss_check_notice_transient() {
157
- // Check if the transient exists
158
- $transient = get_transient( 'wprss_item_blacklist_notice' );
159
- if ( $transient !== FALSE ) {
160
- // Remove the transient
161
- delete_transient( 'wprss_item_blacklist_notice' );
162
- // Show the notice
163
- add_action( 'admin_notices', 'wprss_blacklist_item_notice' );
164
- }
165
- }
166
-
167
- /**
168
- * The admin notice shown when an item is blacklisted.
169
- */
170
- function wprss_blacklist_item_notice() {
171
- ?>
172
- <div class="updated">
173
- <p>
174
- The item was deleted successfully and added to the blacklist.
175
- </p>
176
- </div>
177
- <?php
178
- }
179
-
180
-
181
- /**
182
- * Registers the Blacklist Custom Post Type.
183
- *
184
- * @since 4.4
185
- */
186
- function wprss_blacklist_cpt() {
187
- register_post_type( 'wprss_blacklist', array(
188
- 'label' => 'Blacklist',
189
- 'public' => false,
190
- 'exclude_from_search' => true,
191
- 'show_ui' => true,
192
- 'show_in_menu' => 'edit.php?post_type=wprss_feed',
193
- 'capability_type' => 'feed_source',
194
- 'supports' => array( 'title' ),
195
- 'labels' => array(
196
- 'name' => __( 'Blacklist', WPRSS_TEXT_DOMAIN ),
197
- 'singular_name' => __( 'Blacklist', WPRSS_TEXT_DOMAIN ),
198
- 'all_items' => __( 'Blacklist', WPRSS_TEXT_DOMAIN ),
199
- 'search_items' => __( 'Search Blacklist', WPRSS_TEXT_DOMAIN ),
200
- 'not_found' => __( 'You do not have any items blacklisted yet!', WPRSS_TEXT_DOMAIN ),
201
- )
202
- ));
203
- }
204
-
205
-
206
- /**
207
- * Adds the row actions to the targetted post type.
208
- * Default post type = wprss_feed_item
209
- *
210
- * @since 4.4
211
- * @param array $actions The row actions to be filtered
212
- * @return array The new filtered row actions
213
- */
214
- function wprss_blacklist_row_actions( $actions ) {
215
- // Check the current page, and generate the URL query string for the page
216
- $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
217
-
218
-
219
- // Check the post type
220
- if ( get_post_status() == 'trash' ) {
221
- // Get the Post ID
222
- $ID = get_the_ID();
223
-
224
- // Get the permalink. If does not exist, then it is not an imported item.
225
- $permalink = get_post_meta( $ID, 'wprss_item_permalink', TRUE );
226
- if ( $permalink === '' ) {
227
- $actions;
228
- }
229
-
230
- // The post type on the current screen
231
- $post_type = get_post_type();
232
- // Prepare the blacklist URL
233
- $plain_url = apply_filters(
234
- 'wprss_blacklist_row_action_url',
235
- admin_url( "edit.php?post_type=$post_type&wprss_blacklist=$ID" ),
236
- $ID
237
- ) . $paged;
238
- // Add a nonce to the URL
239
- $nonced_url = wp_nonce_url( $plain_url, 'blacklist-item-' . $ID, 'wprss_blacklist_item' );
240
-
241
- // Prepare the text
242
- $text = apply_filters( 'wprss_blacklist_row_action_text', htmlentities( __( 'Delete Permanently & Blacklist', WPRSS_TEXT_DOMAIN ) ) );
243
- $text = __( $text, WPRSS_TEXT_DOMAIN );
244
-
245
- // Prepare the hint
246
- $hint = apply_filters(
247
- 'wprss_blacklist_row_action_hint',
248
- __( 'The item will be deleted permanently, and its permalink will be recorded in the blacklist', WPRSS_TEXT_DOMAIN )
249
- );
250
- $hint = esc_attr( __( $hint, WPRSS_TEXT_DOMAIN ) );
251
-
252
- // Add the blacklist action
253
- $actions['blacklist-item'] = "<span class='delete'><a title='$hint' href='$nonced_url'>$text</a></span>";
254
- }
255
-
256
- // For the blacklisted item
257
- elseif ( get_post_type() === 'wprss_blacklist' ) {
258
- $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
259
- $remove_url = wp_nonce_url( 'post.php?wprss-blacklist-remove='.get_the_ID(), 'blacklist-remove-' . get_the_ID(), 'wprss_blacklist_trash' );
260
- $actions = array(
261
- 'trash' => '<a href="'.$remove_url.'">' . __( 'Remove from blacklist', WPRSS_TEXT_DOMAIN ) . '</a>'
262
- );
263
- }
264
-
265
- // Return the actions
266
- return $actions;
267
- }
268
-
269
-
270
- /**
271
- * Checks for the GET parameter wprss-blacklist-remove, and if present,
272
- * deletes the appropriate blacklist entry. Uses nonce 'wprss_blacklist_trash'
273
- * with action 'blacklist-remove-$ID'
274
- *
275
- * @since 4.4
276
- */
277
- function wprss_check_if_blacklist_delete() {
278
- // If the GET param is not set, do nothing. Return.
279
- if ( empty( $_GET['wprss-blacklist-remove'] ) ) return;
280
-
281
- // The array of blacklist entries to delete
282
- $to_delete = array();
283
- // The ID of the blacklist entry - if only deleting a single entry
284
- $ID = $_GET['wprss-blacklist-remove'];
285
-
286
- // check if deleting in bulk
287
- if ( isset( $_GET['wprss-bulk'] ) && $_GET['wprss-bulk'] == '1' ) {
288
- $to_delete = explode( ',', $ID );
289
- check_admin_referer( 'blacklist-remove-selected', 'wprss_blacklist_trash' );
290
- } else {
291
- $to_delete = array( $ID );
292
- // Get the ID from the GET param
293
- // Verify the nonce
294
- check_admin_referer( 'blacklist-remove-' . $ID, 'wprss_blacklist_trash' );
295
- }
296
-
297
- // Delete the posts marked for delete
298
- foreach( $to_delete as $delete_id ) {
299
- $post = get_post( $delete_id );
300
- if ( $post === NULL || get_post_type( $post ) !== 'wprss_blacklist' ) continue;
301
- wp_delete_post( $delete_id, TRUE );
302
- }
303
-
304
- // Redirect back to blacklists page
305
- $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
306
- header('Location: ' . admin_url('edit.php?post_type=wprss_blacklist' . $paged ) );
307
- exit;
308
- }
309
-
310
-
311
- /**
312
- * Returns the custom columns for the blacklist post type
313
- *
314
- * @since 4.4
315
- * @params array $cols The columns to filter
316
- * @return array The new columns
317
- */
318
- function wprss_blacklist_columns( $cols ) {
319
- return array(
320
- 'cb' => $cols['cb'],
321
- 'title' => __( 'Title' ),
322
- 'permalink' => __( 'Permalink' )
323
- );
324
- }
325
-
326
-
327
- /**
328
- * Prints the cell data in the table for each blacklist entry
329
- *
330
- * @since 4.4
331
- * @param string $column The column slug
332
- * @param string|int $ID The ID of the post currently being printed
333
- */
334
- function wprss_blacklist_table_contents( $column, $ID ) {
335
- switch ( $column ) {
336
- case 'permalink':
337
- $permalink = get_post_meta( $ID, 'wprss_permalink', TRUE );
338
- echo '<a href="'.$permalink.'" target="_blank">'.$permalink.'</a>';
339
- break;
340
- }
341
- }
342
-
343
-
344
- /**
345
- * Removes the bulk actions for the Blacklist post type
346
- *
347
- * @since 4.4
348
- * @param array $actions The array of actions to be filtered
349
- * @return array An empty array
350
- */
351
- function wprss_blacklist_bulk_actions( $actions ) {
352
- return array();
353
  }
1
+ <?php
2
+
3
+ /**
4
+ * This file contains all functions relating to the blacklisting of
5
+ * imported feeds items.
6
+ *
7
+ * Blacklisting a feed item is in essence nothing more than a saved list
8
+ * of feed items. When a feed item is imported, its normalized permalink
9
+ * is tested against this list, and if found, the feed item is not
10
+ * imported. Admins can add items to the blacklist, to prevent them
11
+ * from being imported again.
12
+ *
13
+ * @package WP RSS Aggregator
14
+ * @since 4.4
15
+ */
16
+
17
+
18
+ // Check if the 'blacklist' GET param is set
19
+ add_action( 'admin_init', 'wprss_check_if_blacklist_item' );
20
+ // Checks if the transient is set to show the notice
21
+ add_action( 'admin_init', 'wprss_check_notice_transient' );
22
+ // Register custom post type
23
+ add_action( 'init', 'wprss_blacklist_cpt' );
24
+ // Add the row actions to the targetted post type
25
+ add_filter( 'post_row_actions', 'wprss_blacklist_row_actions', 10, 1 );
26
+ // Check if deleting a blacklist item, from the GET parameter
27
+ add_action( 'admin_init', 'wprss_check_if_blacklist_delete' );
28
+ // Changes the wprss_blacklist table columns
29
+ add_filter( 'manage_wprss_blacklist_posts_columns', 'wprss_blacklist_columns');
30
+ // Prints the table data for each blacklist entry
31
+ add_action( 'manage_wprss_blacklist_posts_custom_column' , 'wprss_blacklist_table_contents', 10, 2 );
32
+ // Changes the wprss_blacklist bulk actions
33
+ add_filter('bulk_actions-edit-wprss_blacklist','wprss_blacklist_bulk_actions', 5, 1 );
34
+
35
+
36
+ /**
37
+ * Retrieves the blacklisted items.
38
+ *
39
+ * @since 4.4
40
+ * @return array An associative array of blacklisted item, each entry
41
+ * having the key as the permalink, and the value as the title.
42
+ */
43
+ function wprss_get_blacklist() {
44
+ // Get the option
45
+ $blacklist_option = get_option('wprss_blacklist');
46
+ // If the option does not exist
47
+ if ( $blacklist_option === FALSE || !is_array( $blacklist_option ) ) {
48
+ // create it
49
+ update_option( 'wprss_blacklist', array() );
50
+ $blacklist_option = array();
51
+ }
52
+ return $blacklist_option;
53
+ }
54
+
55
+
56
+ /**
57
+ * Creates a blacklist entry for the given feed item.
58
+ *
59
+ * @since 4.4
60
+ * @param int|string The ID of the feed item to add to the blacklist
61
+ */
62
+ function wprss_blacklist_item( $ID ) {
63
+ // Return if feed item is null
64
+ if ( is_null( $ID ) ) return;
65
+
66
+ // Get the feed item data
67
+ $item_title = get_the_title( $ID );
68
+ $item_permalink = get_post_meta( $ID, 'wprss_item_permalink', TRUE );
69
+ // If not an imported item, stop
70
+ if ( $item_permalink === '' ) {
71
+ wprss_log_obj( 'An item being blacklisted was ignored for not being an imported item', $ID, null, WPRSS_LOG_LEVEL_INFO );
72
+ return;
73
+ }
74
+ // Prepare the data for blacklisting
75
+ $title = apply_filters( 'wprss_blacklist_title', trim( $item_title ) );
76
+ $permalink = apply_filters( 'wprss_blacklist_permalink', trim( $item_permalink ) );
77
+
78
+ // Get the blacklisted items
79
+ $blacklist = wprss_get_blacklist();
80
+ // Add the item to the blacklist
81
+ $blacklist[ $permalink ] = $title;
82
+
83
+ // Delete the item
84
+ wp_delete_post( $ID, TRUE );
85
+
86
+ // Add the blacklisted item
87
+ $id = wp_insert_post(array(
88
+ 'post_title' => $title,
89
+ 'post_type' => 'wprss_blacklist',
90
+ 'post_status' => 'publish'
91
+ ));
92
+ update_post_meta( $id, 'wprss_permalink', $permalink );
93
+ }
94
+
95
+
96
+ /**
97
+ * Determines whether the given item is blacklist.
98
+ *
99
+ * @since 4.4
100
+ * @param string $permalink The permalink to look for in the saved option
101
+ * @return bool TRUE if the permalink is found, FALSE otherwise.
102
+ */
103
+ function wprss_is_blacklisted( $permalink ) {
104
+ // Query the blacklist entries, for an item with the given permalink
105
+ $query = new WP_Query(array(
106
+ 'post_type' => 'wprss_blacklist',
107
+ 'meta_key' => 'wprss_permalink',
108
+ 'meta_value' => $permalink
109
+ ));
110
+ // Return TRUE if the query returned a result, FALSE otherwise
111
+ return $query->have_posts();
112
+ }
113
+
114
+
115
+ /**
116
+ * Check if the 'blacklist' GET param is set, and prepare to blacklist
117
+ * the item.
118
+ *
119
+ * @since 4.4
120
+ */
121
+ function wprss_check_if_blacklist_item() {
122
+ // If the GET param is not set, do nothing. Return.
123
+ if ( empty( $_GET['wprss_blacklist'] ) ) return;
124
+
125
+ // Get the ID from the GET param
126
+ $ID = $_GET['wprss_blacklist'];
127
+ // If the post does not exist, stop. Show a message
128
+ if ( get_post($ID) === NULL ) {
129
+ wp_die( __( 'The item you are trying to blacklist does not exist', WPRSS_TEXT_DOMAIN ) );
130
+ }
131
+
132
+ // If the post type is not correct,
133
+ if ( get_post_meta( $ID, 'wprss_item_permalink', TRUE ) === '' || get_post_status( $ID ) !== 'trash' ) {
134
+ wp_die( __( 'The item you are trying to blacklist is not valid!', WPRSS_TEXT_DOMAIN ) );
135
+ }
136
+
137
+ check_admin_referer( 'blacklist-item-' . $ID, 'wprss_blacklist_item' );
138
+ wprss_blacklist_item( $ID );
139
+
140
+ // Get the current post type for the current page
141
+ $post_type = isset( $_GET['post_type'] )? $_GET['post_type'] : 'post';
142
+ // Check the current page, and generate the URL query string for the page
143
+ $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
144
+ // Set the notice transient
145
+ set_transient( 'wprss_item_blacklist_notice', 'true' );
146
+ // Refresh the page without the GET parameter
147
+ wp_redirect( admin_url( "edit.php?post_type=$post_type&post_status=trash" . $paged ) );
148
+ exit();
149
+ }
150
+
151
+
152
+ /**
153
+ * Checks if the transient for the blacklist notice is set, and shows the notice
154
+ * if it is set.
155
+ */
156
+ function wprss_check_notice_transient() {
157
+ // Check if the transient exists
158
+ $transient = get_transient( 'wprss_item_blacklist_notice' );
159
+ if ( $transient !== FALSE ) {
160
+ // Remove the transient
161
+ delete_transient( 'wprss_item_blacklist_notice' );
162
+ // Show the notice
163
+ add_action( 'admin_notices', 'wprss_blacklist_item_notice' );
164
+ }
165
+ }
166
+
167
+ /**
168
+ * The admin notice shown when an item is blacklisted.
169
+ */
170
+ function wprss_blacklist_item_notice() {
171
+ ?>
172
+ <div class="updated">
173
+ <p>
174
+ The item was deleted successfully and added to the blacklist.
175
+ </p>
176
+ </div>
177
+ <?php
178
+ }
179
+
180
+
181
+ /**
182
+ * Registers the Blacklist Custom Post Type.
183
+ *
184
+ * @since 4.4
185
+ */
186
+ function wprss_blacklist_cpt() {
187
+ register_post_type( 'wprss_blacklist', array(
188
+ 'label' => 'Blacklist',
189
+ 'public' => false,
190
+ 'exclude_from_search' => true,
191
+ 'show_ui' => true,
192
+ 'show_in_menu' => 'edit.php?post_type=wprss_feed',
193
+ 'capability_type' => 'feed_source',
194
+ 'supports' => array( 'title' ),
195
+ 'labels' => array(
196
+ 'name' => __( 'Blacklist', WPRSS_TEXT_DOMAIN ),
197
+ 'singular_name' => __( 'Blacklist', WPRSS_TEXT_DOMAIN ),
198
+ 'all_items' => __( 'Blacklist', WPRSS_TEXT_DOMAIN ),
199
+ 'search_items' => __( 'Search Blacklist', WPRSS_TEXT_DOMAIN ),
200
+ 'not_found' => __( 'You do not have any items blacklisted yet!', WPRSS_TEXT_DOMAIN ),
201
+ )
202
+ ));
203
+ }
204
+
205
+
206
+ /**
207
+ * Adds the row actions to the targetted post type.
208
+ * Default post type = wprss_feed_item
209
+ *
210
+ * @since 4.4
211
+ * @param array $actions The row actions to be filtered
212
+ * @return array The new filtered row actions
213
+ */
214
+ function wprss_blacklist_row_actions( $actions ) {
215
+ // Check the current page, and generate the URL query string for the page
216
+ $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
217
+
218
+
219
+ // Check the post type
220
+ if ( get_post_status() == 'trash' ) {
221
+ // Get the Post ID
222
+ $ID = get_the_ID();
223
+
224
+ // Get the permalink. If does not exist, then it is not an imported item.
225
+ $permalink = get_post_meta( $ID, 'wprss_item_permalink', TRUE );
226
+ if ( $permalink === '' ) {
227
+ $actions;
228
+ }
229
+
230
+ // The post type on the current screen
231
+ $post_type = get_post_type();
232
+ // Prepare the blacklist URL
233
+ $plain_url = apply_filters(
234
+ 'wprss_blacklist_row_action_url',
235
+ admin_url( "edit.php?post_type=$post_type&wprss_blacklist=$ID" ),
236
+ $ID
237
+ ) . $paged;
238
+ // Add a nonce to the URL
239
+ $nonced_url = wp_nonce_url( $plain_url, 'blacklist-item-' . $ID, 'wprss_blacklist_item' );
240
+
241
+ // Prepare the text
242
+ $text = apply_filters( 'wprss_blacklist_row_action_text', htmlentities( __( 'Delete Permanently & Blacklist', WPRSS_TEXT_DOMAIN ) ) );
243
+ $text = __( $text, WPRSS_TEXT_DOMAIN );
244
+
245
+ // Prepare the hint
246
+ $hint = apply_filters(
247
+ 'wprss_blacklist_row_action_hint',
248
+ __( 'The item will be deleted permanently, and its permalink will be recorded in the blacklist', WPRSS_TEXT_DOMAIN )
249
+ );
250
+ $hint = esc_attr( __( $hint, WPRSS_TEXT_DOMAIN ) );
251
+
252
+ // Add the blacklist action
253
+ $actions['blacklist-item'] = "<span class='delete'><a title='$hint' href='$nonced_url'>$text</a></span>";
254
+ }
255
+
256
+ // For the blacklisted item
257
+ elseif ( get_post_type() === 'wprss_blacklist' ) {
258
+ $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
259
+ $remove_url = wp_nonce_url( 'post.php?wprss-blacklist-remove='.get_the_ID(), 'blacklist-remove-' . get_the_ID(), 'wprss_blacklist_trash' );
260
+ $actions = array(
261
+ 'trash' => '<a href="'.$remove_url.'">' . __( 'Remove from blacklist', WPRSS_TEXT_DOMAIN ) . '</a>'
262
+ );
263
+ }
264
+
265
+ // Return the actions
266
+ return $actions;
267
+ }
268
+
269
+
270
+ /**
271
+ * Checks for the GET parameter wprss-blacklist-remove, and if present,
272
+ * deletes the appropriate blacklist entry. Uses nonce 'wprss_blacklist_trash'
273
+ * with action 'blacklist-remove-$ID'
274
+ *
275
+ * @since 4.4
276
+ */
277
+ function wprss_check_if_blacklist_delete() {
278
+ // If the GET param is not set, do nothing. Return.
279
+ if ( empty( $_GET['wprss-blacklist-remove'] ) ) return;
280
+
281
+ // The array of blacklist entries to delete
282
+ $to_delete = array();
283
+ // The ID of the blacklist entry - if only deleting a single entry
284
+ $ID = $_GET['wprss-blacklist-remove'];
285
+
286
+ // check if deleting in bulk
287
+ if ( isset( $_GET['wprss-bulk'] ) && $_GET['wprss-bulk'] == '1' ) {
288
+ $to_delete = explode( ',', $ID );
289
+ check_admin_referer( 'blacklist-remove-selected', 'wprss_blacklist_trash' );
290
+ } else {
291
+ $to_delete = array( $ID );
292
+ // Get the ID from the GET param
293
+ // Verify the nonce
294
+ check_admin_referer( 'blacklist-remove-' . $ID, 'wprss_blacklist_trash' );
295
+ }
296
+
297
+ // Delete the posts marked for delete
298
+ foreach( $to_delete as $delete_id ) {
299
+ $post = get_post( $delete_id );
300
+ if ( $post === NULL || get_post_type( $post ) !== 'wprss_blacklist' ) continue;
301
+ wp_delete_post( $delete_id, TRUE );
302
+ }
303
+
304
+ // Redirect back to blacklists page
305
+ $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
306
+ header('Location: ' . admin_url('edit.php?post_type=wprss_blacklist' . $paged ) );
307
+ exit;
308
+ }
309
+
310
+
311
+ /**
312
+ * Returns the custom columns for the blacklist post type
313
+ *
314
+ * @since 4.4
315
+ * @params array $cols The columns to filter
316
+ * @return array The new columns
317
+ */
318
+ function wprss_blacklist_columns( $cols ) {
319
+ return array(
320
+ 'cb' => $cols['cb'],
321
+ 'title' => __( 'Title' ),
322
+ 'permalink' => __( 'Permalink' )
323
+ );
324
+ }
325
+
326
+
327
+ /**
328
+ * Prints the cell data in the table for each blacklist entry
329
+ *
330
+ * @since 4.4
331
+ * @param string $column The column slug
332
+ * @param string|int $ID The ID of the post currently being printed
333
+ */
334
+ function wprss_blacklist_table_contents( $column, $ID ) {
335
+ switch ( $column ) {
336
+ case 'permalink':
337
+ $permalink = get_post_meta( $ID, 'wprss_permalink', TRUE );
338
+ echo '<a href="'.$permalink.'" target="_blank">'.$permalink.'</a>';
339
+ break;
340
+ }
341
+ }
342
+
343
+
344
+ /**
345
+ * Removes the bulk actions for the Blacklist post type
346
+ *
347
+ * @since 4.4
348
+ * @param array $actions The array of actions to be filtered
349
+ * @return array An empty array
350
+ */
351
+ function wprss_blacklist_bulk_actions( $actions ) {
352
+ return array();
353
  }
includes/feed-display.php CHANGED
@@ -1,509 +1,509 @@
1
- <?php
2
- /**
3
- * Feed display related functions
4
- *
5
- * @package WPRSSAggregator
6
- */
7
-
8
-
9
- add_filter( 'the_content', 'wprss_render_feed_view' );
10
- /**
11
- * Display template for a feed source. Simulates a shortcode call.
12
- *
13
- * @since 4.6.6
14
- */
15
- function wprss_render_feed_view( $content ) {
16
- if ( get_post_type() === 'wprss_feed' && ! is_feed() ) {
17
- $content = wprss_shortcode( array(
18
- 'source' => get_the_ID()
19
- ) );
20
- }
21
- return $content;
22
- }
23
-
24
-
25
- add_filter( 'the_content', 'wprss_render_feed_item_view' );
26
- /**
27
- * Display template for a feed source. Simulates a shortcode call.
28
- *
29
- * @since 4.6.6
30
- */
31
- function wprss_render_feed_item_view( $content ) {
32
- if ( get_post_type() === 'wprss_feed_item' && ! is_feed() ) {
33
- $content = wprss_shortcode_single( array(
34
- 'id' => get_the_ID()
35
- ) );
36
- }
37
- return $content;
38
- }
39
-
40
-
41
- /**
42
- * Renders a single feed item.
43
- *
44
- * @param int $ID The ID of the feed item to render
45
- * @param string $default The default text to return if something fails.
46
- * @return string The output
47
- * @since 4.6.6
48
- */
49
- function wprss_render_feed_item( $ID = NULL, $default = '', $args = array() ) {
50
- $ID = ( $ID === NULL )? get_the_ID() : $ID;
51
- if ( get_post_type( $ID ) !== 'wprss_feed_item' || is_feed() ) return $default;
52
-
53
- // Prepare the options
54
- $general_settings = get_option( 'wprss_settings_general' );
55
- $display_settings = wprss_get_display_settings( $general_settings );
56
- $excerpts_settings = get_option( 'wprss_settings_excerpts' );
57
- $thumbnails_settings = get_option( 'wprss_settings_thumbnails' );
58
-
59
- $extra_options = apply_filters( 'wprss_template_extra_options', array(), $args);
60
-
61
- // Normalize the source_link option
62
- $source_link = isset( $general_settings['source_link'] )? $general_settings['source_link'] : 0;
63
-
64
- // Declare each item in $args as its own variable
65
- extract( $args, EXTR_SKIP );
66
-
67
- // Get the item meta
68
- $permalink = get_post_meta( $ID, 'wprss_item_permalink', true );
69
- $enclosure = get_post_meta( $ID, 'wprss_item_enclosure', true );
70
- $feed_source_id = get_post_meta( $ID, 'wprss_feed_id', true );
71
- $link_enclosure = get_post_meta( $feed_source_id, 'wprss_enclosure', true );
72
- $source_name = get_the_title( $feed_source_id );
73
- $source_url = get_post_meta( $feed_source_id, 'wprss_site_url', true );
74
- $timestamp = get_the_time( 'U', $ID );
75
-
76
- // Fallback for feeds created with older versions of the plugin
77
- if ( $source_url === '' ) $source_url = get_post_meta( $feed_source_id, 'wprss_url', true );
78
- // convert from Unix timestamp
79
- $date = wprss_date_i18n( $timestamp );
80
-
81
- // Prepare the title
82
- $feed_item_title = get_the_title();
83
- $feed_item_title_link = ( $link_enclosure === 'true' && $enclosure !== '' )? $enclosure : $permalink;
84
-
85
- // Prepare the text that precedes the source
86
- $text_preceding_source = wprss_get_general_setting('text_preceding_source');
87
- $text_preceding_source = ltrim( __( $text_preceding_source, WPRSS_TEXT_DOMAIN ) . ' ' );
88
-
89
- $text_preceding_date = wprss_get_general_setting('text_preceding_date');
90
- $text_preceding_date = ltrim( __( $text_preceding_date, WPRSS_TEXT_DOMAIN ) . ' ' );
91
-
92
- do_action( 'wprss_get_post_data' );
93
-
94
- $meta = $extra_options;
95
- $extra_meta = apply_filters( 'wprss_template_extra_meta', $meta, $args, $ID );
96
-
97
- ///////////////////////////////////////////////////////////////
98
- // BEGIN TEMPLATE
99
-
100
- // Prepare the output
101
- $output = '';
102
- // Begin output buffering
103
- ob_start();
104
- // Print the links before
105
- echo $link_before;
106
-
107
- // The Title
108
- $item_title = wprss_link_display( $feed_item_title_link, $feed_item_title, wprss_get_general_setting('title_link') );
109
- $item_title = apply_filters('wprss_item_title', $item_title, $feed_item_title_link, $feed_item_title, wprss_get_general_setting('title_link'));
110
- echo $item_title;
111
-
112
- do_action( 'wprss_after_feed_item_title', $extra_meta, $display_settings, $ID );
113
-
114
- // FEED ITEM META
115
- echo '<div class="wprss-feed-meta">';
116
-
117
- // SOURCE
118
- if ( wprss_get_general_setting('source_enable') == 1 ) {
119
- echo '<span class="feed-source">';
120
- $source_link_text = apply_filters('wprss_item_source_link', wprss_link_display( $source_url, $source_name, $source_link ) );
121
- $source_link_text = $text_preceding_source . $source_link_text;
122
- echo $source_link_text;
123
- echo '</span>';
124
- }
125
-
126
- // DATE
127
- if ( wprss_get_general_setting('date_enable') == 1 ) {
128
- echo '<span class="feed-date">';
129
- $date_text = apply_filters( 'wprss_item_date', $date );
130
- $date_text = $text_preceding_date . $date_text;
131
- echo $date_text;
132
- echo '</span>';
133
- }
134
-
135
- // AUTHOR
136
- $author = get_post_meta( $ID, 'wprss_item_author', TRUE );
137
- if ( wprss_get_general_setting('authors_enable') == 1 && $author !== NULL && is_string( $author ) && $author !== '' ) {
138
- echo '<span class="feed-author">';
139
- $author_text = apply_filters( 'wprss_item_author', $author );
140
- $author_prefix_text = apply_filters( 'wprss_author_prefix_text', 'By' );
141
- _e( $author_prefix_text, WPRSS_TEXT_DOMAIN );
142
- echo ' ' . $author_text;
143
- echo '</span>';
144
- }
145
-
146
- echo '</div>';
147
-
148
- // TIME AGO
149
- if ( wprss_get_general_setting('date_enable') == 1 && wprss_get_general_setting('time_ago_format_enable') == 1 ) {
150
- $time_ago = human_time_diff( $timestamp, time() );
151
- echo '<div class="wprss-time-ago">';
152
- $time_ago_text = apply_filters( 'wprss_item_time_ago', $time_ago );
153
- printf( __( '%1$s ago', WPRSS_TEXT_DOMAIN ), $time_ago_text );
154
- echo '</div>';
155
- }
156
-
157
- // END TEMPLATE - Retrieve buffered output
158
- $output .= ob_get_clean();
159
- $output = apply_filters( 'wprss_single_feed_output', $output, $permalink );
160
- $output .= "$link_after";
161
-
162
- // Print the output
163
- return $output;
164
- }
165
-
166
-
167
- /**
168
- * Retrieve settings and prepare them for use in the display function
169
- *
170
- * @since 3.0
171
- */
172
- function wprss_get_display_settings( $settings = NULL ) {
173
- if ( $settings === NULL ) {
174
- $settings = get_option( 'wprss_settings_general' );
175
- }
176
- // Parse the arguments together with their default values
177
- $args = wp_parse_args(
178
- $settings,
179
- array(
180
- 'open_dd' => 'New Window',
181
- 'follow_dd' => '',
182
- )
183
- );
184
-
185
- // Prepare the 'open' setting - how to open links for feed items
186
- $open = '';
187
- switch ( $args['open_dd'] ) {
188
- case 'Lightbox' :
189
- $open = 'class="colorbox"';
190
- break;
191
- case 'New window' :
192
- $open = 'target="_blank"';
193
- break;
194
- }
195
-
196
- // Prepare the 'follow' setting - whether links marked as nofollow or not
197
- $follow = ( $args['follow_dd'] == 'no_follow' )? 'rel="nofollow"' : '';
198
-
199
- // Prepare the final settings array
200
- $display_settings = array(
201
- 'open' => $open,
202
- 'follow' => $follow
203
- );
204
-
205
- do_action( 'wprss_get_settings' );
206
-
207
- return $display_settings;
208
- }
209
-
210
-
211
- /**
212
- * Merges the default arguments with the user set arguments
213
- *
214
- * @since 3.0
215
- */
216
- function wprss_get_shortcode_default_args( $args ) {
217
- // Default shortcode/function arguments for displaying feed items
218
- $shortcode_args = apply_filters(
219
- 'wprss_shortcode_args',
220
- array(
221
- 'links_before' => '<ul class="rss-aggregator">',
222
- 'links_after' => '</ul>',
223
- 'link_before' => '<li class="feed-item">',
224
- 'link_after' => '</li>'
225
- )
226
- );
227
-
228
- // Parse incoming $args into an array and merge it with $shortcode_args
229
- $args = wp_parse_args( $args, $shortcode_args );
230
-
231
- return $args;
232
- }
233
-
234
-
235
- /**
236
- * Prepares and builds the query for fetching the feed items
237
- *
238
- * @since 3.0
239
- */
240
- function wprss_get_feed_items_query( $settings ) {
241
- if( isset( $settings['feed_limit'] ) ) {
242
- $posts_per_page = $settings['feed_limit'];
243
- } else {
244
- $posts_per_page = wprss_get_general_setting('feed_limit');
245
- }
246
- global $paged;
247
- if ( get_query_var('paged') ) {
248
- $paged = get_query_var('paged');
249
- } elseif ( get_query_var('page') ) {
250
- $paged = get_query_var('page');
251
- } else {
252
- $paged = 1;
253
- }
254
-
255
- $feed_items_args = array(
256
- 'post_type' => 'wprss_feed_item',
257
- 'posts_per_page' => $posts_per_page,
258
- 'orderby' => 'date',
259
- 'order' => 'DESC',
260
- 'paged' => $paged,
261
- 'suppress_filters' => true
262
- );
263
-
264
- if ( isset($settings['pagination']) ) {
265
- $pagination = strtolower( $settings['pagination'] );
266
- if ( in_array( $pagination, array('false','off','0') ) ) {
267
- unset( $feed_items_args['paged'] );
268
- }
269
- }
270
-
271
- if ( isset( $settings['no-paged'] ) && $settings['no-paged'] === TRUE ) {
272
- unset( $feed_items_args['no-paged'] );
273
- }
274
-
275
- // If either the source or exclude arguments are set (but not both), prepare a meta query
276
- if ( isset( $settings['source'] ) xor isset( $settings['exclude'] ) ) {
277
- // Set the appropriate setting and operator
278
- $setting = 'source';
279
- $operator = 'IN';
280
- if ( isset( $settings['exclude'] ) ) {
281
- $setting = 'exclude';
282
- $operator = 'NOT IN';
283
- }
284
- $feeds = array_filter( array_map( 'intval', explode( ',', $settings[$setting] ) ) );
285
- foreach ( $feeds as $feed )
286
- trim( $feed );
287
- if ( !empty( $feeds ) ) {
288
- $feed_items_args['meta_query'] = array(
289
- array(
290
- 'key' => 'wprss_feed_id',
291
- 'value' => $feeds,
292
- 'type' => 'numeric',
293
- 'compare' => $operator,
294
- ),
295
- );
296
- }
297
- }
298
-
299
- // Arguments for the next query to fetch all feed items
300
- $feed_items_args = apply_filters( 'wprss_display_feed_items_query', $feed_items_args, $settings );
301
-
302
- // Query to get all feed items for display
303
- $feed_items = new WP_Query( $feed_items_args );
304
-
305
- if ( isset( $settings['get-args'] ) && $settings['get-args'] === TRUE ) {
306
- return $feed_items_args;
307
- } else return $feed_items;
308
- }
309
-
310
-
311
- add_action( 'wprss_display_template', 'wprss_default_display_template', 10, 3 );
312
- /**
313
- * Default template for feed items display
314
- *
315
- * @since 3.0
316
- * @param $args array The shortcode arguments
317
- * @param $feed_items WP_Query The feed items to display
318
- */
319
- function wprss_default_display_template( $args, $feed_items ) {
320
- global $wp_query;
321
- global $paged;
322
-
323
- // Swap the current WordPress Query with our own
324
- $old_wp_query = $wp_query;
325
- $wp_query = $feed_items;
326
-
327
- // Prepare the output
328
- $output = '';
329
-
330
- // Check if our current query returned any feed items
331
- if ( $feed_items->have_posts() ) {
332
- // PRINT LINKS BEFORE LIST OF FEED ITEMS
333
- $output .= $args['links_before'];
334
-
335
- // FOR EACH ITEM
336
- while ( $feed_items->have_posts() ) {
337
- // Get the item
338
- $feed_items->the_post();
339
- // Add the output
340
- $output .= wprss_render_feed_item( NULL, '', $args );
341
- }
342
-
343
- // OUTPUT LINKS AFTER LIST OF FEED ITEMS
344
- $output .= $args['links_after'];
345
-
346
- // Add pagination if needed
347
- if ( !isset( $args['pagination'] ) || !in_array( $args['pagination'], array('off','false','0',0) ) ) {
348
- $output = apply_filters( 'wprss_pagination', $output );
349
- }
350
-
351
- // Filter the final output, and print it
352
- echo apply_filters( 'feed_output', $output );
353
- } else {
354
- // No items found message
355
- echo apply_filters( 'no_feed_items_found', __( 'No feed items found.', WPRSS_TEXT_DOMAIN ) );
356
- }
357
-
358
- // Reset the WordPress query
359
- $wp_query = $old_wp_query;
360
- wp_reset_postdata();
361
- }
362
-
363
-
364
- /**
365
- * Generates an HTML link, using the saved display settings.
366
- *
367
- * @param string $link The link URL
368
- * @param string $text The link text to display
369
- * @param string $bool Optional boolean. If FALSE, the text is returned unlinked. Default: TRUE.
370
- * @return string The generated link
371
- * @since 4.2.4
372
- */
373
- function wprss_link_display( $link, $text, $bool = TRUE ) {
374
- $display_settings = wprss_get_display_settings( get_option( 'wprss_settings_general' ) );
375
- $a = $bool ? "<a {$display_settings['open']} {$display_settings['follow']} href='$link'>$text</a>" : $text;
376
- return $a;
377
- }
378
-
379
-
380
- add_filter( 'wprss_pagination', 'wprss_pagination_links' );
381
- /**
382
- * Display pagination links
383
- *
384
- * @since 3.5
385
- */
386
- function wprss_pagination_links( $output ) {
387
- // Get the general setting
388
- $pagination = wprss_get_general_setting( 'pagination' );;
389
-
390
- // Check the pagination setting, if using page numbers
391
- if ( $pagination === 'numbered' ) {
392
- global $wp_query;
393
- $big = 999999999; // need an unlikely integer
394
- $output .= paginate_links( array(
395
- 'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
396
- 'format' => '?paged=%#%',
397
- 'current' => max( 1, get_query_var('paged') ),
398
- 'total' => $wp_query->max_num_pages
399
- ) );
400
- return $output;
401
- }
402
- // Otherwise, using default paginations
403
- else {
404
- $output .= '<div class="nav-links">';
405
- $output .= ' <div class="nav-previous alignleft">' . get_next_posts_link( __( 'Older posts', WPRSS_TEXT_DOMAIN ) ) . '</div>';
406
- $output .= ' <div class="nav-next alignright">' . get_previous_posts_link( __( 'Newer posts', WPRSS_TEXT_DOMAIN ) ) . '</div>';
407
- $output .= '</div>';
408
- return $output;
409
- }
410
- }
411
-
412
-
413
- add_filter( 'the_title', 'wprss_shorten_title', 10, 2 );
414
- /**
415
- * Checks the title limit option and shortens the title when necassary.
416
- *
417
- * @since 1.0
418
- */
419
- function wprss_shorten_title( $title, $id = null ) {
420
- if ( $id === null ) return $title;
421
- // Get the option. If does not exist, use 0, which is ignored.
422
- $general_settings = get_option( 'wprss_settings_general' );
423
- $title_limit = isset( $general_settings['title_limit'] )? intval( $general_settings['title_limit'] ) : 0;
424
- // Check if the title is for a wprss_feed_item, and check if trimming is needed
425
- if ( isset( $id ) && get_post_type( $id ) === 'wprss_feed_item' && $title_limit > 0 && strlen( $title ) > $title_limit ) {
426
- // Return the trimmed version of the title
427
- return substr( $title, 0, $title_limit ) . apply_filters( 'wprss_shortened_title_ending', '...' );
428
- }
429
- // Otherwise, return the same title
430
- return $title;
431
- }
432
-
433
-
434
- /**
435
- * Display feed items on the front end (via shortcode or function)
436
- *
437
- * @since 2.0
438
- */
439
- function wprss_display_feed_items( $args = array() ) {
440
- $settings = get_option( 'wprss_settings_general' );
441
- $args = wprss_get_shortcode_default_args( $args );
442
-
443
- $args = apply_filters( 'wprss_shortcode_args', $args );
444
-
445
- $query_args = $settings;
446
- if ( isset( $args['limit'] ) ) {
447
- $query_args['feed_limit'] = filter_var( $args['limit'], FILTER_VALIDATE_INT, array(
448
- 'options' => array(
449
- 'min_range' => 1,
450
- 'default' => $query_args['feed_limit'],
451
- ),
452
- ) );
453
- }
454
-
455
- if ( isset( $args['pagination'] ) ) {
456
- $query_args['pagination'] = $args['pagination'];
457
- }
458
-
459
- if ( isset( $args['source'] ) ) {
460
- $query_args['source'] = $args['source'];
461
- }
462
- elseif ( isset( $args['exclude'] ) ) {
463
- $query_args['exclude'] = $args['exclude'];
464
- }
465
-
466
- $query_args = apply_filters( 'wprss_process_shortcode_args', $query_args, $args );
467
-
468
- $feed_items = wprss_get_feed_items_query( $query_args );
469
-
470
- do_action( 'wprss_display_template', $args, $feed_items );
471
- }
472
-
473
-
474
- /**
475
- * Redirects to wprss_display_feed_items
476
- * It is used for backwards compatibility to versions < 2.0
477
- *
478
- * @since 2.1
479
- */
480
- function wp_rss_aggregator( $args = array() ) {
481
- wprss_display_feed_items( $args );
482
- }
483
-
484
-
485
- /**
486
- * Limits a phrase/content to a defined number of words
487
- *
488
- * NOT BEING USED as we're using the native WP function, although the native one strips tags, so I'll
489
- * probably revisit this one again soon.
490
- *
491
- * @since 3.0
492
- * @param string $words
493
- * @param integer $limit
494
- * @param string $append
495
- * @return string
496
- */
497
- function wprss_limit_words( $words, $limit, $append = '' ) {
498
- /* Add 1 to the specified limit becuase arrays start at 0 */
499
- $limit = $limit + 1;
500
- /* Store each individual word as an array element
501
- up to the limit */
502
- $words = explode( ' ', $words, $limit );
503
- /* Shorten the array by 1 because that final element will be the sum of all the words after the limit */
504
- array_pop( $words );
505
- /* Implode the array for output, and append an ellipse */
506
- $words = implode( ' ', $words ) . $append;
507
- /* Return the result */
508
- return rtrim( $words );
509
- }
1
+ <?php
2
+ /**
3
+ * Feed display related functions
4
+ *
5
+ * @package WPRSSAggregator
6
+ */
7
+
8
+
9
+ add_filter( 'the_content', 'wprss_render_feed_view' );
10
+ /**
11
+ * Display template for a feed source. Simulates a shortcode call.
12
+ *
13
+ * @since 4.6.6
14
+ */
15
+ function wprss_render_feed_view( $content ) {
16
+ if ( get_post_type() === 'wprss_feed' && ! is_feed() ) {
17
+ $content = wprss_shortcode( array(
18
+ 'source' => get_the_ID()
19
+ ) );
20
+ }
21
+ return $content;
22
+ }
23
+
24
+
25
+ add_filter( 'the_content', 'wprss_render_feed_item_view' );
26
+ /**
27
+ * Display template for a feed source. Simulates a shortcode call.
28
+ *
29
+ * @since 4.6.6
30
+ */
31
+ function wprss_render_feed_item_view( $content ) {
32
+ if ( get_post_type() === 'wprss_feed_item' && ! is_feed() ) {
33
+ $content = wprss_shortcode_single( array(
34
+ 'id' => get_the_ID()
35
+ ) );
36
+ }
37
+ return $content;
38
+ }
39
+
40
+
41
+ /**
42
+ * Renders a single feed item.
43
+ *
44
+ * @param int $ID The ID of the feed item to render
45
+ * @param string $default The default text to return if something fails.
46
+ * @return string The output
47
+ * @since 4.6.6
48
+ */
49
+ function wprss_render_feed_item( $ID = NULL, $default = '', $args = array() ) {
50
+ $ID = ( $ID === NULL )? get_the_ID() : $ID;
51
+ if ( get_post_type( $ID ) !== 'wprss_feed_item' || is_feed() ) return $default;
52
+
53
+ // Prepare the options
54
+ $general_settings = get_option( 'wprss_settings_general' );
55
+ $display_settings = wprss_get_display_settings( $general_settings );
56
+ $excerpts_settings = get_option( 'wprss_settings_excerpts' );
57
+ $thumbnails_settings = get_option( 'wprss_settings_thumbnails' );
58
+
59
+ $extra_options = apply_filters( 'wprss_template_extra_options', array(), $args);
60
+
61
+ // Normalize the source_link option
62
+ $source_link = isset( $general_settings['source_link'] )? $general_settings['source_link'] : 0;
63
+
64
+ // Declare each item in $args as its own variable
65
+ extract( $args, EXTR_SKIP );
66
+
67
+ // Get the item meta
68
+ $permalink = get_post_meta( $ID, 'wprss_item_permalink', true );
69
+ $enclosure = get_post_meta( $ID, 'wprss_item_enclosure', true );
70
+ $feed_source_id = get_post_meta( $ID, 'wprss_feed_id', true );
71
+ $link_enclosure = get_post_meta( $feed_source_id, 'wprss_enclosure', true );
72
+ $source_name = get_the_title( $feed_source_id );
73
+ $source_url = get_post_meta( $feed_source_id, 'wprss_site_url', true );
74
+ $timestamp = get_the_time( 'U', $ID );
75
+
76
+ // Fallback for feeds created with older versions of the plugin
77
+ if ( $source_url === '' ) $source_url = get_post_meta( $feed_source_id, 'wprss_url', true );
78
+ // convert from Unix timestamp
79
+ $date = wprss_date_i18n( $timestamp );
80
+
81
+ // Prepare the title
82
+ $feed_item_title = get_the_title();
83
+ $feed_item_title_link = ( $link_enclosure === 'true' && $enclosure !== '' )? $enclosure : $permalink;
84
+
85
+ // Prepare the text that precedes the source
86
+ $text_preceding_source = wprss_get_general_setting('text_preceding_source');
87
+ $text_preceding_source = ltrim( __( $text_preceding_source, WPRSS_TEXT_DOMAIN ) . ' ' );
88
+
89
+ $text_preceding_date = wprss_get_general_setting('text_preceding_date');
90
+ $text_preceding_date = ltrim( __( $text_preceding_date, WPRSS_TEXT_DOMAIN ) . ' ' );
91
+
92
+ do_action( 'wprss_get_post_data' );
93
+
94
+ $meta = $extra_options;
95
+ $extra_meta = apply_filters( 'wprss_template_extra_meta', $meta, $args, $ID );
96
+
97
+ ///////////////////////////////////////////////////////////////
98
+ // BEGIN TEMPLATE
99
+
100
+ // Prepare the output
101
+ $output = '';
102
+ // Begin output buffering
103
+ ob_start();
104
+ // Print the links before
105
+ echo $link_before;
106
+
107
+ // The Title
108
+ $item_title = wprss_link_display( $feed_item_title_link, $feed_item_title, wprss_get_general_setting('title_link') );
109
+ $item_title = apply_filters('wprss_item_title', $item_title, $feed_item_title_link, $feed_item_title, wprss_get_general_setting('title_link'));
110
+ echo $item_title;
111
+
112
+ do_action( 'wprss_after_feed_item_title', $extra_meta, $display_settings, $ID );
113
+
114
+ // FEED ITEM META
115
+ echo '<div class="wprss-feed-meta">';
116
+
117
+ // SOURCE
118
+ if ( wprss_get_general_setting('source_enable') == 1 ) {
119
+ echo '<span class="feed-source">';
120
+ $source_link_text = apply_filters('wprss_item_source_link', wprss_link_display( $source_url, $source_name, $source_link ) );
121
+ $source_link_text = $text_preceding_source . $source_link_text;
122
+ echo $source_link_text;
123
+ echo '</span>';
124
+ }
125
+
126
+ // DATE
127
+ if ( wprss_get_general_setting('date_enable') == 1 ) {
128
+ echo '<span class="feed-date">';
129
+ $date_text = apply_filters( 'wprss_item_date', $date );
130
+ $date_text = $text_preceding_date . $date_text;
131
+ echo $date_text;
132
+ echo '</span>';
133
+ }
134
+
135
+ // AUTHOR
136
+ $author = get_post_meta( $ID, 'wprss_item_author', TRUE );
137
+ if ( wprss_get_general_setting('authors_enable') == 1 && $author !== NULL && is_string( $author ) && $author !== '' ) {
138
+ echo '<span class="feed-author">';
139
+ $author_text = apply_filters( 'wprss_item_author', $author );
140
+ $author_prefix_text = apply_filters( 'wprss_author_prefix_text', 'By' );
141
+ _e( $author_prefix_text, WPRSS_TEXT_DOMAIN );
142
+ echo ' ' . $author_text;
143
+ echo '</span>';
144
+ }
145
+
146
+ echo '</div>';
147
+
148
+ // TIME AGO
149
+ if ( wprss_get_general_setting('date_enable') == 1 && wprss_get_general_setting('time_ago_format_enable') == 1 ) {
150
+ $time_ago = human_time_diff( $timestamp, time() );
151
+ echo '<div class="wprss-time-ago">';
152
+ $time_ago_text = apply_filters( 'wprss_item_time_ago', $time_ago );
153
+ printf( __( '%1$s ago', WPRSS_TEXT_DOMAIN ), $time_ago_text );
154
+ echo '</div>';
155
+ }
156
+
157
+ // END TEMPLATE - Retrieve buffered output
158
+ $output .= ob_get_clean();
159
+ $output = apply_filters( 'wprss_single_feed_output', $output, $permalink );
160
+ $output .= "$link_after";
161
+
162
+ // Print the output
163
+ return $output;
164
+ }
165
+
166
+
167
+ /**
168
+ * Retrieve settings and prepare them for use in the display function
169
+ *
170
+ * @since 3.0
171
+ */
172
+ function wprss_get_display_settings( $settings = NULL ) {
173
+ if ( $settings === NULL ) {
174
+ $settings = get_option( 'wprss_settings_general' );
175
+ }
176
+ // Parse the arguments together with their default values
177
+ $args = wp_parse_args(
178
+ $settings,
179
+ array(
180
+ 'open_dd' => 'New Window',
181
+ 'follow_dd' => '',
182
+ )
183
+ );
184
+
185
+ // Prepare the 'open' setting - how to open links for feed items
186
+ $open = '';
187
+ switch ( $args['open_dd'] ) {
188
+ case 'Lightbox' :
189
+ $open = 'class="colorbox"';
190
+ break;
191
+ case 'New window' :
192
+ $open = 'target="_blank"';
193
+ break;
194
+ }
195
+
196
+ // Prepare the 'follow' setting - whether links marked as nofollow or not
197
+ $follow = ( $args['follow_dd'] == 'no_follow' )? 'rel="nofollow"' : '';
198
+
199
+ // Prepare the final settings array
200
+ $display_settings = array(
201
+ 'open' => $open,
202
+ 'follow' => $follow
203
+ );
204
+
205
+ do_action( 'wprss_get_settings' );
206
+
207
+ return $display_settings;
208
+ }
209
+
210
+
211
+ /**
212
+ * Merges the default arguments with the user set arguments
213
+ *
214
+ * @since 3.0
215
+ */
216
+ function wprss_get_shortcode_default_args( $args ) {
217
+ // Default shortcode/function arguments for displaying feed items
218
+ $shortcode_args = apply_filters(
219
+ 'wprss_shortcode_args',
220
+ array(
221
+ 'links_before' => '<ul class="rss-aggregator">',
222
+ 'links_after' => '</ul>',
223
+ 'link_before' => '<li class="feed-item">',
224
+ 'link_after' => '</li>'
225
+ )
226
+ );
227
+
228
+ // Parse incoming $args into an array and merge it with $shortcode_args
229
+ $args = wp_parse_args( $args, $shortcode_args );
230
+
231
+ return $args;
232
+ }
233
+
234
+
235
+ /**
236
+ * Prepares and builds the query for fetching the feed items
237
+ *
238
+ * @since 3.0
239
+ */
240
+ function wprss_get_feed_items_query( $settings ) {
241
+ if( isset( $settings['feed_limit'] ) ) {
242
+ $posts_per_page = $settings['feed_limit'];
243
+ } else {
244
+ $posts_per_page = wprss_get_general_setting('feed_limit');
245
+ }
246
+ global $paged;
247
+ if ( get_query_var('paged') ) {
248
+ $paged = get_query_var('paged');
249
+ } elseif ( get_query_var('page') ) {
250
+ $paged = get_query_var('page');
251
+ } else {
252
+ $paged = 1;
253
+ }
254
+
255
+ $feed_items_args = array(
256
+ 'post_type' => 'wprss_feed_item',
257
+ 'posts_per_page' => $posts_per_page,
258
+ 'orderby' => 'date',
259
+ 'order' => 'DESC',
260
+ 'paged' => $paged,
261
+ 'suppress_filters' => true
262
+ );
263
+
264
+ if ( isset($settings['pagination']) ) {
265
+ $pagination = strtolower( $settings['pagination'] );
266
+ if ( in_array( $pagination, array('false','off','0') ) ) {
267
+ unset( $feed_items_args['paged'] );
268
+ }
269
+ }
270
+
271
+ if ( isset( $settings['no-paged'] ) && $settings['no-paged'] === TRUE ) {
272
+ unset( $feed_items_args['no-paged'] );
273
+ }
274
+
275
+ // If either the source or exclude arguments are set (but not both), prepare a meta query
276
+ if ( isset( $settings['source'] ) xor isset( $settings['exclude'] ) ) {
277
+ // Set the appropriate setting and operator
278
+ $setting = 'source';
279
+ $operator = 'IN';
280
+ if ( isset( $settings['exclude'] ) ) {
281
+ $setting = 'exclude';
282
+ $operator = 'NOT IN';
283
+ }
284
+ $feeds = array_filter( array_map( 'intval', explode( ',', $settings[$setting] ) ) );
285
+ foreach ( $feeds as $feed )
286
+ trim( $feed );
287
+ if ( !empty( $feeds ) ) {
288
+ $feed_items_args['meta_query'] = array(
289
+ array(
290
+ 'key' => 'wprss_feed_id',
291
+ 'value' => $feeds,
292
+ 'type' => 'numeric',
293
+ 'compare' => $operator,
294
+ ),
295
+ );
296
+ }
297
+ }
298
+
299
+ // Arguments for the next query to fetch all feed items
300
+ $feed_items_args = apply_filters( 'wprss_display_feed_items_query', $feed_items_args, $settings );
301
+
302
+ // Query to get all feed items for display
303
+ $feed_items = new WP_Query( $feed_items_args );
304
+
305
+ if ( isset( $settings['get-args'] ) && $settings['get-args'] === TRUE ) {
306
+ return $feed_items_args;
307
+ } else return $feed_items;
308
+ }
309
+
310
+
311
+ add_action( 'wprss_display_template', 'wprss_default_display_template', 10, 3 );
312
+ /**
313
+ * Default template for feed items display
314
+ *
315
+ * @since 3.0
316
+ * @param $args array The shortcode arguments
317
+ * @param $feed_items WP_Query The feed items to display
318
+ */
319
+ function wprss_default_display_template( $args, $feed_items ) {
320
+ global $wp_query;
321
+ global $paged;
322
+
323
+ // Swap the current WordPress Query with our own
324
+ $old_wp_query = $wp_query;
325
+ $wp_query = $feed_items;
326
+
327
+ // Prepare the output
328
+ $output = '';
329
+
330
+ // Check if our current query returned any feed items
331
+ if ( $feed_items->have_posts() ) {
332
+ // PRINT LINKS BEFORE LIST OF FEED ITEMS
333
+ $output .= $args['links_before'];
334
+
335
+ // FOR EACH ITEM
336
+ while ( $feed_items->have_posts() ) {
337
+ // Get the item
338
+ $feed_items->the_post();
339
+ // Add the output
340
+ $output .= wprss_render_feed_item( NULL, '', $args );
341
+ }
342
+
343
+ // OUTPUT LINKS AFTER LIST OF FEED ITEMS
344
+ $output .= $args['links_after'];
345
+
346
+ // Add pagination if needed
347
+ if ( !isset( $args['pagination'] ) || !in_array( $args['pagination'], array('off','false','0',0) ) ) {
348
+ $output = apply_filters( 'wprss_pagination', $output );
349
+ }
350
+
351
+ // Filter the final output, and print it
352
+ echo apply_filters( 'feed_output', $output );
353
+ } else {
354
+ // No items found message
355
+ echo apply_filters( 'no_feed_items_found', __( 'No feed items found.', WPRSS_TEXT_DOMAIN ) );
356
+ }
357
+
358
+ // Reset the WordPress query
359
+ $wp_query = $old_wp_query;
360
+ wp_reset_postdata();
361
+ }
362
+
363
+
364
+ /**
365
+ * Generates an HTML link, using the saved display settings.
366
+ *
367
+ * @param string $link The link URL
368
+ * @param string $text The link text to display
369
+ * @param string $bool Optional boolean. If FALSE, the text is returned unlinked. Default: TRUE.
370
+ * @return string The generated link
371
+ * @since 4.2.4
372
+ */
373
+ function wprss_link_display( $link, $text, $bool = TRUE ) {
374
+ $display_settings = wprss_get_display_settings( get_option( 'wprss_settings_general' ) );
375
+ $a = $bool ? "<a {$display_settings['open']} {$display_settings['follow']} href='$link'>$text</a>" : $text;
376
+ return $a;
377
+ }
378
+
379
+
380
+ add_filter( 'wprss_pagination', 'wprss_pagination_links' );
381
+ /**
382
+ * Display pagination links
383
+ *
384
+ * @since 3.5
385
+ */
386
+ function wprss_pagination_links( $output ) {
387
+ // Get the general setting
388
+ $pagination = wprss_get_general_setting( 'pagination' );;
389
+
390
+ // Check the pagination setting, if using page numbers
391
+ if ( $pagination === 'numbered' ) {
392
+ global $wp_query;
393
+ $big = 999999999; // need an unlikely integer
394
+ $output .= paginate_links( array(
395
+ 'base' => str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) ),
396
+ 'format' => '?paged=%#%',
397
+ 'current' => max( 1, get_query_var('paged') ),
398
+ 'total' => $wp_query->max_num_pages
399
+ ) );
400
+ return $output;
401
+ }
402
+ // Otherwise, using default paginations
403
+ else {
404
+ $output .= '<div class="nav-links">';
405
+ $output .= ' <div class="nav-previous alignleft">' . get_next_posts_link( __( 'Older posts', WPRSS_TEXT_DOMAIN ) ) . '</div>';
406
+ $output .= ' <div class="nav-next alignright">' . get_previous_posts_link( __( 'Newer posts', WPRSS_TEXT_DOMAIN ) ) . '</div>';
407
+ $output .= '</div>';
408
+ return $output;
409
+ }
410
+ }
411
+
412
+
413
+ add_filter( 'the_title', 'wprss_shorten_title', 10, 2 );
414
+ /**
415
+ * Checks the title limit option and shortens the title when necassary.
416
+ *
417
+ * @since 1.0
418
+ */
419
+ function wprss_shorten_title( $title, $id = null ) {
420
+ if ( $id === null ) return $title;
421
+ // Get the option. If does not exist, use 0, which is ignored.
422
+ $general_settings = get_option( 'wprss_settings_general' );
423
+ $title_limit = isset( $general_settings['title_limit'] )? intval( $general_settings['title_limit'] ) : 0;
424
+ // Check if the title is for a wprss_feed_item, and check if trimming is needed
425
+ if ( isset( $id ) && get_post_type( $id ) === 'wprss_feed_item' && $title_limit > 0 && strlen( $title ) > $title_limit ) {
426
+ // Return the trimmed version of the title
427
+ return substr( $title, 0, $title_limit ) . apply_filters( 'wprss_shortened_title_ending', '...' );
428
+ }
429
+ // Otherwise, return the same title
430
+ return $title;
431
+ }
432
+
433
+
434
+ /**
435
+ * Display feed items on the front end (via shortcode or function)
436
+ *
437
+ * @since 2.0
438
+ */
439
+ function wprss_display_feed_items( $args = array() ) {
440
+ $settings = get_option( 'wprss_settings_general' );
441
+ $args = wprss_get_shortcode_default_args( $args );
442
+
443
+ $args = apply_filters( 'wprss_shortcode_args', $args );
444
+
445
+ $query_args = $settings;
446
+ if ( isset( $args['limit'] ) ) {
447
+ $query_args['feed_limit'] = filter_var( $args['limit'], FILTER_VALIDATE_INT, array(
448
+ 'options' => array(
449
+ 'min_range' => 1,
450
+ 'default' => $query_args['feed_limit'],
451
+ ),
452
+ ) );
453
+ }
454
+
455
+ if ( isset( $args['pagination'] ) ) {
456
+ $query_args['pagination'] = $args['pagination'];
457
+ }
458
+
459
+ if ( isset( $args['source'] ) ) {
460
+ $query_args['source'] = $args['source'];
461
+ }
462
+ elseif ( isset( $args['exclude'] ) ) {
463
+ $query_args['exclude'] = $args['exclude'];
464
+ }
465
+
466
+ $query_args = apply_filters( 'wprss_process_shortcode_args', $query_args, $args );
467
+
468
+ $feed_items = wprss_get_feed_items_query( $query_args );
469
+
470
+ do_action( 'wprss_display_template', $args, $feed_items );
471
+ }
472
+
473
+
474
+ /**
475
+ * Redirects to wprss_display_feed_items
476
+ * It is used for backwards compatibility to versions < 2.0
477
+ *
478
+ * @since 2.1
479
+ */
480
+ function wp_rss_aggregator( $args = array() ) {
481
+ wprss_display_feed_items( $args );
482
+ }
483
+
484
+
485
+ /**
486
+ * Limits a phrase/content to a defined number of words
487
+ *
488
+ * NOT BEING USED as we're using the native WP function, although the native one strips tags, so I'll
489
+ * probably revisit this one again soon.
490
+ *
491
+ * @since 3.0
492
+ * @param string $words
493
+ * @param integer $limit
494
+ * @param string $append
495
+ * @return string
496
+ */
497
+ function wprss_limit_words( $words, $limit, $append = '' ) {
498
+ /* Add 1 to the specified limit becuase arrays start at 0 */
499
+ $limit = $limit + 1;
500
+ /* Store each individual word as an array element
501
+ up to the limit */
502
+ $words = explode( ' ', $words, $limit );
503
+ /* Shorten the array by 1 because that final element will be the sum of all the words after the limit */
504
+ array_pop( $words );
505
+ /* Implode the array for output, and append an ellipse */
506
+ $words = implode( ' ', $words ) . $append;
507
+ /* Return the result */
508
+ return rtrim( $words );
509
+ }
includes/feed-importing.php CHANGED
@@ -1,661 +1,681 @@
1
- <?php
2
- /**
3
- * Functions relating to feed importing
4
- *
5
- * @package WPRSSAggregator
6
- */
7
-
8
-
9
- // Warning: Order may be important
10
- add_filter('wprss_normalize_permalink', 'wprss_google_news_url_fix', 8);
11
- add_filter('wprss_normalize_permalink', 'wprss_bing_news_url_fix', 9);
12
- add_filter('wprss_normalize_permalink', 'wprss_convert_video_permalink', 100);
13
-
14
-
15
- add_action( 'wprss_fetch_single_feed_hook', 'wprss_fetch_insert_single_feed_items' );
16
- /**
17
- * The main feed fetching function.
18
- * Fetches the feed items from the source provided and inserts them into the DB.
19
- *
20
- * Called on hook 'wprss_fetch_single_feed_hook'.
21
- *
22
- * @since 3.2
23
- */
24
- function wprss_fetch_insert_single_feed_items( $feed_ID ) {
25
- wprss_log_obj( 'Starting import of feed', $feed_ID, null, WPRSS_LOG_LEVEL_INFO );
26
-
27
- global $wprss_importing_feed;
28
- $wprss_importing_feed = $feed_ID;
29
- register_shutdown_function( 'wprss_detect_exec_timeout' );
30
-
31
- // Check if the feed source is active.
32
- if ( wprss_is_feed_source_active( $feed_ID ) === FALSE && wprss_feed_source_force_next_fetch( $feed_ID ) === FALSE ) {
33
- // If it is not active ( paused ), return without fetching the feed items.
34
- wprss_log( 'Feed is not active and not forced. Import cancelled.', null, WPRSS_LOG_LEVEL_INFO );
35
- return;
36
- }
37
- // If the feed source is forced for next fetch, remove the force next fetch data
38
- if ( wprss_feed_source_force_next_fetch( $feed_ID ) ) {
39
- delete_post_meta( $feed_ID, 'wprss_force_next_fetch' );
40
- wprss_log( 'Force feed flag removed', null, WPRSS_LOG_LEVEL_SYSTEM );
41
- }
42
-
43
- $start_of_update = wprss_flag_feed_as_updating( $feed_ID );
44
- wprss_log_obj( 'Start of import time updated', date( 'Y-m-d H:i:s', $start_of_update), null, WPRSS_LOG_LEVEL_SYSTEM );
45
-
46
- // Get the feed source URL from post meta, and filter it
47
- $feed_url = get_post_meta( $feed_ID, 'wprss_url', true );
48
- wprss_log_obj( 'Original feed source URL', $feed_url, null, WPRSS_LOG_LEVEL_SYSTEM );
49
- $feed_url = apply_filters( 'wprss_feed_source_url', $feed_url, $feed_ID );
50
- wprss_log_obj( 'Actual feed source URL', $feed_url, null, WPRSS_LOG_LEVEL_INFO );
51
-
52
- // Get the feed limit from post meta
53
- $feed_limit = get_post_meta( $feed_ID, 'wprss_limit', true );
54
- wprss_log_obj( 'Feed limit value is', $feed_limit, null, WPRSS_LOG_LEVEL_SYSTEM );
55
-
56
- // If the feed has no individual limit
57
- if ( $feed_limit === '' || intval( $feed_limit ) <= 0 ) {
58
- wprss_log_obj( 'Using global limit', $feed_limit, null, WPRSS_LOG_LEVEL_NOTICE );
59
- // Get the global limit
60
- $global_limit = wprss_get_general_setting('limit_feed_items_imported');
61
- // If no global limit is set, mark as NULL
62
- if ( $global_limit === '' || intval($global_limit) <= 0 ) {
63
- $feed_limit = NULL;
64
- }
65
- else $feed_limit = $global_limit;
66
- }
67
- wprss_log_obj( 'Feed import limit', $feed_limit, null, WPRSS_LOG_LEVEL_INFO );
68
-
69
- // Filter the URL for validaty
70
- if ( wprss_validate_url( $feed_url ) ) {
71
- wprss_log_obj( 'Feed URL is valid', $feed_url, null, WPRSS_LOG_LEVEL_INFO );
72
- // Get the feed items from the source
73
- $items = wprss_get_feed_items( $feed_url, $feed_ID );
74
- // If got NULL, convert to an empty array
75
- if ( $items === NULL ) {
76
- $items = array();
77
- wprss_log( 'Items were NULL. Using empty array', null, WPRSS_LOG_LEVEL_WARNING );
78
- }
79
-
80
- // If using a limit ...
81
- if ( $feed_limit === NULL ) {
82
- $items_to_insert = $items;
83
- } else {
84
- $items_to_insert = array_slice( $items, 0, $feed_limit );
85
- wprss_log_obj( 'Sliced a segment of items', count($items_to_insert), null, WPRSS_LOG_LEVEL_SYSTEM );
86
- }
87
-
88
- // Gather the permalinks of existing feed item's related to this feed source
89
- $existing_permalinks = get_existing_permalinks( $feed_ID );
90
- wprss_log_obj( 'Retrieved existing permalinks', count( $existing_permalinks ), null, WPRSS_LOG_LEVEL_SYSTEM );
91
-
92
- // Generate a list of items fetched, that are not already in the DB
93
- $new_items = array();
94
- foreach ( $items_to_insert as $item ) {
95
- $permalink = wprss_normalize_permalink( $item->get_permalink() );
96
- wprss_log_obj( 'Normalizing permalink', sprintf('%1$s -> %2$s', $item->get_permalink(), $permalink), null, WPRSS_LOG_LEVEL_SYSTEM );
97
- // Check if not blacklisted and not already imported
98
- $is_blacklisted = wprss_is_blacklisted( $permalink );
99
- $already_exists = in_array( $permalink, $existing_permalinks );
100
- if ( $is_blacklisted === FALSE && $already_exists === FALSE ) {
101
- $new_items[] = $item;
102
- wprss_log_obj( 'Permalink OK', $permalink, null, WPRSS_LOG_LEVEL_SYSTEM );
103
- }
104
- else {
105
- if ( $is_blacklisted ) {
106
- wprss_log( 'Permalink blacklisted', null, WPRSS_LOG_LEVEL_SYSTEM );
107
- }
108
- if ( $already_exists ) {
109
- wprss_log( 'Permalink already exists', null, WPRSS_LOG_LEVEL_SYSTEM );
110
- }
111
- }
112
- }
113
-
114
- $original_count = count( $items_to_insert );
115
- $new_count = count( $new_items );
116
- if ( $new_count !== $original_count ) {
117
- wprss_log_obj( 'Items filtered out', $original_count - $new_count, null, WPRSS_LOG_LEVEL_SYSTEM );
118
- } else {
119
- wprss_log( 'Items to import remained untouched. Not items already exist or are blacklisted.', null, WPRSS_LOG_LEVEL_SYSTEM );
120
- }
121
-
122
- $items_to_insert = $new_items;
123
-
124
- // If using a limit - delete any excess items to make room for the new items
125
- if ( $feed_limit !== NULL ) {
126
- wprss_log_obj( 'Some items may be deleted due to limit', $feed_limit, null, WPRSS_LOG_LEVEL_SYSTEM );
127
-
128
- // Get the number of feed items in DB, and their count
129
- $db_feed_items = wprss_get_feed_items_for_source( $feed_ID );
130
- $num_db_feed_items = $db_feed_items->post_count;
131
-
132
- // Get the number of feed items we can store until we reach the limit
133
- $num_can_insert = $feed_limit - $num_db_feed_items;
134
- // Calculate how many feed items we must delete before importing, to keep to the limit
135
- $num_new_items = count( $new_items );
136
- $num_feed_items_to_delete = $num_can_insert > $num_new_items
137
- ? 0
138
- : $num_new_items - $num_can_insert;
139
-
140
- // Get an array with the DB feed items in reverse order (oldest first)
141
- $db_feed_items_reversed = array_reverse( $db_feed_items->posts );
142
- // Cut the array to get only the first few that are to be deleted ( equal to $num_feed_items_to_delete )
143
- $feed_items_to_delete = array_slice( $db_feed_items_reversed, 0, $num_feed_items_to_delete );
144
- wprss_log( sprintf( 'There already are %1$d items in the database. %2$d items can be inserted. %3$d items will be deleted', $num_db_feed_items, $num_can_insert, $num_feed_items_to_delete ), null, WPRSS_LOG_LEVEL_SYSTEM );
145
-
146
- // Iterate the feed items and delete them
147
- foreach ( $feed_items_to_delete as $key => $post ) {
148
- wp_delete_post( $post->ID, TRUE );
149
- }
150
-
151
- if ( $deleted_items_count = count( $feed_items_to_delete ) )
152
- wprss_log_obj( 'Items deleted due to limit', $deleted_items_count, null, WPRSS_LOG_LEVEL_NOTICE );
153
- }
154
-
155
- update_post_meta( $feed_ID, 'wprss_last_update', $last_update_time = time() );
156
- update_post_meta( $feed_ID, 'wprss_last_update_items', 0 );
157
- wprss_log_obj( 'Last import time updated', $last_update_time, null, WPRSS_LOG_LEVEL_SYSTEM );
158
-
159
- // Insert the items into the db
160
- if ( !empty( $items_to_insert ) ) {
161
- wprss_log_obj( 'There are items to insert', count($items_to_insert), null, WPRSS_LOG_LEVEL_INFO );
162
- wprss_items_insert_post( $items_to_insert, $feed_ID );
163
- }
164
- } else {
165
- wprss_log_obj('The feed URL is not valid! Please recheck', $feed_url);
166
- }
167
-
168
- $next_scheduled = get_post_meta( $feed_ID, 'wprss_reschedule_event', TRUE );
169
-
170
- if ( $next_scheduled !== '' ) {
171
- wprss_feed_source_update_start_schedule( $feed_ID );
172
- delete_post_meta( $feed_ID, 'wprss_reschedule_event' );
173
- wprss_log( 'Next update rescheduled', null, WPRSS_LOG_LEVEL_SYSTEM );
174
- }
175
-
176
- wprss_flag_feed_as_idle( $feed_ID );
177
- wprss_log_obj( 'Import complete', $feed_ID, __FUNCTION__, WPRSS_LOG_LEVEL_INFO );
178
- }
179
-
180
-
181
- /**
182
- * Fetches the feed items from a feed at the given URL.
183
- *
184
- * Called from 'wprss_fetch_insert_single_feed_items'
185
- *
186
- * @since 3.0
187
- */
188
- function wprss_get_feed_items( $feed_url, $source, $force_feed = FALSE ) {
189
- // Add filters and actions prior to fetching the feed items
190
- add_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_feed_cache_lifetime' );
191
- add_action( 'wp_feed_options', 'wprss_do_not_cache_feeds' );
192
-
193
- /* Fetch the feed from the soure URL specified */
194
- $feed = wprss_fetch_feed( $feed_url, $source, $force_feed );
195
-
196
- // Remove previously added filters and actions
197
- remove_action( 'wp_feed_options', 'wprss_do_not_cache_feeds' );
198
- remove_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_feed_cache_lifetime' );
199
-
200
- if ( !is_wp_error( $feed ) ) {
201
- // Return the items in the feed.
202
- return $feed->get_items();
203
- }
204
- else {
205
- wprss_log( 'Failed to fetch feed "' . $feed_url . '". ' . $feed->get_error_message() );
206
- return NULL;
207
- }
208
- }
209
-
210
-
211
- /**
212
- * A clone of the function 'fetch_feed' in wp-includes/feed.php [line #529]
213
- *
214
- * Called from 'wprss_get_feed_items'
215
- *
216
- * @since 3.5
217
- */
218
- function wprss_fetch_feed( $url, $source = NULL, $param_force_feed = FALSE ) {
219
- // Import SimplePie
220
- require_once ( ABSPATH . WPINC . '/class-feed.php' );
221
-
222
- // Trim the URL
223
- $url = trim( $url );
224
-
225
- // Initialize the Feed
226
- $feed = new SimplePie();
227
- // Obselete method calls ?
228
- //$feed->set_cache_class( 'WP_Feed_Cache' );
229
- //$feed->set_file_class( 'WP_SimplePie_File' );
230
- $feed->set_feed_url( $url );
231
- $feed->set_autodiscovery_level( SIMPLEPIE_LOCATOR_ALL );
232
-
233
- // If a feed source was passed
234
- if ( $source !== NULL || $param_force_feed ) {
235
- // Get the force feed option for the feed source
236
- $force_feed = get_post_meta( $source, 'wprss_force_feed', TRUE );
237
- // If turned on, force the feed
238
- if ( $force_feed == 'true' || $param_force_feed ) {
239
- $feed->force_feed( TRUE );
240
- }
241
- }
242
-
243
- // Set timeout limit
244
- $fetch_time_limit = wprss_get_feed_fetch_time_limit();
245
- $feed->set_timeout( $fetch_time_limit );
246
-
247
- //$feed->set_cache_duration( apply_filters( 'wp_feed_cache_transient_lifetime', 12 * HOUR_IN_SECONDS, $url ) );
248
- $feed->enable_cache( FALSE );
249
-
250
- // Reference array action hook, for the feed object and the URL
251
- do_action_ref_array( 'wp_feed_options', array( &$feed, $url ) );
252
-
253
- // Prepare the tags to strip from the feed
254
- $tags_to_strip = apply_filters( 'wprss_feed_tags_to_strip', $feed->strip_htmltags, $source );
255
- // Strip them
256
- $feed->strip_htmltags( $tags_to_strip );
257
-
258
- // Fetch the feed
259
- $feed->init();
260
- $feed->handle_content_type();
261
-
262
- // Convert the feed error into a WP_Error, if applicable
263
- if ( $feed->error() ) {
264
- if ( $source !== NULL ) {
265
- $msg = sprintf( __( 'Failed to fetch the RSS feed. Error: %s', WPRSS_TEXT_DOMAIN ), $feed->error() );
266
- update_post_meta( $source, 'wprss_error_last_import', $msg );
267
- }
268
- return new WP_Error( 'simplepie-error', $feed->error() );
269
- }
270
- // If no error, return the feed and remove any error meta
271
- delete_post_meta( $source, "wprss_error_last_import" );
272
- return $feed;
273
- }
274
-
275
-
276
- /**
277
- * Normalizes the given permalink.
278
- *
279
- * @param $permalink The permalink to normalize
280
- * @return string The normalized permalink
281
- * @since 4.2.3
282
- */
283
- function wprss_normalize_permalink( $permalink ) {
284
- // Apply normalization functions on the permalink
285
- $permalink = trim( $permalink );
286
- $permalink = apply_filters( 'wprss_normalize_permalink', $permalink );
287
- // Return the normalized permalink
288
- return $permalink;
289
- }
290
-
291
-
292
- /**
293
- * Extracts the actual URL from a Google News permalink
294
- *
295
- * @param string $permalink The permalink to normalize.
296
- * @since 4.2.3
297
- */
298
- function wprss_google_news_url_fix($permalink) {
299
- return wprss_tracking_url_fix($permalink, '!^(https?:\/\/)?' . preg_quote('news.google.com', '!') . '.*!');
300
- }
301
-
302
-
303
- /**
304
- * Extracts the actual URL from a Bing permalink
305
- *
306
- * @param string $permalink The permalink to normalize.
307
- * @since 4.2.3
308
- */
309
- function wprss_bing_news_url_fix($permalink) {
310
- return wprss_tracking_url_fix($permalink, '!^(https?:\/\/)?(www\.)?' . preg_quote('bing.com/news', '!') . '.*!');
311
- }
312
-
313
-
314
- /**
315
- * Checks if the permalink is a tracking permalink based on host, and if
316
- * it is, returns the normalized URL of the proper feed item article,
317
- * determined by the named query argument.
318
- *
319
- * Fixes the issue with equivalent Google News etc. items having
320
- * different URLs, that contain randomly generated GET parameters.
321
- * Example:
322
- *
323
- * http://news.google.com/news/url?sa=t&fd=R&ct2=us&ei=V3e9U6izMMnm1QaB1YHoDA&url=http://abcd...
324
- * http://news.google.com/news/url?sa=t&fd=R&ct2=us&ei=One9U-HQLsTp1Aal-oDQBQ&url=http://abcd...
325
- *
326
- * @param string $permalink The permalink URL to check and/or normalize.
327
- * @param string|array $patterns One or an array of host names, for which the URL should be fixed.
328
- * @param string Name of the query argument that specifies the actual URL.
329
- * @return string The normalized URL of the original article, as indicated by the `url`
330
- * parameter in the URL query string.
331
- * @since 4.2.3
332
- */
333
- function wprss_tracking_url_fix( $permalink, $patterns, $argName = 'url' ) {
334
- // Parse the url
335
- $parsed = parse_url( urldecode( html_entity_decode( $permalink ) ) );
336
- $patterns = is_array($patterns) ? $patterns :array($patterns);
337
-
338
- // If parsing failed, return the permalink
339
- if ( $parsed === FALSE || $parsed === NULL ) return $permalink;
340
-
341
- // Determine if it's a tracking item
342
- $isMatch = false;
343
- foreach( $patterns as $_idx => $_pattern ) {
344
- if( preg_match($_pattern, $permalink) ) {
345
- $isMatch = true;
346
- break;
347
- }
348
- }
349
-
350
- if( !$isMatch ) return $permalink;
351
-
352
- // Check if the url GET query string is present
353
- if ( !isset( $parsed['query'] ) ) return $permalink;
354
-
355
- // Parse the query string
356
- $query = array();
357
- parse_str( $parsed['query'], $query );
358
-
359
- // Check if the url GET parameter is present in the query string
360
- if ( !is_array($query) || !isset( $query[$argName] ) ) return $permalink;
361
-
362
- return urldecode( $query[$argName] );
363
- }
364
-
365
-
366
- /**
367
- * Converts YouTube, Vimeo and DailyMotion video urls
368
- * into embedded video player urls.
369
- * If the permalink is not a video url, the permalink is returned as is.
370
- *
371
- * @param $permalink The string permalink url to convert.
372
- * @return A string, with the convert permalink, or the same permalink passed as parameter if
373
- * not a video url.
374
- * @since 4.0
375
- */
376
- function wprss_convert_video_permalink( $permalink ) {
377
- // CHECK PERMALINK FOR VIDEO HOSTS : YOUTUBE, VIMEO AND DAILYMOTION
378
- $found_video_host = preg_match( '/http[s]?:\/\/(www\.)?(youtube|dailymotion|vimeo)\.com\/(.*)/i', $permalink, $matches );
379
-
380
- // If video host was found
381
- if ( $found_video_host !== 0 && $found_video_host !== FALSE ) {
382
-
383
- // Get general options
384
- $options = get_option( 'wprss_settings_general' );
385
- // Get the video link option entry, or false if it does not exist
386
- $video_link = ( isset($options['video_link']) )? $options['video_link'] : 'false';
387
-
388
- // If the video link option is true, change the video URL to its repective host's embedded
389
- // video player URL. Otherwise, leave the permalink as is.
390
- if ( strtolower( $video_link ) === 'true' ) {
391
- $host = $matches[2];
392
- switch( $host ) {
393
- case 'youtube':
394
- preg_match( '/(&|\?)v=([^&]+)/', $permalink, $yt_matches );
395
- $permalink = 'http://www.youtube.com/embed/' . $yt_matches[2];
396
- break;
397
- case 'vimeo':
398
- preg_match( '/(\d*)$/i', $permalink, $vim_matches );
399
- $permalink = 'http://player.vimeo.com/video/' . $vim_matches[0];
400
- break;
401
- case 'dailymotion':
402
- preg_match( '/(\.com\/)(video\/)(.*)/i', $permalink, $dm_matches );
403
- $permalink = 'http://www.dailymotion.com/embed/video/' . $dm_matches[3];
404
- break;
405
- }
406
- }
407
- }
408
-
409
- return $permalink;
410
- }
411
-
412
-
413
- /**
414
- * Insert wprss_feed_item posts into the DB
415
- *
416
- * @since 3.0
417
- */
418
- function wprss_items_insert_post( $items, $feed_ID ) {
419
- update_post_meta( $feed_ID, 'wprss_feed_is_updating', $update_started_at = time() );
420
- wprss_log_obj( 'Starting import of items for feed ' . $feed_ID, $update_started_at, null, WPRSS_LOG_LEVEL_INFO );
421
-
422
- // Gather the permalinks of existing feed item's related to this feed source
423
- $existing_permalinks = get_existing_permalinks( $feed_ID );
424
-
425
- // Count of items inserted
426
- $items_inserted = 0;
427
-
428
- foreach ( $items as $item ) {
429
-
430
- // Normalize the URL
431
- $permalink = wprss_normalize_permalink( $item->get_permalink() );
432
- wprss_log_obj( 'Importing item', $permalink, null, WPRSS_LOG_LEVEL_INFO );
433
- wprss_log_obj( 'Original permalink', $item->get_permalink(), null, WPRSS_LOG_LEVEL_SYSTEM );
434
-
435
- // Save the enclosure URL
436
- $enclosure_url = '';
437
- if ( $enclosure = $item->get_enclosure(0) ) {
438
- wprss_log( 'Item has an enclosure', null, WPRSS_LOG_LEVEL_SYSTEM );
439
- if ( $enclosure->get_link() ) {
440
- $enclosure_url = $enclosure->get_link();
441
- wprss_log_obj( 'Enclosure has link', $enclosure_url, null, WPRSS_LOG_LEVEL_SYSTEM );
442
- }
443
- }
444
-
445
- /* OLD NORMALIZATION CODE - TO NORMALIZE URLS FROM PROXY URLS
446
- $response = wp_remote_head( $permalink );
447
- if ( !is_wp_error( $response ) && isset( $response['headers']['location'] ) ) {
448
- $permalink = current( explode( '?', $response['headers']['location'] ) );
449
- }*/
450
-
451
- // Check if newly fetched item already present in existing feed items,
452
- // if not insert it into wp_posts and insert post meta.
453
- if ( ! ( in_array( $permalink, $existing_permalinks ) ) ) {
454
- wprss_log( "Importing (unique) feed item (Source: $feed_ID)", null, WPRSS_LOG_LEVEL_INFO );
455
-
456
- // Extend the importing time and refresh the feed's updating flag to reflect that it is active
457
- $extend_time = wprss_flag_feed_as_updating( $feed_ID );
458
- $extend_time_f = date( 'Y-m-d H:i:s', $extend_time );
459
- $time_limit = wprss_get_item_import_time_limit();
460
- wprss_log( "Extended execution time limit by {$time_limit}. (Current Time: {$extend_time_f})", null, WPRSS_LOG_LEVEL_INFO );
461
- set_time_limit( $time_limit );
462
-
463
- // Apply filters that determine if the feed item should be inserted into the DB or not.
464
- $item = apply_filters( 'wprss_insert_post_item_conditionals', $item, $feed_ID, $permalink );
465
-
466
- // Check if the imported count should still be updated, even if the item is NULL
467
- $still_update_count = apply_filters( 'wprss_still_update_import_count', FALSE );
468
-
469
- // If the item is not NULL, continue to inserting the feed item post into the DB
470
- if ( $item !== NULL && !is_bool($item) ) {
471
- wprss_log( 'Using core logic', null, WPRSS_LOG_LEVEL_SYSTEM );
472
-
473
- // Get the date and GTM date and normalize if not valid dor not present
474
- $format = 'Y-m-d H:i:s';
475
- $has_date = $item->get_date( 'U' ) ? TRUE : FALSE;
476
- $timestamp = $has_date ? $item->get_date( 'U' ) : date( 'U' );
477
- $date = date( $format, $timestamp );
478
- $date_gmt = gmdate( $format, $timestamp );
479
- // Prepare the item data
480
- $feed_item = apply_filters(
481
- 'wprss_populate_post_data',
482
- array(
483
- 'post_title' => html_entity_decode( $item->get_title() ),
484
- 'post_content' => '',
485
- 'post_status' => 'publish',
486
- 'post_type' => 'wprss_feed_item',
487
- 'post_date' => $date,
488
- 'post_date_gmt' => $date_gmt
489
- ),
490
- $item
491
- );
492
- wprss_log( 'Post data filters applied', null, WPRSS_LOG_LEVEL_SYSTEM );
493
-
494
- if ( defined('ICL_SITEPRESS_VERSION') )
495
- @include_once( WP_PLUGIN_DIR . '/sitepress-multilingual-cms/inc/wpml-api.php' );
496
- if ( defined('ICL_LANGUAGE_CODE') ) {
497
- $_POST['icl_post_language'] = $language_code = ICL_LANGUAGE_CODE;
498
- wprss_log_obj( 'WPML detected. Language code determined', $language_code, null, WPRSS_LOG_LEVEL_SYSTEM );
499
- }
500
-
501
- // Create and insert post object into the DB
502
- $inserted_ID = wp_insert_post( $feed_item );
503
-
504
- if ( !is_wp_error( $inserted_ID ) ) {
505
-
506
- if ( is_object( $inserted_ID ) ) {
507
- if ( isset( $inserted_ID['ID'] ) ) {
508
- $inserted_ID = $inserted_ID['ID'];
509
- }
510
- elseif ( isset( $inserted_ID->ID ) ) {
511
- $inserted_ID = $inserted_ID->ID;
512
- }
513
- }
514
-
515
- // Increment the inserted items counter
516
- $items_inserted++;
517
-
518
- // Create and insert post meta into the DB
519
- wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink, $enclosure_url );
520
-
521
- // Remember newly added permalink
522
- $existing_permalinks[] = $permalink;
523
- wprss_log_obj( 'Item imported', $inserted_ID, null, WPRSS_LOG_LEVEL_INFO );
524
- }
525
- else {
526
- update_post_meta( $source, 'wprss_error_last_import', 'An error occurred while inserting a feed item into the database.' );
527
- wprss_log_obj( 'Failed to insert post', $feed_item, 'wprss_items_insert_post > wp_insert_post' );
528
- }
529
- }
530
- // If the item is TRUE, then a hook function in the filter inserted the item.
531
- // increment the inserted counter
532
- elseif ( ( is_bool($item) && $item === TRUE ) || ( $still_update_count === TRUE && $item !== FALSE ) ) {
533
- $items_inserted++;
534
- }
535
- }
536
- else {
537
- wprss_log( 'Item already exists and will be skipped', null, WPRSS_LOG_LEVEL_NOTICE );
538
- }
539
-
540
- wprss_log_obj( 'Finished importing item', $permalink, null, WPRSS_LOG_LEVEL_INFO );
541
- }
542
-
543
- update_post_meta( $feed_ID, 'wprss_last_update_items', $items_inserted );
544
- wprss_log_obj( sprintf( 'Finished importing %1$d items for feed source', $items_inserted ), $feed_ID, null, WPRSS_LOG_LEVEL_INFO );
545
- }
546
-
547
-
548
- /**
549
- * Inserts the appropriate post meta for feed items.
550
- *
551
- * Called from 'wprss_items_insert_post'
552
- *
553
- * @since 2.3
554
- */
555
- function wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink, $enclosure_url ) {
556
- update_post_meta( $inserted_ID, 'wprss_item_permalink', $permalink );
557
- update_post_meta( $inserted_ID, 'wprss_item_enclosure', $enclosure_url );
558
-
559
- $author = $item->get_author();
560
- if ( $author ) {
561
- update_post_meta( $inserted_ID, 'wprss_item_author', $author->get_name() );
562
- }
563
-
564
- update_post_meta( $inserted_ID, 'wprss_feed_id', $feed_ID);
565
- do_action( 'wprss_items_create_post_meta', $inserted_ID, $item, $feed_ID );
566
- }
567
-
568
-
569
- /**
570
- * Returns the time limit for the importing of a single feed item.
571
- * The value if filtered through 'wprss_item_import_time_limit'. The default value is WPRSS_ITEM_IMPORT_TIME_LIMIT.
572
- *
573
- * @since 4.6.6
574
- * @return int The maximum amount of seconds allowed for a single feed item to import.
575
- */
576
- function wprss_get_item_import_time_limit() {
577
- return apply_filters( 'wprss_item_import_time_limit', WPRSS_ITEM_IMPORT_TIME_LIMIT );
578
- }
579
-
580
- /**
581
- * Returns the time limit for a feed fetch operation.
582
- * The value if filtered through 'wprss_feed_fetch_time_limit'. The default value is WPRSS_FEED_FETCH_TIME_LIMIT.
583
- *
584
- * @since 4.6.6
585
- * @return int The maximum amount of seconds allowed for an RSS feed XML document to be fetched.
586
- */
587
- function wprss_get_feed_fetch_time_limit() {
588
- return apply_filters( 'wprss_feed_fetch_time_limit', WPRSS_FEED_FETCH_TIME_LIMIT );
589
- }
590
-
591
-
592
- /**
593
- * Fetches all feed items from all feed sources.
594
- * Iteratively calls 'wprss_fetch_insert_single_feed_items' for all feed sources.
595
- *
596
- * This function is used by the cron job or the debugging functions to get all feeds from all feed sources
597
- *
598
- * @param $all If set to TRUE, the function will pull from all feed sources, regardless of their individual
599
- * update interval. If set to FALSE, only feed sources using the global update system will be updated.
600
- * (Optional) Default: TRUE.
601
- * @since 3.0
602
- */
603
- function wprss_fetch_insert_all_feed_items( $all = TRUE ) {
604
- wprss_log( 'Importing from all sources...', __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM );
605
- // Get all feed sources
606
- $feed_sources = wprss_get_all_feed_sources();
607
-
608
- if( $feed_sources->have_posts() ) {
609
- // Start by getting one feed source, we will cycle through them one by one,
610
- // fetching feed items and adding them to the database in each pass
611
- while ( $feed_sources->have_posts() ) {
612
- $feed_sources->the_post();
613
-
614
- $interval = get_post_meta( get_the_ID(), 'wprss_update_interval', TRUE );
615
- $using_global_interval = ( $interval === wprss_get_default_feed_source_update_interval() || $interval === '' );
616
-
617
- // Check if fetching from all, or if feed source uses the global interval
618
- if ( $all === TRUE || $using_global_interval ) {
619
- wp_schedule_single_event( time(), 'wprss_fetch_single_feed_hook', array( get_the_ID() ) );
620
- }
621
- }
622
- wp_reset_postdata(); // Restore the $post global to the current post in the main query
623
- }
624
- }
625
-
626
-
627
- /**
628
- * Runs the above function with parameter FALSE
629
- *
630
- * @since 3.9
631
- */
632
- function wprss_fetch_insert_all_feed_items_from_cron() {
633
- wprss_fetch_insert_all_feed_items( FALSE );
634
- }
635
-
636
-
637
- /**
638
- * Shutdown function for detecting if the PHP script reaches the maximum execution time limit
639
- * while importing a feed.
640
- *
641
- * @since 4.6.6
642
- */
643
- function wprss_detect_exec_timeout() {
644
- // Get last error
645
- if ( $error = error_get_last() ){
646
- // Check if it is an E_ERROR and if it is a max exec time limit error
647
- if ( $error['type'] === E_ERROR && stripos( $error['message'], 'maximum execution' ) === 0 ) {
648
- // If the importing process was running
649
- if ( array_key_exists( 'wprss_importing_feed', $GLOBALS ) && $GLOBALS['wprss_importing_feed'] !== NULL ) {
650
- // Get the ID of the feed that was importing
651
- $feed_ID = $GLOBALS['wprss_importing_feed'];
652
- // Perform clean up
653
- wprss_flag_feed_as_idle( $feed_ID );
654
- $msg = sprintf( __( 'The PHP script timed out while importing an item from this feed, after %d seconds.', WPRSS_TEXT_DOMAIN ), wprss_get_item_import_time_limit() );
655
- update_post_meta( $feed_ID, 'wprss_error_last_import', $msg );
656
- // Log the error
657
- wprss_log( 'The PHP script timed out while importing feed #' . $feed_ID, NULL, WPRSS_LOG_LEVEL_ERROR );
658
- }
659
- }
660
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
661
  }
1
+ <?php
2
+ /**
3
+ * Functions relating to feed importing
4
+ *
5
+ * @package WPRSSAggregator
6
+ */
7
+
8
+
9
+ // Warning: Order may be important
10
+ add_filter('wprss_normalize_permalink', 'wprss_google_news_url_fix', 8);
11
+ add_filter('wprss_normalize_permalink', 'wprss_bing_news_url_fix', 9);
12
+ add_filter('wprss_normalize_permalink', 'wprss_convert_video_permalink', 100);
13
+
14
+
15
+ add_action( 'wprss_fetch_single_feed_hook', 'wprss_fetch_insert_single_feed_items' );
16
+ /**
17
+ * The main feed fetching function.
18
+ * Fetches the feed items from the source provided and inserts them into the DB.
19
+ *
20
+ * Called on hook 'wprss_fetch_single_feed_hook'.
21
+ *
22
+ * @since 3.2
23
+ */
24
+ function wprss_fetch_insert_single_feed_items( $feed_ID ) {
25
+ wprss_log_obj( 'Starting import of feed', $feed_ID, null, WPRSS_LOG_LEVEL_INFO );
26
+
27
+ global $wprss_importing_feed;
28
+ $wprss_importing_feed = $feed_ID;
29
+ register_shutdown_function( 'wprss_detect_exec_timeout' );
30
+
31
+ // Check if the feed source is active.
32
+ if ( wprss_is_feed_source_active( $feed_ID ) === FALSE && wprss_feed_source_force_next_fetch( $feed_ID ) === FALSE ) {
33
+ // If it is not active ( paused ), return without fetching the feed items.
34
+ wprss_log( 'Feed is not active and not forced. Import cancelled.', null, WPRSS_LOG_LEVEL_INFO );
35
+ return;
36
+ }
37
+ // If the feed source is forced for next fetch, remove the force next fetch data
38
+ if ( wprss_feed_source_force_next_fetch( $feed_ID ) ) {
39
+ delete_post_meta( $feed_ID, 'wprss_force_next_fetch' );
40
+ wprss_log( 'Force feed flag removed', null, WPRSS_LOG_LEVEL_SYSTEM );
41
+ }
42
+
43
+ $start_of_update = wprss_flag_feed_as_updating( $feed_ID );
44
+ wprss_log_obj( 'Start of import time updated', date( 'Y-m-d H:i:s', $start_of_update), null, WPRSS_LOG_LEVEL_SYSTEM );
45
+
46
+ // Get the feed source URL from post meta, and filter it
47
+ $feed_url = get_post_meta( $feed_ID, 'wprss_url', true );
48
+ wprss_log_obj( 'Original feed source URL', $feed_url, null, WPRSS_LOG_LEVEL_SYSTEM );
49
+ $feed_url = apply_filters( 'wprss_feed_source_url', $feed_url, $feed_ID );
50
+ wprss_log_obj( 'Actual feed source URL', $feed_url, null, WPRSS_LOG_LEVEL_INFO );
51
+
52
+ // Get the feed limit from post meta
53
+ $feed_limit = get_post_meta( $feed_ID, 'wprss_limit', true );
54
+ wprss_log_obj( 'Feed limit value is', $feed_limit, null, WPRSS_LOG_LEVEL_SYSTEM );
55
+
56
+ // If the feed has no individual limit
57
+ if ( $feed_limit === '' || intval( $feed_limit ) <= 0 ) {
58
+ wprss_log_obj( 'Using global limit', $feed_limit, null, WPRSS_LOG_LEVEL_NOTICE );
59
+ // Get the global limit
60
+ $global_limit = wprss_get_general_setting('limit_feed_items_imported');
61
+ // If no global limit is set, mark as NULL
62
+ if ( $global_limit === '' || intval($global_limit) <= 0 ) {
63
+ $feed_limit = NULL;
64
+ }
65
+ else $feed_limit = $global_limit;
66
+ }
67
+ wprss_log_obj( 'Feed import limit', $feed_limit, null, WPRSS_LOG_LEVEL_INFO );
68
+
69
+ // Filter the URL for validaty
70
+ if ( wprss_validate_url( $feed_url ) ) {
71
+ wprss_log_obj( 'Feed URL is valid', $feed_url, null, WPRSS_LOG_LEVEL_INFO );
72
+ // Get the feed items from the source
73
+ $items = wprss_get_feed_items( $feed_url, $feed_ID );
74
+ // If got NULL, convert to an empty array
75
+ if ( $items === NULL ) {
76
+ $items = array();
77
+ wprss_log( 'Items were NULL. Using empty array', null, WPRSS_LOG_LEVEL_WARNING );
78
+ }
79
+
80
+ // If using a limit ...
81
+ if ( $feed_limit === NULL ) {
82
+ $items_to_insert = $items;
83
+ } else {
84
+ $items_to_insert = array_slice( $items, 0, $feed_limit );
85
+ wprss_log_obj( 'Sliced a segment of items', count($items_to_insert), null, WPRSS_LOG_LEVEL_SYSTEM );
86
+ }
87
+
88
+ // Gather the permalinks of existing feed item's related to this feed source
89
+ $existing_permalinks = wprss_get_existing_permalinks( $feed_ID );
90
+ wprss_log_obj( 'Retrieved existing permalinks', count( $existing_permalinks ), null, WPRSS_LOG_LEVEL_SYSTEM );
91
+
92
+ // Check if we should only import uniquely-titled feed items.
93
+ $existing_titles = array();
94
+ $unique_titles = FALSE;
95
+ if ( wprss_get_general_setting( 'unique_titles' ) ) {
96
+ $unique_titles = TRUE;
97
+ $existing_titles = wprss_get_existing_titles( );
98
+ wprss_log_obj( 'Retrieved existing titles from global', count( $existing_titles ), null, WPRSS_LOG_LEVEL_SYSTEM );
99
+ } else if ( get_post_meta( $feed_ID, 'wprss_unique_titles', true ) === 'true' ) {
100
+ $unique_titles = TRUE;
101
+ $existing_titles = wprss_get_existing_titles( $feed_ID );
102
+ wprss_log_obj( 'Retrieved existing titles from feed source', count( $existing_titles ), null, WPRSS_LOG_LEVEL_SYSTEM );
103
+ }
104
+
105
+ // Generate a list of items fetched, that are not already in the DB
106
+ $new_items = array();
107
+ foreach ( $items_to_insert as $item ) {
108
+ $permalink = wprss_normalize_permalink( $item->get_permalink() );
109
+ wprss_log_obj( 'Normalizing permalink', sprintf('%1$s -> %2$s', $item->get_permalink(), $permalink), null, WPRSS_LOG_LEVEL_SYSTEM );
110
+
111
+ // Check if not blacklisted and not already imported
112
+ $is_blacklisted = wprss_is_blacklisted( $permalink );
113
+ $permalink_exists = array_key_exists( $permalink, $existing_permalinks );
114
+ $title_exists = array_key_exists( $item->get_title(), $existing_titles );
115
+
116
+ if ( $is_blacklisted === FALSE && $permalink_exists === FALSE && $title_exists === FALSE) {
117
+ $new_items[] = $item;
118
+ wprss_log_obj( 'Permalink OK', $permalink, null, WPRSS_LOG_LEVEL_SYSTEM );
119
+
120
+ if ( $unique_titles ) {
121
+ $existing_titles[$item->get_title()] = 1;
122
+ }
123
+ } else {
124
+ if ( $is_blacklisted ) {
125
+ wprss_log( 'Permalink blacklisted', null, WPRSS_LOG_LEVEL_SYSTEM );
126
+ }
127
+ if ( $permalink_exists ) {
128
+ wprss_log( 'Permalink already exists', null, WPRSS_LOG_LEVEL_SYSTEM );
129
+ }
130
+ if ( $title_exists ) {
131
+ wprss_log( 'Title already exists', null, WPRSS_LOG_LEVEL_SYSTEM );
132
+ }
133
+ }
134
+ }
135
+
136
+ $original_count = count( $items_to_insert );
137
+ $new_count = count( $new_items );
138
+ if ( $new_count !== $original_count ) {
139
+ wprss_log_obj( 'Items filtered out', $original_count - $new_count, null, WPRSS_LOG_LEVEL_SYSTEM );
140
+ } else {
141
+ wprss_log( 'Items to import remained untouched. Not items already exist or are blacklisted.', null, WPRSS_LOG_LEVEL_SYSTEM );
142
+ }
143
+
144
+ $items_to_insert = $new_items;
145
+
146
+ // If using a limit - delete any excess items to make room for the new items
147
+ if ( $feed_limit !== NULL ) {
148
+ wprss_log_obj( 'Some items may be deleted due to limit', $feed_limit, null, WPRSS_LOG_LEVEL_SYSTEM );
149
+
150
+ // Get the number of feed items in DB, and their count
151
+ $db_feed_items = wprss_get_feed_items_for_source( $feed_ID );
152
+ $num_db_feed_items = $db_feed_items->post_count;
153
+
154
+ // Get the number of feed items we can store until we reach the limit
155
+ $num_can_insert = $feed_limit - $num_db_feed_items;
156
+ // Calculate how many feed items we must delete before importing, to keep to the limit
157
+ $num_new_items = count( $new_items );
158
+ $num_feed_items_to_delete = $num_can_insert > $num_new_items
159
+ ? 0
160
+ : $num_new_items - $num_can_insert;
161
+
162
+ // Get an array with the DB feed items in reverse order (oldest first)
163
+ $db_feed_items_reversed = array_reverse( $db_feed_items->posts );
164
+ // Cut the array to get only the first few that are to be deleted ( equal to $num_feed_items_to_delete )
165
+ $feed_items_to_delete = array_slice( $db_feed_items_reversed, 0, $num_feed_items_to_delete );
166
+ wprss_log( sprintf( 'There already are %1$d items in the database. %2$d items can be inserted. %3$d items will be deleted', $num_db_feed_items, $num_can_insert, $num_feed_items_to_delete ), null, WPRSS_LOG_LEVEL_SYSTEM );
167
+
168
+ // Iterate the feed items and delete them
169
+ foreach ( $feed_items_to_delete as $key => $post ) {
170
+ wp_delete_post( $post->ID, TRUE );
171
+ }
172
+
173
+ if ( $deleted_items_count = count( $feed_items_to_delete ) )
174
+ wprss_log_obj( 'Items deleted due to limit', $deleted_items_count, null, WPRSS_LOG_LEVEL_NOTICE );
175
+ }
176
+
177
+ update_post_meta( $feed_ID, 'wprss_last_update', $last_update_time = time() );
178
+ update_post_meta( $feed_ID, 'wprss_last_update_items', 0 );
179
+ wprss_log_obj( 'Last import time updated', $last_update_time, null, WPRSS_LOG_LEVEL_SYSTEM );
180
+
181
+ // Insert the items into the db
182
+ if ( !empty( $items_to_insert ) ) {
183
+ wprss_log_obj( 'There are items to insert', count($items_to_insert), null, WPRSS_LOG_LEVEL_INFO );
184
+ wprss_items_insert_post( $items_to_insert, $feed_ID );
185
+ }
186
+ } else {
187
+ wprss_log_obj('The feed URL is not valid! Please recheck', $feed_url);
188
+ }
189
+
190
+ $next_scheduled = get_post_meta( $feed_ID, 'wprss_reschedule_event', TRUE );
191
+
192
+ if ( $next_scheduled !== '' ) {
193
+ wprss_feed_source_update_start_schedule( $feed_ID );
194
+ delete_post_meta( $feed_ID, 'wprss_reschedule_event' );
195
+ wprss_log( 'Next update rescheduled', null, WPRSS_LOG_LEVEL_SYSTEM );
196
+ }
197
+
198
+ wprss_flag_feed_as_idle( $feed_ID );
199
+ wprss_log_obj( 'Import complete', $feed_ID, __FUNCTION__, WPRSS_LOG_LEVEL_INFO );
200
+ }
201
+
202
+
203
+ /**
204
+ * Fetches the feed items from a feed at the given URL.
205
+ *
206
+ * Called from 'wprss_fetch_insert_single_feed_items'
207
+ *
208
+ * @since 3.0
209
+ */
210
+ function wprss_get_feed_items( $feed_url, $source, $force_feed = FALSE ) {
211
+ // Add filters and actions prior to fetching the feed items
212
+ add_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_feed_cache_lifetime' );
213
+ add_action( 'wp_feed_options', 'wprss_do_not_cache_feeds' );
214
+
215
+ /* Fetch the feed from the soure URL specified */
216
+ $feed = wprss_fetch_feed( $feed_url, $source, $force_feed );
217
+
218
+ // Remove previously added filters and actions
219
+ remove_action( 'wp_feed_options', 'wprss_do_not_cache_feeds' );
220
+ remove_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_feed_cache_lifetime' );
221
+
222
+ if ( !is_wp_error( $feed ) ) {
223
+ // Return the items in the feed.
224
+ return $feed->get_items();
225
+ }
226
+ else {
227
+ wprss_log( 'Failed to fetch feed "' . $feed_url . '". ' . $feed->get_error_message() );
228
+ return NULL;
229
+ }
230
+ }
231
+
232
+
233
+ /**
234
+ * A clone of the function 'fetch_feed' in wp-includes/feed.php [line #529]
235
+ *
236
+ * Called from 'wprss_get_feed_items'
237
+ *
238
+ * @since 3.5
239
+ */
240
+ function wprss_fetch_feed( $url, $source = NULL, $param_force_feed = FALSE ) {
241
+
242
+ // Trim the URL
243
+ $url = trim( $url );
244
+
245
+ // Initialize the Feed
246
+ $feed = new SimplePie();
247
+ // Obselete method calls ?
248
+ //$feed->set_cache_class( 'WP_Feed_Cache' );
249
+ //$feed->set_file_class( 'WP_SimplePie_File' );
250
+ $feed->set_feed_url( $url );
251
+ $feed->set_autodiscovery_level( SIMPLEPIE_LOCATOR_ALL );
252
+
253
+ // If a feed source was passed
254
+ if ( $source !== NULL || $param_force_feed ) {
255
+ // Get the force feed option for the feed source
256
+ $force_feed = get_post_meta( $source, 'wprss_force_feed', TRUE );
257
+ // If turned on, force the feed
258
+ if ( $force_feed == 'true' || $param_force_feed ) {
259
+ $feed->force_feed( TRUE );
260
+ }
261
+ }
262
+
263
+ // Set timeout limit
264
+ $fetch_time_limit = wprss_get_feed_fetch_time_limit();
265
+ $feed->set_timeout( $fetch_time_limit );
266
+
267
+ //$feed->set_cache_duration( apply_filters( 'wp_feed_cache_transient_lifetime', 12 * HOUR_IN_SECONDS, $url ) );
268
+ $feed->enable_cache( FALSE );
269
+
270
+ // Reference array action hook, for the feed object and the URL
271
+ do_action_ref_array( 'wp_feed_options', array( &$feed, $url ) );
272
+
273
+ // Prepare the tags to strip from the feed
274
+ $tags_to_strip = apply_filters( 'wprss_feed_tags_to_strip', $feed->strip_htmltags, $source );
275
+ // Strip them
276
+ $feed->strip_htmltags( $tags_to_strip );
277
+
278
+ // Fetch the feed
279
+ $feed->init();
280
+ $feed->handle_content_type();
281
+
282
+ // Convert the feed error into a WP_Error, if applicable
283
+ if ( $feed->error() ) {
284
+ if ( $source !== NULL ) {
285
+ $msg = sprintf( __( 'Failed to fetch the RSS feed. Error: %s', WPRSS_TEXT_DOMAIN ), $feed->error() );
286
+ update_post_meta( $source, 'wprss_error_last_import', $msg );
287
+ }
288
+ return new WP_Error( 'simplepie-error', $feed->error() );
289
+ }
290
+ // If no error, return the feed and remove any error meta
291
+ delete_post_meta( $source, "wprss_error_last_import" );
292
+ return $feed;
293
+ }
294
+
295
+
296
+ /**
297
+ * Normalizes the given permalink.
298
+ *
299
+ * @param $permalink The permalink to normalize
300
+ * @return string The normalized permalink
301
+ * @since 4.2.3
302
+ */
303
+ function wprss_normalize_permalink( $permalink ) {
304
+ // Apply normalization functions on the permalink
305
+ $permalink = trim( $permalink );
306
+ $permalink = apply_filters( 'wprss_normalize_permalink', $permalink );
307
+ // Return the normalized permalink
308
+ return $permalink;
309
+ }
310
+
311
+
312
+ /**
313
+ * Extracts the actual URL from a Google News permalink
314
+ *
315
+ * @param string $permalink The permalink to normalize.
316
+ * @since 4.2.3
317
+ */
318
+ function wprss_google_news_url_fix($permalink) {
319
+ return wprss_tracking_url_fix($permalink, '!^(https?:\/\/)?' . preg_quote('news.google.com', '!') . '.*!');
320
+ }
321
+
322
+
323
+ /**
324
+ * Extracts the actual URL from a Bing permalink
325
+ *
326
+ * @param string $permalink The permalink to normalize.
327
+ * @since 4.2.3
328
+ */
329
+ function wprss_bing_news_url_fix($permalink) {
330
+ return wprss_tracking_url_fix($permalink, '!^(https?:\/\/)?(www\.)?' . preg_quote('bing.com/news', '!') . '.*!');
331
+ }
332
+
333
+
334
+ /**
335
+ * Checks if the permalink is a tracking permalink based on host, and if
336
+ * it is, returns the normalized URL of the proper feed item article,
337
+ * determined by the named query argument.
338
+ *
339
+ * Fixes the issue with equivalent Google News etc. items having
340
+ * different URLs, that contain randomly generated GET parameters.
341
+ * Example:
342
+ *
343
+ * http://news.google.com/news/url?sa=t&fd=R&ct2=us&ei=V3e9U6izMMnm1QaB1YHoDA&url=http://abcd...
344
+ * http://news.google.com/news/url?sa=t&fd=R&ct2=us&ei=One9U-HQLsTp1Aal-oDQBQ&url=http://abcd...
345
+ *
346
+ * @param string $permalink The permalink URL to check and/or normalize.
347
+ * @param string|array $patterns One or an array of host names, for which the URL should be fixed.
348
+ * @param string Name of the query argument that specifies the actual URL.
349
+ * @return string The normalized URL of the original article, as indicated by the `url`
350
+ * parameter in the URL query string.
351
+ * @since 4.2.3
352
+ */
353
+ function wprss_tracking_url_fix( $permalink, $patterns, $argName = 'url' ) {
354
+ // Parse the url
355
+ $parsed = parse_url( urldecode( html_entity_decode( $permalink ) ) );
356
+ $patterns = is_array($patterns) ? $patterns :array($patterns);
357
+
358
+ // If parsing failed, return the permalink
359
+ if ( $parsed === FALSE || $parsed === NULL ) return $permalink;
360
+
361
+ // Determine if it's a tracking item
362
+ $isMatch = false;
363
+ foreach( $patterns as $_idx => $_pattern ) {
364
+ if( preg_match($_pattern, $permalink) ) {
365
+ $isMatch = true;
366
+ break;
367
+ }
368
+ }
369
+
370
+ if( !$isMatch ) return $permalink;
371
+
372
+ // Check if the url GET query string is present
373
+ if ( !isset( $parsed['query'] ) ) return $permalink;
374
+
375
+ // Parse the query string
376
+ $query = array();
377
+ parse_str( $parsed['query'], $query );
378
+
379
+ // Check if the url GET parameter is present in the query string
380
+ if ( !is_array($query) || !isset( $query[$argName] ) ) return $permalink;
381
+
382
+ return urldecode( $query[$argName] );
383
+ }
384
+
385
+
386
+ /**
387
+ * Converts YouTube, Vimeo and DailyMotion video urls
388
+ * into embedded video player urls.
389
+ * If the permalink is not a video url, the permalink is returned as is.
390
+ *
391
+ * @param $permalink The string permalink url to convert.
392
+ * @return A string, with the convert permalink, or the same permalink passed as parameter if
393
+ * not a video url.
394
+ * @since 4.0
395
+ */
396
+ function wprss_convert_video_permalink( $permalink ) {
397
+ // CHECK PERMALINK FOR VIDEO HOSTS : YOUTUBE, VIMEO AND DAILYMOTION
398
+ $found_video_host = preg_match( '/http[s]?:\/\/(www\.)?(youtube|dailymotion|vimeo)\.com\/(.*)/i', $permalink, $matches );
399
+
400
+ // If video host was found
401
+ if ( $found_video_host !== 0 && $found_video_host !== FALSE ) {
402
+
403
+ // Get general options
404
+ $options = get_option( 'wprss_settings_general' );
405
+ // Get the video link option entry, or false if it does not exist
406
+ $video_link = ( isset($options['video_link']) )? $options['video_link'] : 'false';
407
+
408
+ // If the video link option is true, change the video URL to its repective host's embedded
409
+ // video player URL. Otherwise, leave the permalink as is.
410
+ if ( strtolower( $video_link ) === 'true' ) {
411
+ $host = $matches[2];
412
+ switch( $host ) {
413
+ case 'youtube':
414
+ preg_match( '/(&|\?)v=([^&]+)/', $permalink, $yt_matches );
415
+ $permalink = 'http://www.youtube.com/embed/' . $yt_matches[2];
416
+ break;
417
+ case 'vimeo':
418
+ preg_match( '/(\d*)$/i', $permalink, $vim_matches );
419
+ $permalink = 'http://player.vimeo.com/video/' . $vim_matches[0];
420
+ break;
421
+ case 'dailymotion':
422
+ preg_match( '/(\.com\/)(video\/)(.*)/i', $permalink, $dm_matches );
423
+ $permalink = 'http://www.dailymotion.com/embed/video/' . $dm_matches[3];
424
+ break;
425
+ }
426
+ }
427
+ }
428
+
429
+ return $permalink;
430
+ }
431
+
432
+
433
+ /**
434
+ * Insert wprss_feed_item posts into the DB
435
+ *
436
+ * @since 3.0
437
+ */
438
+ function wprss_items_insert_post( $items, $feed_ID ) {
439
+ update_post_meta( $feed_ID, 'wprss_feed_is_updating', $update_started_at = time() );
440
+ wprss_log_obj( 'Starting import of items for feed ' . $feed_ID, $update_started_at, null, WPRSS_LOG_LEVEL_INFO );
441
+
442
+ // Gather the permalinks of existing feed item's related to this feed source
443
+ $existing_permalinks = wprss_get_existing_permalinks( $feed_ID );
444
+
445
+ // Count of items inserted
446
+ $items_inserted = 0;
447
+
448
+ foreach ( $items as $item ) {
449
+
450
+ // Normalize the URL
451
+ $permalink = wprss_normalize_permalink( $item->get_permalink() );
452
+ wprss_log_obj( 'Importing item', $permalink, null, WPRSS_LOG_LEVEL_INFO );
453
+ wprss_log_obj( 'Original permalink', $item->get_permalink(), null, WPRSS_LOG_LEVEL_SYSTEM );
454
+
455
+ // Save the enclosure URL
456
+ $enclosure_url = '';
457
+ if ( $enclosure = $item->get_enclosure(0) ) {
458
+ wprss_log( 'Item has an enclosure', null, WPRSS_LOG_LEVEL_SYSTEM );
459
+ if ( $enclosure->get_link() ) {
460
+ $enclosure_url = $enclosure->get_link();
461
+ wprss_log_obj( 'Enclosure has link', $enclosure_url, null, WPRSS_LOG_LEVEL_SYSTEM );
462
+ }
463
+ }
464
+
465
+ /* OLD NORMALIZATION CODE - TO NORMALIZE URLS FROM PROXY URLS
466
+ $response = wp_remote_head( $permalink );
467
+ if ( !is_wp_error( $response ) && isset( $response['headers']['location'] ) ) {
468
+ $permalink = current( explode( '?', $response['headers']['location'] ) );
469
+ }*/
470
+
471
+ // Check if newly fetched item already present in existing feed items,
472
+ // if not insert it into wp_posts and insert post meta.
473
+ if ( ! ( array_key_exists( $permalink, $existing_permalinks ) ) ) {
474
+ wprss_log( "Importing (unique) feed item (Source: $feed_ID)", null, WPRSS_LOG_LEVEL_INFO );
475
+
476
+ // Extend the importing time and refresh the feed's updating flag to reflect that it is active
477
+ $extend_time = wprss_flag_feed_as_updating( $feed_ID );
478
+ $extend_time_f = date( 'Y-m-d H:i:s', $extend_time );
479
+ $time_limit = wprss_get_item_import_time_limit();
480
+ wprss_log( "Extended execution time limit by {$time_limit}. (Current Time: {$extend_time_f})", null, WPRSS_LOG_LEVEL_INFO );
481
+ set_time_limit( $time_limit );
482
+
483
+ // Apply filters that determine if the feed item should be inserted into the DB or not.
484
+ $item = apply_filters( 'wprss_insert_post_item_conditionals', $item, $feed_ID, $permalink );
485
+
486
+ // Check if the imported count should still be updated, even if the item is NULL
487
+ $still_update_count = apply_filters( 'wprss_still_update_import_count', FALSE );
488
+
489
+ // If the item is not NULL, continue to inserting the feed item post into the DB
490
+ if ( $item !== NULL && !is_bool($item) ) {
491
+ wprss_log( 'Using core logic', null, WPRSS_LOG_LEVEL_SYSTEM );
492
+
493
+ // Get the date and GTM date and normalize if not valid dor not present
494
+ $format = 'Y-m-d H:i:s';
495
+ $has_date = $item->get_date( 'U' ) ? TRUE : FALSE;
496
+ $timestamp = $has_date ? $item->get_date( 'U' ) : date( 'U' );
497
+ $date = date( $format, $timestamp );
498
+ $date_gmt = gmdate( $format, $timestamp );
499
+ // Prepare the item data
500
+ $feed_item = apply_filters(
501
+ 'wprss_populate_post_data',
502
+ array(
503
+ 'post_title' => html_entity_decode( $item->get_title() ),
504
+ 'post_content' => '',
505
+ 'post_status' => 'publish',
506
+ 'post_type' => 'wprss_feed_item',
507
+ 'post_date' => $date,
508
+ 'post_date_gmt' => $date_gmt
509
+ ),
510
+ $item
511
+ );
512
+ wprss_log( 'Post data filters applied', null, WPRSS_LOG_LEVEL_SYSTEM );
513
+
514
+ if ( defined('ICL_SITEPRESS_VERSION') )
515
+ @include_once( WP_PLUGIN_DIR . '/sitepress-multilingual-cms/inc/wpml-api.php' );
516
+ if ( defined('ICL_LANGUAGE_CODE') ) {
517
+ $_POST['icl_post_language'] = $language_code = ICL_LANGUAGE_CODE;
518
+ wprss_log_obj( 'WPML detected. Language code determined', $language_code, null, WPRSS_LOG_LEVEL_SYSTEM );
519
+ }
520
+
521
+ // Create and insert post object into the DB
522
+ $inserted_ID = wp_insert_post( $feed_item );
523
+
524
+ if ( !is_wp_error( $inserted_ID ) ) {
525
+
526
+ if ( is_object( $inserted_ID ) ) {
527
+ if ( isset( $inserted_ID['ID'] ) ) {
528
+ $inserted_ID = $inserted_ID['ID'];
529
+ }
530
+ elseif ( isset( $inserted_ID->ID ) ) {
531
+ $inserted_ID = $inserted_ID->ID;
532
+ }
533
+ }
534
+
535
+ // Increment the inserted items counter
536
+ $items_inserted++;
537
+
538
+ // Create and insert post meta into the DB
539
+ wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink, $enclosure_url );
540
+
541
+ // Remember newly added permalink
542
+ $existing_permalinks[$permalink] = 1;
543
+ wprss_log_obj( 'Item imported', $inserted_ID, null, WPRSS_LOG_LEVEL_INFO );
544
+ }
545
+ else {
546
+ update_post_meta( $source, 'wprss_error_last_import', 'An error occurred while inserting a feed item into the database.' );
547
+ wprss_log_obj( 'Failed to insert post', $feed_item, 'wprss_items_insert_post > wp_insert_post' );
548
+ }
549
+ }
550
+ // If the item is TRUE, then a hook function in the filter inserted the item.
551
+ // increment the inserted counter
552
+ elseif ( ( is_bool($item) && $item === TRUE ) || ( $still_update_count === TRUE && $item !== FALSE ) ) {
553
+ $items_inserted++;
554
+ }
555
+ }
556
+ else {
557
+ wprss_log( 'Item already exists and will be skipped', null, WPRSS_LOG_LEVEL_NOTICE );
558
+ }
559
+
560
+ wprss_log_obj( 'Finished importing item', $permalink, null, WPRSS_LOG_LEVEL_INFO );
561
+ }
562
+
563
+ update_post_meta( $feed_ID, 'wprss_last_update_items', $items_inserted );
564
+ wprss_log_obj( sprintf( 'Finished importing %1$d items for feed source', $items_inserted ), $feed_ID, null, WPRSS_LOG_LEVEL_INFO );
565
+ }
566
+
567
+
568
+ /**
569
+ * Inserts the appropriate post meta for feed items.
570
+ *
571
+ * Called from 'wprss_items_insert_post'
572
+ *
573
+ * @since 2.3
574
+ */
575
+ function wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink, $enclosure_url ) {
576
+ update_post_meta( $inserted_ID, 'wprss_item_permalink', $permalink );
577
+ update_post_meta( $inserted_ID, 'wprss_item_enclosure', $enclosure_url );
578
+
579
+ $author = $item->get_author();
580
+ if ( $author ) {
581
+ update_post_meta( $inserted_ID, 'wprss_item_author', $author->get_name() );
582
+ }
583
+
584
+ update_post_meta( $inserted_ID, 'wprss_feed_id', $feed_ID);
585
+ do_action( 'wprss_items_create_post_meta', $inserted_ID, $item, $feed_ID );
586
+ }
587
+
588
+
589
+ /**
590
+ * Returns the time limit for the importing of a single feed item.
591
+ * The value if filtered through 'wprss_item_import_time_limit'. The default value is WPRSS_ITEM_IMPORT_TIME_LIMIT.
592
+ *
593
+ * @since 4.6.6
594
+ * @return int The maximum amount of seconds allowed for a single feed item to import.
595
+ */
596
+ function wprss_get_item_import_time_limit() {
597
+ return apply_filters( 'wprss_item_import_time_limit', WPRSS_ITEM_IMPORT_TIME_LIMIT );
598
+ }
599
+
600
+ /**
601
+ * Returns the time limit for a feed fetch operation.
602
+ * The value if filtered through 'wprss_feed_fetch_time_limit'. The default value is WPRSS_FEED_FETCH_TIME_LIMIT.
603
+ *
604
+ * @since 4.6.6
605
+ * @return int The maximum amount of seconds allowed for an RSS feed XML document to be fetched.
606
+ */
607
+ function wprss_get_feed_fetch_time_limit() {
608
+ return apply_filters( 'wprss_feed_fetch_time_limit', WPRSS_FEED_FETCH_TIME_LIMIT );
609
+ }
610
+
611
+
612
+ /**
613
+ * Fetches all feed items from all feed sources.
614
+ * Iteratively calls 'wprss_fetch_insert_single_feed_items' for all feed sources.
615
+ *
616
+ * This function is used by the cron job or the debugging functions to get all feeds from all feed sources
617
+ *
618
+ * @param $all If set to TRUE, the function will pull from all feed sources, regardless of their individual
619
+ * update interval. If set to FALSE, only feed sources using the global update system will be updated.
620
+ * (Optional) Default: TRUE.
621
+ * @since 3.0
622
+ */
623
+ function wprss_fetch_insert_all_feed_items( $all = TRUE ) {
624
+ wprss_log( 'Importing from all sources...', __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM );
625
+ // Get all feed sources
626
+ $feed_sources = wprss_get_all_feed_sources();
627
+
628
+ if( $feed_sources->have_posts() ) {
629
+ // Start by getting one feed source, we will cycle through them one by one,
630
+ // fetching feed items and adding them to the database in each pass
631
+ while ( $feed_sources->have_posts() ) {
632
+ $feed_sources->the_post();
633
+
634
+ $interval = get_post_meta( get_the_ID(), 'wprss_update_interval', TRUE );
635
+ $using_global_interval = ( $interval === wprss_get_default_feed_source_update_interval() || $interval === '' );
636
+
637
+ // Check if fetching from all, or if feed source uses the global interval
638
+ if ( $all === TRUE || $using_global_interval ) {
639
+ wp_schedule_single_event( time(), 'wprss_fetch_single_feed_hook', array( get_the_ID() ) );
640
+ }
641
+ }
642
+ wp_reset_postdata(); // Restore the $post global to the current post in the main query
643
+ }
644
+ }
645
+
646
+
647
+ /**
648
+ * Runs the above function with parameter FALSE
649
+ *
650
+ * @since 3.9
651
+ */
652
+ function wprss_fetch_insert_all_feed_items_from_cron() {
653
+ wprss_fetch_insert_all_feed_items( FALSE );
654
+ }
655
+
656
+
657
+ /**
658
+ * Shutdown function for detecting if the PHP script reaches the maximum execution time limit
659
+ * while importing a feed.
660
+ *
661
+ * @since 4.6.6
662
+ */
663
+ function wprss_detect_exec_timeout() {
664
+ // Get last error
665
+ if ( $error = error_get_last() ){
666
+ // Check if it is an E_ERROR and if it is a max exec time limit error
667
+ if ( $error['type'] === E_ERROR && stripos( $error['message'], 'maximum execution' ) === 0 ) {
668
+ // If the importing process was running
669
+ if ( array_key_exists( 'wprss_importing_feed', $GLOBALS ) && $GLOBALS['wprss_importing_feed'] !== NULL ) {
670
+ // Get the ID of the feed that was importing
671
+ $feed_ID = $GLOBALS['wprss_importing_feed'];
672
+ // Perform clean up
673
+ wprss_flag_feed_as_idle( $feed_ID );
674
+ $msg = sprintf( __( 'The PHP script timed out while importing an item from this feed, after %d seconds.', WPRSS_TEXT_DOMAIN ), wprss_get_item_import_time_limit() );
675
+ update_post_meta( $feed_ID, 'wprss_error_last_import', $msg );
676
+ // Log the error
677
+ wprss_log( 'The PHP script timed out while importing feed #' . $feed_ID, NULL, WPRSS_LOG_LEVEL_ERROR );
678
+ }
679
+ }
680
+ }
681
  }
includes/feed-processing.php CHANGED
@@ -1,704 +1,728 @@
1
- <?php
2
-
3
- define( 'WPRSS_TRANSIENT_NAME_IS_REIMPORTING', 'is_reimporting' );
4
-
5
- /**
6
- * Feed processing related functions
7
- *
8
- * @package WPRSSAggregator
9
- */
10
-
11
- /**
12
- * Returns whether or not the feed source will forcefully fetch the next fetch,
13
- * ignoring whether or not it is paused or not.
14
- *
15
- * @param $source_id The ID of the feed soruce
16
- * @return boolean
17
- * @since 3.7
18
- */
19
- function wprss_feed_source_force_next_fetch( $source_id ) {
20
- $force = get_post_meta( $source_id, 'wprss_force_next_fetch', TRUE );
21
- return ( $force !== '' || $force == '1' );
22
- }
23
-
24
-
25
- /**
26
- * Change the default feed cache recreation period to 2 hours
27
- *
28
- * Probably not needed since we are now disabling caching altogether
29
- *
30
- * @since 2.1
31
- */
32
- function wprss_feed_cache_lifetime( $seconds ) {
33
- return 1; // one second
34
- }
35
-
36
-
37
- /**
38
- * Disable caching of feeds in transients, we don't need it as we are storing them in the wp_posts table
39
- *
40
- * @since 3.0
41
- */
42
- function wprss_do_not_cache_feeds( &$feed ) {
43
- $feed->enable_cache( false );
44
- }
45
-
46
-
47
- /**
48
- * Parameters for query to get all feed sources
49
- *
50
- * @since 3.0
51
- */
52
- function wprss_get_all_feed_sources() {
53
- // Get all feed sources
54
- $feed_sources = new WP_Query( apply_filters(
55
- 'wprss_get_all_feed_sources',
56
- array(
57
- 'post_type' => 'wprss_feed',
58
- 'post_status' => 'publish',
59
- 'cache_results' => false, // Disable caching, used for one-off queries
60
- 'no_found_rows' => true, // We don't need pagination, so disable it
61
- 'posts_per_page' => -1
62
- )
63
- ) );
64
- return $feed_sources;
65
- }
66
-
67
-
68
- /**
69
- * Returns all the feed items of a source.
70
- *
71
- * @since 3.8
72
- */
73
- function wprss_get_feed_items_for_source( $source_id ) {
74
- $args = apply_filters(
75
- 'wprss_get_feed_items_for_source_args',
76
- array(
77
- 'post_type' => 'wprss_feed_item',
78
- 'cache_results' => false, // Disable caching, used for one-off queries
79
- 'no_found_rows' => true, // We don't need pagination, so disable it
80
- 'posts_per_page'=> -1,
81
- 'orderby' => 'date',
82
- 'order' => 'DESC',
83
- 'meta_query' => array(
84
- array(
85
- 'key' => 'wprss_feed_id',
86
- 'value' => $source_id,
87
- 'compare' => '=',
88
- ),
89
- ),
90
- 'suppress_filters' => 1
91
- ), $source_id
92
- );
93
- return new WP_Query( $args );
94
- }
95
-
96
-
97
- /**
98
- * Parameters for query to get feed sources
99
- *
100
- * @since 3.0
101
- */
102
- function wprss_get_feed_source() {
103
- // Get all feed sources
104
- $feed_sources = new WP_Query( apply_filters(
105
- 'wprss_get_all_feed_sources',
106
- array(
107
- 'post_type' => 'wprss_feed',
108
- 'post_status' => 'publish',
109
- 'cache_results' => false, // Disable caching, used for one-off queries
110
- 'no_found_rows' => true, // We don't need pagination, so disable it
111
- 'posts_per_page' => -1
112
- )
113
- ) );
114
- return $feed_sources;
115
- }
116
-
117
-
118
- /**
119
- * Database query to get existing permalinks
120
- *
121
- * @since 3.0
122
- */
123
- function get_existing_permalinks( $feed_ID ) {
124
- global $wpdb;
125
-
126
- return $wpdb->get_col(
127
- "SELECT q.`meta_value`
128
- FROM {$wpdb->postmeta} AS p
129
- JOIN {$wpdb->postmeta} AS q ON (q.`meta_key` = 'wprss_item_permalink' AND p.`post_id` = q.`post_id`)
130
- WHERE p.`meta_key` = 'wprss_feed_id' AND p.`meta_value` = '{$feed_ID}'"
131
- );
132
- }
133
-
134
- /**
135
- * Checks if a permalink exists.
136
- *
137
- * Untested!
138
- *
139
- * @param string $permalink The permalink, expected to be normalized.
140
- * @return bool
141
- */
142
- function wprss_permalink_exists( $permalink ) {
143
- global $wpdb;
144
-
145
- $wpdb->query(
146
- $wpdb->prepare(
147
- "SELECT *
148
- FROM {$wpdb->postmeta}
149
- WHERE `meta_value` = '{$permalink}'"
150
- )
151
- );
152
-
153
- return $wpdb->num_rows > 0;
154
- }
155
-
156
-
157
- add_action( 'publish_wprss_feed', 'wprss_fetch_insert_feed_items', 10 );
158
- /**
159
- * Fetches feed items from source provided and inserts into db
160
- *
161
- * This function is used when inserting or untrashing a new feed source, it only gets feeds from that particular source
162
- *
163
- * @since 3.0
164
- */
165
- function wprss_fetch_insert_feed_items( $post_id ) {
166
- wp_schedule_single_event( time(), 'wprss_fetch_single_feed_hook', array( $post_id ) );
167
- }
168
-
169
-
170
- /**
171
- * Returns the image of the feed.
172
- * The reason this function exists is for add-ons to be able to detect if the plugin core
173
- * supports feed image functionality through a simple function_exists() call.
174
- *
175
- * @param $source_id The ID of the feed source
176
- * @return string The link to the feed image
177
- * @since 1.0
178
- */
179
- function wprss_get_feed_image( $source_id ) {
180
- return get_post_meta( $source_id, 'wprss_feed_image', true );
181
- }
182
-
183
-
184
- add_action( 'post_updated', 'wprss_updated_feed_source', 10, 3 );
185
- /**
186
- * This function is triggered just after a post is updated.
187
- * It checks if the updated post is a feed source, and carries out any
188
- * updating necassary.
189
- *
190
- * @since 3.3
191
- */
192
- function wprss_updated_feed_source( $post_ID, $post_after, $post_before ) {
193
- // Check if the post is a feed source and is published
194
-
195
- if ( ( $post_after->post_type == 'wprss_feed' ) && ( $post_after->post_status == 'publish' ) ) {
196
-
197
- if ( isset( $_POST['wprss_url'] ) && !empty( $_POST['wprss_url'] ) ) {
198
- $url = $_POST['wprss_url'];
199
- $feed = wprss_fetch_feed( $url );
200
- if ( $feed !== NULL && !is_wp_error( $feed ) ) {
201
- update_post_meta( $post_ID, 'wprss_site_url', $feed->get_permalink() );
202
- update_post_meta( $post_ID, 'wprss_feed_image', $feed->get_image_url() );
203
- }
204
- }
205
-
206
-
207
- if ( isset( $_POST['wprss_limit'] ) && !empty( $_POST['wprss_limit'] ) ) {
208
- // Checking feed limit change
209
- // Get the limit currently saved in db, and limit in POST request
210
- //$limit = get_post_meta( $post_ID, 'wprss_limit', true );
211
- $limit = $_POST['wprss_limit'];
212
- // Get all feed items for this source
213
- $feed_sources = new WP_Query(
214
- array(
215
- 'post_type' => 'wprss_feed_item',
216
- 'post_status' => 'publish',
217
- 'cache_results' => false, // Disable caching, used for one-off queries
218
- 'no_found_rows' => true, // We don't need pagination, so disable it
219
- 'posts_per_page' => -1,
220
- 'orderby' => 'date',
221
- 'order' => 'ASC',
222
- 'meta_query' => array(
223
- array(
224
- 'key' => 'wprss_feed_id',
225
- 'value' => $post_ID,
226
- 'compare' => 'LIKE'
227
- )
228
- )
229
- )
230
- );
231
- // If the limit is smaller than the number of found posts, delete the feed items
232
- // and re-import, to ensure that most recent feed items are present.
233
- $difference = intval( $feed_sources->post_count ) - intval( $limit );
234
- if ( $difference > 0 ) {
235
- // Loop and delete the excess feed items
236
- while ( $feed_sources->have_posts() && $difference > 0 ) {
237
- $feed_sources->the_post();
238
- wp_delete_post( get_the_ID(), true );
239
- $difference--;
240
- }
241
- }
242
- }
243
- }
244
- }
245
-
246
-
247
- add_action( 'added_post_meta', 'wprss_update_feed_meta', 10, 4 );
248
- add_action( 'updated_post_meta', 'wprss_update_feed_meta', 10, 4 );
249
- /**
250
- * This function is run whenever a post is saved or updated.
251
- *
252
- * @since 3.4
253
- */
254
- function wprss_update_feed_meta( $meta_id, $post_id, $meta_key, $meta_value ) {
255
- $post = get_post( $post_id );
256
- if ( $post !== NULL && $post->post_status === 'publish' && $post->post_type === 'wprss_feed' ) {
257
- if ( $meta_key === 'wprss_url' )
258
- wprss_change_fb_url( $post_id, $meta_value );
259
- }
260
- }
261
-
262
-
263
- function wprss_change_fb_url( $post_id, $url ) {
264
- # Check if url begins with a known facebook hostname.
265
- if ( stripos( $url, 'http://facebook.com' ) === 0
266
- || stripos( $url, 'http://www.facebook.com' ) === 0
267
- || stripos( $url, 'https://facebook.com' ) === 0
268
- || stripos( $url, 'https://www.facebook.com' ) === 0
269
- ) {
270
- # Generate the new URL to FB Graph
271
- $com_index = stripos( $url, '.com' );
272
- $fb_page = substr( $url, $com_index + 4 ); # 4 = length of ".com"
273
- $fb_graph_url = 'http://graph.facebook.com' . $fb_page;
274
- # Contact FB Graph and get data
275
- $response = wp_remote_get( $fb_graph_url );
276
- # If the repsonse successful and has a body
277
- if ( !is_wp_error( $response ) && isset( $response['body'] ) ) {
278
- # Parse the body as a JSON string
279
- $json = json_decode( $response['body'], true );
280
- # If an id is present ...
281
- if ( isset( $json['id'] ) ) {
282
- # Generate the final URL for this feed and update the post meta
283
- $final_url = "http://www.facebook.com/feeds/page.php?format=rss20&id=" . $json['id'];
284
- update_post_meta( $post_id, 'wprss_url', $final_url, $url );
285
- }
286
- }
287
- }
288
- }
289
-
290
-
291
- add_action( 'trash_wprss_feed', 'wprss_delete_feed_items' ); // maybe use wp_trash_post action? wp_trash_wprss_feed
292
- /**
293
- * Delete feed items on trashing of corresponding feed source
294
- *
295
- * @since 2.0
296
- */
297
- function wprss_delete_feed_items( $postid ) {
298
-
299
- $args = array(
300
- 'post_type' => 'wprss_feed_item',
301
- // Next 3 parameters for performance, see http://thomasgriffinmedia.com/blog/2012/10/optimize-wordpress-queries
302
- 'cache_results' => false, // Disable caching, used for one-off queries
303
- 'no_found_rows' => true, // We don't need pagination, so disable it
304
- 'fields' => 'ids', // Returns post IDs only
305
- 'posts_per_page'=> -1,
306
- 'meta_query' => array(
307
- array(
308
- 'key' => 'wprss_feed_id',
309
- 'value' => $postid,
310
- 'compare' => 'LIKE'
311
- )
312
- )
313
- );
314
-
315
- $feed_item_ids = get_posts( $args );
316
- foreach( $feed_item_ids as $feed_item_id ) {
317
- $purge = wp_delete_post( $feed_item_id, true ); // delete the feed item, skipping trash
318
- }
319
- wp_reset_postdata();
320
- }
321
-
322
-
323
- add_action( 'wprss_delete_all_feed_items_hook', 'wprss_delete_all_feed_items' );
324
- /**
325
- * Delete all feed items
326
- *
327
- * @since 3.0
328
- */
329
- function wprss_delete_all_feed_items() {
330
- wprss_log( sprintf( 'Deleting all feed items...'), __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM );
331
- $args = array(
332
- 'post_type' => 'wprss_feed_item',
333
- 'cache_results' => false, // Disable caching, used for one-off queries
334
- 'no_found_rows' => true, // We don't need pagination, so disable it
335
- 'fields' => 'ids', // Returns post IDs only
336
- 'posts_per_page' => -1,
337
- );
338
-
339
- $feed_item_ids = get_posts( $args );
340
- foreach( $feed_item_ids as $feed_item_id ) {
341
- $purge = wp_delete_post( $feed_item_id, true ); // delete the feed item, skipping trash
342
- }
343
- wp_reset_postdata();
344
- wprss_log( sprintf( 'All feed items deleted: %1$d', count($feed_item_ids) ), __FUNCTION__, WPRSS_LOG_LEVEL_INFO );
345
- do_action('wprss_delete_all_feed_items_after', $feed_item_ids);
346
- }
347
-
348
-
349
- /**
350
- * Marks the feed source as 'updating' (importing).
351
- *
352
- * @since 4.6.6
353
- * @return int The time value set in the 'updating' meta field
354
- */
355
- function wprss_flag_feed_as_updating( $feed_ID ) {
356
- update_post_meta( $feed_ID, 'wprss_feed_is_updating', $start_time = time() );
357
- return $start_time;
358
- }
359
-
360
- /**
361
- * Marks the feed source as 'idle' (not importing).
362
- *
363
- * @since 4.6.6
364
- */
365
- function wprss_flag_feed_as_idle( $feed_ID ) {
366
- delete_post_meta( $feed_ID, 'wprss_feed_is_updating' );
367
- }
368
-
369
-
370
- /**
371
- * Returns whether or not the feed source is updating.
372
- *
373
- * @param (string|int) The id of the feed source
374
- * @return (bool) TRUE if the feed source is currently updating, FALSE otherwise.
375
- *
376
- */
377
- function wprss_is_feed_source_updating( $id ) {
378
- // Get the 'updating' meta field
379
- $is_updating_meta = get_post_meta( $id, 'wprss_feed_is_updating', TRUE );
380
-
381
- // Check if the feed has the 'updating' meta field set
382
- if ( $is_updating_meta === '' ) {
383
- // If not, then the feed is not updating
384
- return FALSE;
385
- }
386
-
387
- // Get the limit used for the feed
388
- $limit = get_post_meta( $id, 'wprss_limit', true );
389
- if ( $limit === '' || intval( $limit ) <= 0 ) {
390
- $global_limit = wprss_get_general_setting('limit_feed_items_imported');
391
- $limit = ( $global_limit === '' || intval( $global_limit ) <= 0 ) ? NULL : $global_limit;
392
- }
393
-
394
- // Calculate the allowed maximum time, based on the maximum number of items allowed to be
395
- // imported from this source.
396
- // If no limit is used, 60s (1min) is used.
397
- $single_item_time_limit = wprss_get_item_import_time_limit();
398
- $allowed_time = $limit === NULL ? 60 : $single_item_time_limit * intval( $limit );
399
-
400
- // Calculate how many seconds have passed since the feed last signalled that it is updating
401
- $diff = time() - $is_updating_meta;
402
-
403
- // If the difference is greater than the allowed maximum amount of time, mark the feed as idle.
404
- if ( $diff > $allowed_time ) {
405
- wprss_flag_feed_as_idle( $id );
406
- // Feed is not updating
407
- return FALSE;
408
- }
409
-
410
- // Feed is updating
411
- return TRUE;
412
- }
413
-
414
-
415
- /**
416
- * Returns whether or not the feed source is deleting its feeed items.
417
- *
418
- * @param (string|int) The id of the feed source
419
- * @return (bool) TRUE if the feed source is currently deleting its items, FALSE otherwise.
420
- *
421
- */
422
- function wprss_is_feed_source_deleting( $id ) {
423
- $is_deleting_meta = get_post_meta( $id, 'wprss_feed_is_deleting_items', TRUE );
424
-
425
- if ( $is_deleting_meta === '' ) {
426
- return FALSE;
427
- }
428
-
429
- $diff = time() - $is_deleting_meta;
430
-
431
- $items = wprss_get_feed_items_for_source( $id );
432
- if ( $items->post_count == 0 || $diff > 300 ) {
433
- delete_post_meta( $id, 'wprss_feed_is_deleting_items' );
434
- return FALSE;
435
- }
436
-
437
- return TRUE;
438
- }
439
-
440
-
441
- /**
442
- * Returns the given parameter as a string. Used in wprss_truncate_posts()
443
- *
444
- * @return string The given parameter as a string
445
- * @since 3.5.1
446
- */
447
- function wprss_return_as_string( $item ) {
448
- return "'$item'";
449
- }
450
-
451
-
452
- /**
453
- * Returns true if the feed item is older than the given timestamp,
454
- * false otherwise;
455
- *
456
- * @since 3.8
457
- */
458
- function wprss_is_feed_item_older_than( $id, $timestamp ) {
459
- // GET THE DATE
460
- $age = get_the_time( 'U', $id );
461
- if ( $age === '' ) return FALSE;
462
- // Calculate the age difference
463
- $difference = $age - $timestamp;
464
- // Return whether the difference is negative ( the age is smaller than the timestamp )
465
- return ( $difference <= 0 );
466
- }
467
-
468
-
469
- /**
470
- * Returns the maximum age setting for a feed source.
471
- *
472
- * @since 3.8
473
- */
474
- function wprss_get_max_age_for_feed_source( $source_id ) {
475
- $general_settings = get_option( 'wprss_settings_general' );
476
- // Get the meta data for age for this feed source
477
- $age_limit = trim( get_post_meta( $source_id, 'wprss_age_limit', TRUE ) );
478
- $age_unit = get_post_meta( $source_id, 'wprss_age_unit', TRUE );
479
-
480
- // If the meta does not exist, use the global settings
481
- if( $age_limit === '' ) {
482
- $age_limit = trim( wprss_get_general_setting( 'limit_feed_items_age' ) );
483
- $age_unit = wprss_get_general_setting( 'limit_feed_items_age_unit' );
484
- }
485
-
486
- // If the age limit is an empty string, use no limit
487
- if ( $age_limit === '' ) {
488
- return FALSE;
489
- }
490
-
491
- // Return the timestamp of the max age date
492
- return strtotime( "-$age_limit $age_unit" );
493
- }
494
-
495
-
496
- /**
497
- * Delete old feed items from the database to avoid bloat.
498
- * As of 3.8, it uses the new feed age system.
499
- *
500
- * @since 3.8
501
- */
502
- function wprss_truncate_posts() {
503
- // Get general settings
504
- $general_settings = get_option( 'wprss_settings_general' );
505
- // Get all feed sources
506
- $feed_sources = wprss_get_all_feed_sources();
507
-
508
- // Check if there are feed sources
509
- if( $feed_sources->have_posts() ) {
510
- // FOR EACH FEED SOURCE
511
- while ( $feed_sources->have_posts() ) {
512
- $feed_sources->the_post();
513
- // Get the max age setting for this feed source
514
- $max_age = wprss_get_max_age_for_feed_source( get_the_ID() );
515
-
516
- // If the data is empty, do not delete
517
- if ( $max_age === FALSE ) continue;
518
-
519
- // Get all feed items for this source
520
- $feed_items = wprss_get_feed_items_for_source( get_the_ID() );
521
- // If there are feed items
522
- if ( $feed_items-> have_posts() ) {
523
- // Extend the timeout time limit for the deletion of the feed items
524
- $time_limit = wprss_get_item_import_time_limit();
525
- wprss_log( "Extended execution time limit by {$time_limit}s for imported items truncation.", null, WPRSS_LOG_LEVEL_INFO );
526
- set_time_limit( $time_limit );
527
- // For each feed item
528
- while ( $feed_items->have_posts() ) {
529
- $feed_items->the_post();
530
- // If the post is older than the maximum age
531
- if ( wprss_is_feed_item_older_than( get_the_ID(), $max_age ) === TRUE ){
532
- // Delete the post
533
- wp_delete_post( get_the_ID(), true );
534
- }
535
- }
536
- // Reset feed items query data
537
- wp_reset_postdata();
538
- }
539
- }
540
- // Reset feed sources query data
541
- wp_reset_postdata();
542
- }
543
-
544
- // If the filter to use the fixed limit is enabled, call the old truncation function
545
- if ( apply_filters( 'wprss_use_fixed_feed_limit', FALSE ) === TRUE && isset( $general_settings['limit_feed_items_db'] ) ) {
546
- wprss_old_truncate_posts();
547
- }
548
- }
549
-
550
-
551
- /**
552
- * The old truncation function.
553
- * This truncation method uses the deprecated fixed feed limit.
554
- *
555
- * @since 2.0
556
- */
557
- function wprss_old_truncate_posts() {
558
- global $wpdb;
559
- $general_settings = get_option( 'wprss_settings_general' );
560
-
561
- if ( $general_settings['limit_feed_items_db'] == 0 ) {
562
- return;
563
- }
564
-
565
- // Set your threshold of max posts and post_type name
566
- $threshold = $general_settings['limit_feed_items_db'];
567
- $post_types = apply_filters( 'wprss_truncation_post_types', array( 'wprss_feed_item' ) );
568
- $post_types_str = array_map( 'wprss_return_as_string', $post_types );
569
-
570
- $post_type_list = implode( ',' , $post_types_str );
571
-
572
- // Query post type
573
- // $wpdb query allows me to select specific columns instead of grabbing the entire post object.
574
- $query = "
575
- SELECT ID, post_title FROM $wpdb->posts
576
- WHERE post_type IN ($post_type_list)
577
- AND post_status = 'publish'
578
- ORDER BY post_modified DESC
579
- ";
580
-
581
- $results = $wpdb->get_results( $query );
582
-
583
- // Check if there are any results
584
- $i = 0;
585
- if ( count( $results ) ){
586
- foreach ( $results as $post ) {
587
- $i++;
588
-
589
- // Skip any posts within our threshold
590
- if ( $i <= $threshold )
591
- continue;
592
-
593
- // Let the WordPress API do the heavy lifting for cleaning up entire post trails
594
- $purge = wp_delete_post( $post->ID, true );
595
- }
596
- }
597
- }
598
-
599
-
600
- add_filter( 'wprss_insert_post_item_conditionals', 'wprss_check_feed_item_date_on_import', 2, 3 );
601
- /**
602
- * When a feed item is imported, it's date is compared against the max age of it's feed source.
603
- *
604
- *
605
- * @since 3.8
606
- */
607
- function wprss_check_feed_item_date_on_import( $item, $source, $permalink ){
608
- if ( $item === NULL ) return NULL;
609
-
610
- // Get the age of the item and the max age setting for its feed source
611
- $age = $item->get_date( 'U' );
612
- $max_age = wprss_get_max_age_for_feed_source( $source );
613
-
614
- // If the age is not a valid timestamp, and the max age setting is disabled, return the item
615
- if ( $age === '' || $age === NULL || $max_age === FALSE || $max_age === NULL ) {
616
- return $item;
617
- }
618
-
619
- // Calculate the age difference
620
- $difference = $age - $max_age;
621
-
622
- if ( $difference <= 0 ) {
623
- return NULL;
624
- } else {
625
- return $item;
626
- }
627
- }
628
-
629
-
630
- /**
631
- * Deletes all imported feeds and re-imports everything
632
- *
633
- * @since 3.0
634
- */
635
- function wprss_feed_reset() {
636
- wp_schedule_single_event( time(), 'wprss_delete_all_feed_items_hook' );
637
- set_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING, true );
638
- }
639
-
640
-
641
-
642
- function wprss_schedule_reimport_all($deleted_ids) {
643
- if( !get_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING ) )
644
- return;
645
-
646
- wprss_log( 'Re-import scheduled...', __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM);
647
- delete_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING );
648
- wprss_fetch_insert_all_feed_items( TRUE );
649
- }
650
- add_action('wprss_delete_all_feed_items_after', 'wprss_schedule_reimport_all');
651
-
652
-
653
- /**
654
- * Deletes N oldest feed items for the given source
655
- *
656
- * @since 4.2
657
- * @deprecated
658
- */
659
- function wprss_delete_oldest_feed_items( $n, $source ) {
660
- // If the source does not exist, do nothing
661
- if ( get_post( $source ) == NULL ) return;
662
-
663
- // Make sure $n is an integer
664
- $n = intval($n);
665
-
666
- // Do nothing if n is zero or negative
667
- if ( $n <= 0 ) return;
668
-
669
- // Get the feed items, as an array, not WP_Query.
670
- // We will need to perform some array operations
671
- $feed_items = wprss_get_feed_items_for_source( $source );
672
- $feed_items = $feed_items->get_posts();
673
- // Get number of feed items
674
- $count = count( $feed_items );
675
-
676
- // Index of first feed item to delete
677
- $start = $count - $n;
678
- // Cut the array of feed items to get the items to delete
679
- $to_delete = array_slice( $feed_items, $start );
680
- // log -- for now
681
- foreach( $to_delete as $fi ) {
682
- //wprss_log_obj( "To delete" , $fi->ID );
683
- }
684
- }
685
-
686
-
687
- /**
688
- * Deletes the required number of feed items for the given source,
689
- * to keep the number of feed items below its limit.
690
- *
691
- * @since 4.2
692
- * @deprecated
693
- */
694
- function wprss_truncate_feed_items_for_source( $source ) {
695
- // Get the limit setting
696
- $limit = get_post_meta( $source, 'wprss_limit', true );
697
-
698
- // Calculate the number of feed items to delete
699
- $feed_items = wprss_get_feed_items_for_source( $source );
700
- $n = intval($feed_items->found_posts) - intval($limit);
701
-
702
- // Delete the feed items
703
- wprss_delete_oldest_feed_items( $n, $source );
704
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ define( 'WPRSS_TRANSIENT_NAME_IS_REIMPORTING', 'is_reimporting' );
4
+
5
+ /**
6
+ * Feed processing related functions
7
+ *
8
+ * @package WPRSSAggregator
9
+ */
10
+
11
+ /**
12
+ * Returns whether or not the feed source will forcefully fetch the next fetch,
13
+ * ignoring whether or not it is paused or not.
14
+ *
15
+ * @param $source_id The ID of the feed soruce
16
+ * @return boolean
17
+ * @since 3.7
18
+ */
19
+ function wprss_feed_source_force_next_fetch( $source_id ) {
20
+ $force = get_post_meta( $source_id, 'wprss_force_next_fetch', TRUE );
21
+ return ( $force !== '' || $force == '1' );
22
+ }
23
+
24
+
25
+ /**
26
+ * Change the default feed cache recreation period to 2 hours
27
+ *
28
+ * Probably not needed since we are now disabling caching altogether
29
+ *
30
+ * @since 2.1
31
+ */
32
+ function wprss_feed_cache_lifetime( $seconds ) {
33
+ return 1; // one second
34
+ }
35
+
36
+
37
+ /**
38
+ * Disable caching of feeds in transients, we don't need it as we are storing them in the wp_posts table
39
+ *
40
+ * @since 3.0
41
+ */
42
+ function wprss_do_not_cache_feeds( &$feed ) {
43
+ $feed->enable_cache( false );
44
+ }
45
+
46
+
47
+ /**
48
+ * Parameters for query to get all feed sources
49
+ *
50
+ * @since 3.0
51
+ */
52
+ function wprss_get_all_feed_sources() {
53
+ // Get all feed sources
54
+ $feed_sources = new WP_Query( apply_filters(
55
+ 'wprss_get_all_feed_sources',
56
+ array(
57
+ 'post_type' => 'wprss_feed',
58
+ 'post_status' => 'publish',
59
+ 'cache_results' => false, // Disable caching, used for one-off queries
60
+ 'no_found_rows' => true, // We don't need pagination, so disable it
61
+ 'posts_per_page' => -1
62
+ )
63
+ ) );
64
+ return $feed_sources;
65
+ }
66
+
67
+
68
+ /**
69
+ * Returns all the feed items of a source.
70
+ *
71
+ * @since 3.8
72
+ */
73
+ function wprss_get_feed_items_for_source( $source_id ) {
74
+ $args = apply_filters(
75
+ 'wprss_get_feed_items_for_source_args',
76
+ array(
77
+ 'post_type' => 'wprss_feed_item',
78
+ 'cache_results' => false, // Disable caching, used for one-off queries
79
+ 'no_found_rows' => true, // We don't need pagination, so disable it
80
+ 'posts_per_page'=> -1,
81
+ 'orderby' => 'date',
82
+ 'order' => 'DESC',
83
+ 'meta_query' => array(
84
+ array(
85
+ 'key' => 'wprss_feed_id',
86
+ 'value' => $source_id,
87
+ 'compare' => '=',
88
+ ),
89
+ ),
90
+ 'suppress_filters' => 1
91
+ ), $source_id
92
+ );
93
+ return new WP_Query( $args );
94
+ }
95
+
96
+
97
+ /**
98
+ * Parameters for query to get feed sources
99
+ *
100
+ * @since 3.0
101
+ */
102
+ function wprss_get_feed_source() {
103
+ // Get all feed sources
104
+ $feed_sources = new WP_Query( apply_filters(
105
+ 'wprss_get_all_feed_sources',
106
+ array(
107
+ 'post_type' => 'wprss_feed',
108
+ 'post_status' => 'publish',
109
+ 'cache_results' => false, // Disable caching, used for one-off queries
110
+ 'no_found_rows' => true, // We don't need pagination, so disable it
111
+ 'posts_per_page' => -1
112
+ )
113
+ ) );
114
+ return $feed_sources;
115
+ }
116
+
117
+
118
+ /**
119
+ * Database query to get existing permalinks
120
+ *
121
+ * @since 3.0
122
+ */
123
+ function wprss_get_existing_permalinks( $feed_ID ) {
124
+ global $wpdb;
125
+
126
+ $cols = $wpdb->get_col(
127
+ "SELECT q.`meta_value`
128
+ FROM {$wpdb->postmeta} AS p
129
+ JOIN {$wpdb->postmeta} AS q ON (q.`meta_key` = 'wprss_item_permalink' AND p.`post_id` = q.`post_id`)
130
+ WHERE p.`meta_key` = 'wprss_feed_id' AND p.`meta_value` = '{$feed_ID}'"
131
+ );
132
+
133
+ return array_flip( $cols );
134
+ }
135
+
136
+
137
+ /**
138
+ * Database query to get existing titles
139
+ *
140
+ * @since 4.7
141
+ */
142
+ function wprss_get_existing_titles( $feed_ID = NULL ) {
143
+ global $wpdb;
144
+
145
+ $condition = ($feed_ID !== NULL) ? "AND q.`meta_value` = '{$feed_ID}'" : '';
146
+
147
+ $cols = $wpdb->get_col(
148
+ "SELECT p.`post_title`
149
+ FROM `{$wpdb->posts}` AS p
150
+ JOIN `{$wpdb->postmeta}` AS q ON p.`ID` = q.`post_id`
151
+ WHERE q.`meta_key` = 'wprss_feed_id' $condition"
152
+ );
153
+
154
+ return array_flip( $cols );
155
+ }
156
+
157
+
158
+ /**
159
+ * Checks if a permalink exists.
160
+ *
161
+ * Untested!
162
+ *
163
+ * @param string $permalink The permalink, expected to be normalized.
164
+ * @return bool
165
+ */
166
+ function wprss_permalink_exists( $permalink ) {
167
+ global $wpdb;
168
+
169
+ $wpdb->query(
170
+ $wpdb->prepare(
171
+ "SELECT *
172
+ FROM {$wpdb->postmeta}
173
+ WHERE `meta_value` = '{$permalink}'"
174
+ )
175
+ );
176
+
177
+ return $wpdb->num_rows > 0;
178
+ }
179
+
180
+
181
+ add_action( 'publish_wprss_feed', 'wprss_fetch_insert_feed_items', 10 );
182
+ /**
183
+ * Fetches feed items from source provided and inserts into db
184
+ *
185
+ * This function is used when inserting or untrashing a new feed source, it only gets feeds from that particular source
186
+ *
187
+ * @since 3.0
188
+ */
189
+ function wprss_fetch_insert_feed_items( $post_id ) {
190
+ wp_schedule_single_event( time(), 'wprss_fetch_single_feed_hook', array( $post_id ) );
191
+ }
192
+
193
+
194
+ /**
195
+ * Returns the image of the feed.
196
+ * The reason this function exists is for add-ons to be able to detect if the plugin core
197
+ * supports feed image functionality through a simple function_exists() call.
198
+ *
199
+ * @param $source_id The ID of the feed source
200
+ * @return string The link to the feed image
201
+ * @since 1.0
202
+ */
203
+ function wprss_get_feed_image( $source_id ) {
204
+ return get_post_meta( $source_id, 'wprss_feed_image', true );
205
+ }
206
+
207
+
208
+ add_action( 'post_updated', 'wprss_updated_feed_source', 10, 3 );
209
+ /**
210
+ * This function is triggered just after a post is updated.
211
+ * It checks if the updated post is a feed source, and carries out any
212
+ * updating necassary.
213
+ *
214
+ * @since 3.3
215
+ */
216
+ function wprss_updated_feed_source( $post_ID, $post_after, $post_before ) {
217
+ // Check if the post is a feed source and is published
218
+
219
+ if ( ( $post_after->post_type == 'wprss_feed' ) && ( $post_after->post_status == 'publish' ) ) {
220
+
221
+ if ( isset( $_POST['wprss_url'] ) && !empty( $_POST['wprss_url'] ) ) {
222
+ $url = $_POST['wprss_url'];
223
+ $feed = wprss_fetch_feed( $url );
224
+ if ( $feed !== NULL && !is_wp_error( $feed ) ) {
225
+ update_post_meta( $post_ID, 'wprss_site_url', $feed->get_permalink() );
226
+ update_post_meta( $post_ID, 'wprss_feed_image', $feed->get_image_url() );
227
+ }
228
+ }
229
+
230
+
231
+ if ( isset( $_POST['wprss_limit'] ) && !empty( $_POST['wprss_limit'] ) ) {
232
+ // Checking feed limit change
233
+ // Get the limit currently saved in db, and limit in POST request
234
+ //$limit = get_post_meta( $post_ID, 'wprss_limit', true );
235
+ $limit = $_POST['wprss_limit'];
236
+ // Get all feed items for this source
237
+ $feed_sources = new WP_Query(
238
+ array(
239
+ 'post_type' => 'wprss_feed_item',
240
+ 'post_status' => 'publish',
241
+ 'cache_results' => false, // Disable caching, used for one-off queries
242
+ 'no_found_rows' => true, // We don't need pagination, so disable it
243
+ 'posts_per_page' => -1,
244
+ 'orderby' => 'date',
245
+ 'order' => 'ASC',
246
+ 'meta_query' => array(
247
+ array(
248
+ 'key' => 'wprss_feed_id',
249
+ 'value' => $post_ID,
250
+ 'compare' => 'LIKE'
251
+ )
252
+ )
253
+ )
254
+ );
255
+ // If the limit is smaller than the number of found posts, delete the feed items
256
+ // and re-import, to ensure that most recent feed items are present.
257
+ $difference = intval( $feed_sources->post_count ) - intval( $limit );
258
+ if ( $difference > 0 ) {
259
+ // Loop and delete the excess feed items
260
+ while ( $feed_sources->have_posts() && $difference > 0 ) {
261
+ $feed_sources->the_post();
262
+ wp_delete_post( get_the_ID(), true );
263
+ $difference--;
264
+ }
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+
271
+ add_action( 'added_post_meta', 'wprss_update_feed_meta', 10, 4 );
272
+ add_action( 'updated_post_meta', 'wprss_update_feed_meta', 10, 4 );
273
+ /**
274
+ * This function is run whenever a post is saved or updated.
275
+ *
276
+ * @since 3.4
277
+ */
278
+ function wprss_update_feed_meta( $meta_id, $post_id, $meta_key, $meta_value ) {
279
+ $post = get_post( $post_id );
280
+ if ( $post !== NULL && $post->post_status === 'publish' && $post->post_type === 'wprss_feed' ) {
281
+ if ( $meta_key === 'wprss_url' )
282
+ wprss_change_fb_url( $post_id, $meta_value );
283
+ }
284
+ }
285
+
286
+
287
+ function wprss_change_fb_url( $post_id, $url ) {
288
+ # Check if url begins with a known facebook hostname.
289
+ if ( stripos( $url, 'http://facebook.com' ) === 0
290
+ || stripos( $url, 'http://www.facebook.com' ) === 0
291
+ || stripos( $url, 'https://facebook.com' ) === 0
292
+ || stripos( $url, 'https://www.facebook.com' ) === 0
293
+ ) {
294
+ # Generate the new URL to FB Graph
295
+ $com_index = stripos( $url, '.com' );
296
+ $fb_page = substr( $url, $com_index + 4 ); # 4 = length of ".com"
297
+ $fb_graph_url = 'http://graph.facebook.com' . $fb_page;
298
+ # Contact FB Graph and get data
299
+ $response = wp_remote_get( $fb_graph_url );
300
+ # If the repsonse successful and has a body
301
+ if ( !is_wp_error( $response ) && isset( $response['body'] ) ) {
302
+ # Parse the body as a JSON string
303
+ $json = json_decode( $response['body'], true );
304
+ # If an id is present ...
305
+ if ( isset( $json['id'] ) ) {
306
+ # Generate the final URL for this feed and update the post meta
307
+ $final_url = "http://www.facebook.com/feeds/page.php?format=rss20&id=" . $json['id'];
308
+ update_post_meta( $post_id, 'wprss_url', $final_url, $url );
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+
315
+ add_action( 'trash_wprss_feed', 'wprss_delete_feed_items' ); // maybe use wp_trash_post action? wp_trash_wprss_feed
316
+ /**
317
+ * Delete feed items on trashing of corresponding feed source
318
+ *
319
+ * @since 2.0
320
+ */
321
+ function wprss_delete_feed_items( $postid ) {
322
+
323
+ $args = array(
324
+ 'post_type' => 'wprss_feed_item',
325
+ // Next 3 parameters for performance, see http://thomasgriffinmedia.com/blog/2012/10/optimize-wordpress-queries
326
+ 'cache_results' => false, // Disable caching, used for one-off queries
327
+ 'no_found_rows' => true, // We don't need pagination, so disable it
328
+ 'fields' => 'ids', // Returns post IDs only
329
+ 'posts_per_page'=> -1,
330
+ 'meta_query' => array(
331
+ array(
332
+ 'key' => 'wprss_feed_id',
333
+ 'value' => $postid,
334
+ 'compare' => 'LIKE'
335
+ )
336
+ )
337
+ );
338
+
339
+ $feed_item_ids = get_posts( $args );
340
+ foreach( $feed_item_ids as $feed_item_id ) {
341
+ $purge = wp_delete_post( $feed_item_id, true ); // delete the feed item, skipping trash
342
+ }
343
+ wp_reset_postdata();
344
+ }
345
+
346
+
347
+ add_action( 'wprss_delete_all_feed_items_hook', 'wprss_delete_all_feed_items' );
348
+ /**
349
+ * Delete all feed items
350
+ *
351
+ * @since 3.0
352
+ */
353
+ function wprss_delete_all_feed_items() {
354
+ wprss_log( sprintf( 'Deleting all feed items...'), __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM );
355
+ $args = array(
356
+ 'post_type' => 'wprss_feed_item',
357
+ 'cache_results' => false, // Disable caching, used for one-off queries
358
+ 'no_found_rows' => true, // We don't need pagination, so disable it
359
+ 'fields' => 'ids', // Returns post IDs only
360
+ 'posts_per_page' => -1,
361
+ );
362
+
363
+ $feed_item_ids = get_posts( $args );
364
+ foreach( $feed_item_ids as $feed_item_id ) {
365
+ $purge = wp_delete_post( $feed_item_id, true ); // delete the feed item, skipping trash
366
+ }
367
+ wp_reset_postdata();
368
+ wprss_log( sprintf( 'All feed items deleted: %1$d', count($feed_item_ids) ), __FUNCTION__, WPRSS_LOG_LEVEL_INFO );
369
+ do_action('wprss_delete_all_feed_items_after', $feed_item_ids);
370
+ }
371
+
372
+
373
+ /**
374
+ * Marks the feed source as 'updating' (importing).
375
+ *
376
+ * @since 4.6.6
377
+ * @return int The time value set in the 'updating' meta field
378
+ */
379
+ function wprss_flag_feed_as_updating( $feed_ID ) {
380
+ update_post_meta( $feed_ID, 'wprss_feed_is_updating', $start_time = time() );
381
+ return $start_time;
382
+ }
383
+
384
+ /**
385
+ * Marks the feed source as 'idle' (not importing).
386
+ *
387
+ * @since 4.6.6
388
+ */
389
+ function wprss_flag_feed_as_idle( $feed_ID ) {
390
+ delete_post_meta( $feed_ID, 'wprss_feed_is_updating' );
391
+ }
392
+
393
+
394
+ /**
395
+ * Returns whether or not the feed source is updating.
396
+ *
397
+ * @param (string|int) The id of the feed source
398
+ * @return (bool) TRUE if the feed source is currently updating, FALSE otherwise.
399
+ *
400
+ */
401
+ function wprss_is_feed_source_updating( $id ) {
402
+ // Get the 'updating' meta field
403
+ $is_updating_meta = get_post_meta( $id, 'wprss_feed_is_updating', TRUE );
404
+
405
+ // Check if the feed has the 'updating' meta field set
406
+ if ( $is_updating_meta === '' ) {
407
+ // If not, then the feed is not updating
408
+ return FALSE;
409
+ }
410
+
411
+ // Get the limit used for the feed
412
+ $limit = get_post_meta( $id, 'wprss_limit', true );
413
+ if ( $limit === '' || intval( $limit ) <= 0 ) {
414
+ $global_limit = wprss_get_general_setting('limit_feed_items_imported');
415
+ $limit = ( $global_limit === '' || intval( $global_limit ) <= 0 ) ? NULL : $global_limit;
416
+ }
417
+
418
+ // Calculate the allowed maximum time, based on the maximum number of items allowed to be
419
+ // imported from this source.
420
+ // If no limit is used, 60s (1min) is used.
421
+ $single_item_time_limit = wprss_get_item_import_time_limit();
422
+ $allowed_time = $limit === NULL ? 60 : $single_item_time_limit * intval( $limit );
423
+
424
+ // Calculate how many seconds have passed since the feed last signalled that it is updating
425
+ $diff = time() - $is_updating_meta;
426
+
427
+ // If the difference is greater than the allowed maximum amount of time, mark the feed as idle.
428
+ if ( $diff > $allowed_time ) {
429
+ wprss_flag_feed_as_idle( $id );
430
+ // Feed is not updating
431
+ return FALSE;
432
+ }
433
+
434
+ // Feed is updating
435
+ return TRUE;
436
+ }
437
+
438
+
439
+ /**
440
+ * Returns whether or not the feed source is deleting its feeed items.
441
+ *
442
+ * @param (string|int) The id of the feed source
443
+ * @return (bool) TRUE if the feed source is currently deleting its items, FALSE otherwise.
444
+ *
445
+ */
446
+ function wprss_is_feed_source_deleting( $id ) {
447
+ $is_deleting_meta = get_post_meta( $id, 'wprss_feed_is_deleting_items', TRUE );
448
+
449
+ if ( $is_deleting_meta === '' ) {
450
+ return FALSE;
451
+ }
452
+
453
+ $diff = time() - $is_deleting_meta;
454
+
455
+ $items = wprss_get_feed_items_for_source( $id );
456
+ if ( $items->post_count == 0 || $diff > 300 ) {
457
+ delete_post_meta( $id, 'wprss_feed_is_deleting_items' );
458
+ return FALSE;
459
+ }
460
+
461
+ return TRUE;
462
+ }
463
+
464
+
465
+ /**
466
+ * Returns the given parameter as a string. Used in wprss_truncate_posts()
467
+ *
468
+ * @return string The given parameter as a string
469
+ * @since 3.5.1
470
+ */
471
+ function wprss_return_as_string( $item ) {
472
+ return "'$item'";
473
+ }
474
+
475
+
476
+ /**
477
+ * Returns true if the feed item is older than the given timestamp,
478
+ * false otherwise;
479
+ *
480
+ * @since 3.8
481
+ */
482
+ function wprss_is_feed_item_older_than( $id, $timestamp ) {
483
+ // GET THE DATE
484
+ $age = get_the_time( 'U', $id );
485
+ if ( $age === '' ) return FALSE;
486
+ // Calculate the age difference
487
+ $difference = $age - $timestamp;
488
+ // Return whether the difference is negative ( the age is smaller than the timestamp )
489
+ return ( $difference <= 0 );
490
+ }
491
+
492
+
493
+ /**
494
+ * Returns the maximum age setting for a feed source.
495
+ *
496
+ * @since 3.8
497
+ */
498
+ function wprss_get_max_age_for_feed_source( $source_id ) {
499
+ $general_settings = get_option( 'wprss_settings_general' );
500
+ // Get the meta data for age for this feed source
501
+ $age_limit = trim( get_post_meta( $source_id, 'wprss_age_limit', TRUE ) );
502
+ $age_unit = get_post_meta( $source_id, 'wprss_age_unit', TRUE );
503
+
504
+ // If the meta does not exist, use the global settings
505
+ if( $age_limit === '' ) {
506
+ $age_limit = trim( wprss_get_general_setting( 'limit_feed_items_age' ) );
507
+ $age_unit = wprss_get_general_setting( 'limit_feed_items_age_unit' );
508
+ }
509
+
510
+ // If the age limit is an empty string, use no limit
511
+ if ( $age_limit === '' ) {
512
+ return FALSE;
513
+ }
514
+
515
+ // Return the timestamp of the max age date
516
+ return strtotime( "-$age_limit $age_unit" );
517
+ }
518
+
519
+
520
+ /**
521
+ * Delete old feed items from the database to avoid bloat.
522
+ * As of 3.8, it uses the new feed age system.
523
+ *
524
+ * @since 3.8
525
+ */
526
+ function wprss_truncate_posts() {
527
+ // Get general settings
528
+ $general_settings = get_option( 'wprss_settings_general' );
529
+ // Get all feed sources
530
+ $feed_sources = wprss_get_all_feed_sources();
531
+
532
+ // Check if there are feed sources
533
+ if( $feed_sources->have_posts() ) {
534
+ // FOR EACH FEED SOURCE
535
+ while ( $feed_sources->have_posts() ) {
536
+ $feed_sources->the_post();
537
+ // Get the max age setting for this feed source
538
+ $max_age = wprss_get_max_age_for_feed_source( get_the_ID() );
539
+
540
+ // If the data is empty, do not delete
541
+ if ( $max_age === FALSE ) continue;
542
+
543
+ // Get all feed items for this source
544
+ $feed_items = wprss_get_feed_items_for_source( get_the_ID() );
545
+ // If there are feed items
546
+ if ( $feed_items-> have_posts() ) {
547
+ // Extend the timeout time limit for the deletion of the feed items
548
+ $time_limit = wprss_get_item_import_time_limit();
549
+ wprss_log( "Extended execution time limit by {$time_limit}s for imported items truncation.", null, WPRSS_LOG_LEVEL_INFO );
550
+ set_time_limit( $time_limit );
551
+ // For each feed item
552
+ while ( $feed_items->have_posts() ) {
553
+ $feed_items->the_post();
554
+ // If the post is older than the maximum age
555
+ if ( wprss_is_feed_item_older_than( get_the_ID(), $max_age ) === TRUE ){
556
+ // Delete the post
557
+ wp_delete_post( get_the_ID(), true );
558
+ }
559
+ }
560
+ // Reset feed items query data
561
+ wp_reset_postdata();
562
+ }
563
+ }
564
+ // Reset feed sources query data
565
+ wp_reset_postdata();
566
+ }
567
+
568
+ // If the filter to use the fixed limit is enabled, call the old truncation function
569
+ if ( apply_filters( 'wprss_use_fixed_feed_limit', FALSE ) === TRUE && isset( $general_settings['limit_feed_items_db'] ) ) {
570
+ wprss_old_truncate_posts();
571
+ }
572
+ }
573
+
574
+
575
+ /**
576
+ * The old truncation function.
577
+ * This truncation method uses the deprecated fixed feed limit.
578
+ *
579
+ * @since 2.0
580
+ */
581
+ function wprss_old_truncate_posts() {
582
+ global $wpdb;
583
+ $general_settings = get_option( 'wprss_settings_general' );
584
+
585
+ if ( $general_settings['limit_feed_items_db'] == 0 ) {
586
+ return;
587
+ }
588
+
589
+ // Set your threshold of max posts and post_type name
590
+ $threshold = $general_settings['limit_feed_items_db'];
591
+ $post_types = apply_filters( 'wprss_truncation_post_types', array( 'wprss_feed_item' ) );
592
+ $post_types_str = array_map( 'wprss_return_as_string', $post_types );
593
+
594
+ $post_type_list = implode( ',' , $post_types_str );
595
+
596
+ // Query post type
597
+ // $wpdb query allows me to select specific columns instead of grabbing the entire post object.
598
+ $query = "
599
+ SELECT ID, post_title FROM $wpdb->posts
600
+ WHERE post_type IN ($post_type_list)
601
+ AND post_status = 'publish'
602
+ ORDER BY post_modified DESC
603
+ ";
604
+
605
+ $results = $wpdb->get_results( $query );
606
+
607
+ // Check if there are any results
608
+ $i = 0;
609
+ if ( count( $results ) ){
610
+ foreach ( $results as $post ) {
611
+ $i++;
612
+
613
+ // Skip any posts within our threshold
614
+ if ( $i <= $threshold )
615
+ continue;
616
+
617
+ // Let the WordPress API do the heavy lifting for cleaning up entire post trails
618
+ $purge = wp_delete_post( $post->ID, true );
619
+ }
620
+ }
621
+ }
622
+
623
+
624
+ add_filter( 'wprss_insert_post_item_conditionals', 'wprss_check_feed_item_date_on_import', 2, 3 );
625
+ /**
626
+ * When a feed item is imported, it's date is compared against the max age of it's feed source.
627
+ *
628
+ *
629
+ * @since 3.8
630
+ */
631
+ function wprss_check_feed_item_date_on_import( $item, $source, $permalink ){
632
+ if ( $item === NULL ) return NULL;
633
+
634
+ // Get the age of the item and the max age setting for its feed source
635
+ $age = $item->get_date( 'U' );
636
+ $max_age = wprss_get_max_age_for_feed_source( $source );
637
+
638
+ // If the age is not a valid timestamp, and the max age setting is disabled, return the item
639
+ if ( $age === '' || $age === NULL || $max_age === FALSE || $max_age === NULL ) {
640
+ return $item;
641
+ }
642
+
643
+ // Calculate the age difference
644
+ $difference = $age - $max_age;
645
+
646
+ if ( $difference <= 0 ) {
647
+ return NULL;
648
+ } else {
649
+ return $item;
650
+ }
651
+ }
652
+
653
+
654
+ /**
655
+ * Deletes all imported feeds and re-imports everything
656
+ *
657
+ * @since 3.0
658
+ */
659
+ function wprss_feed_reset() {
660
+ wp_schedule_single_event( time(), 'wprss_delete_all_feed_items_hook' );
661
+ set_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING, true );
662
+ }
663
+
664
+
665
+
666
+ function wprss_schedule_reimport_all($deleted_ids) {
667
+ if( !get_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING ) )
668
+ return;
669
+
670
+ wprss_log( 'Re-import scheduled...', __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM);
671
+ delete_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING );
672
+ wprss_fetch_insert_all_feed_items( TRUE );
673
+ }
674
+ add_action('wprss_delete_all_feed_items_after', 'wprss_schedule_reimport_all');
675
+
676
+
677
+ /**
678
+ * Deletes N oldest feed items for the given source
679
+ *
680
+ * @since 4.2
681
+ * @deprecated
682
+ */
683
+ function wprss_delete_oldest_feed_items( $n, $source ) {
684
+ // If the source does not exist, do nothing
685
+ if ( get_post( $source ) == NULL ) return;
686
+
687
+ // Make sure $n is an integer
688
+ $n = intval($n);
689
+
690
+ // Do nothing if n is zero or negative
691
+ if ( $n <= 0 ) return;
692
+
693
+ // Get the feed items, as an array, not WP_Query.
694
+ // We will need to perform some array operations
695
+ $feed_items = wprss_get_feed_items_for_source( $source );
696
+ $feed_items = $feed_items->get_posts();
697
+ // Get number of feed items
698
+ $count = count( $feed_items );
699
+
700
+ // Index of first feed item to delete
701
+ $start = $count - $n;
702
+ // Cut the array of feed items to get the items to delete
703
+ $to_delete = array_slice( $feed_items, $start );
704
+ // log -- for now
705
+ foreach( $to_delete as $fi ) {
706
+ //wprss_log_obj( "To delete" , $fi->ID );
707
+ }
708
+ }
709
+
710
+
711
+ /**
712
+ * Deletes the required number of feed items for the given source,
713
+ * to keep the number of feed items below its limit.
714
+ *
715
+ * @since 4.2
716
+ * @deprecated
717
+ */
718
+ function wprss_truncate_feed_items_for_source( $source ) {
719
+ // Get the limit setting
720
+ $limit = get_post_meta( $source, 'wprss_limit', true );
721
+
722
+ // Calculate the number of feed items to delete
723
+ $feed_items = wprss_get_feed_items_for_source( $source );
724
+ $n = intval($feed_items->found_posts) - intval($limit);
725
+
726
+ // Delete the feed items
727
+ wprss_delete_oldest_feed_items( $n, $source );
728
+ }
includes/feed-states.php CHANGED
@@ -1,178 +1,178 @@
1
- <?php
2
- /**
3
- * Functions relating to feed source states
4
- *
5
- * @package WPRSSAggregator
6
- */
7
-
8
-
9
- add_action( 'admin_init', 'wprss_change_feed_state' );
10
- /**
11
- * Changes the state of a feed source, using POST data
12
- *
13
- * @since 3.7
14
- */
15
- function wprss_change_feed_state() {
16
- // If the id and state are in POST data
17
- if ( isset( $_GET['wprss-feed-id'] ) ) {
18
- // Get the id and state
19
- $feed_ID = $_GET['wprss-feed-id'];
20
- // Change the state
21
- if ( wprss_is_feed_source_active( $feed_ID ) ) {
22
- wprss_pause_feed_source( $feed_ID );
23
- } else {
24
- wprss_activate_feed_source( $feed_ID );
25
- }
26
- // Check for a redirect
27
- if ( isset( $_GET['wprss-redirect'] ) && $_GET['wprss-redirect'] == '1' ) {
28
- wp_redirect( admin_url( 'edit.php?post_type=wprss_feed', 301 ) );
29
- exit();
30
- }
31
- }
32
- }
33
-
34
-
35
-
36
-
37
- add_action( 'admin_init', 'wprss_bulk_change_state', 2 );
38
- /**
39
- * Changes the state of feed sources selected from the table bulk actions.
40
- *
41
- * @since 4.1
42
- */
43
- function wprss_bulk_change_state() {
44
- // If the id and state are in POST data
45
- if ( isset( $_GET['post_type'] ) && (isset( $_GET['action'] ) || isset( $_GET['action2'] )) && isset( $_GET['post'] ) ) {
46
- // Get the action and post ids from GET request
47
- $action = isset($_GET['action']) && $_GET['action'] !== '-1' ? $_GET['action'] : $_GET['action2'];
48
- $post_ids = $_GET['post'];
49
-
50
- // check the action
51
- switch ( $action ) {
52
- // Activate all feed sources in $post_ids
53
- case 'activate':
54
- foreach( $post_ids as $post_id ) {
55
- wprss_activate_feed_source( $post_id );
56
- }
57
- // Set a transient to show the admin notice, after redirection
58
- set_transient( 'wprss_notify_bulk_change_state', 'activated', 0 );
59
- break;
60
-
61
- // Pause all feed sources in $post_ids
62
- case 'pause':
63
- foreach( $post_ids as $post_id ) {
64
- wprss_pause_feed_source( $post_id );
65
- }
66
- // Set a transient to show the admin notice, after redirection
67
- set_transient( 'wprss_notify_bulk_change_state', 'paused', 0 );
68
- break;
69
- }
70
-
71
- /* Note:
72
- * Transients are used since bulk actions will, after processing, case a redirect to the same page.
73
- * Thus, using add_action( 'all_admin_notices', ... ) will result in the notice appearing on the first request,
74
- * and not be shown after redirection.
75
- * The transient is set to show the notification AFTER redirection.
76
- */
77
- }
78
- }
79
-
80
-
81
-
82
- add_action( 'admin_init', 'check_for_state_notice_after_redirect', 1 );
83
- /**
84
- * Checks if the 'wprss_notify_bulk_change_state' transient is set.
85
- * If it is, it will show the appropriate admin notice
86
- *
87
- * @since 4.1
88
- */
89
- function check_for_state_notice_after_redirect() {
90
- $transient = get_transient( 'wprss_notify_bulk_change_state' );
91
- if ( $transient !== FALSE ) {
92
- switch ( $transient ) {
93
- case 'activated': add_action( 'all_admin_notices', 'wprss_notify_feed_sources_activated' ); break;
94
- case 'paused': add_action( 'all_admin_notices', 'wprss_notify_feed_sources_paused' ); break;
95
- }
96
- delete_transient( 'wprss_notify_bulk_change_state' );
97
- }
98
- }
99
-
100
-
101
-
102
- /**
103
- * Shows an admin notice to notify that feed sources have been activated.
104
- *
105
- * @since 4.1
106
- */
107
- function wprss_notify_feed_sources_activated() {
108
- ?>
109
- <div class="updated">
110
- <?php echo wpautop( __( 'The feed sources have been activated!', WPRSS_TEXT_DOMAIN ) ) ?>
111
- </div>
112
- <?php
113
- }
114
-
115
-
116
- /**
117
- * Shows an admin notice to notify that feed sources have been activated.
118
- *
119
- * @since 4.1
120
- */
121
- function wprss_notify_feed_sources_paused() {
122
- ?>
123
- <div class="updated">
124
- <?php echo wpautop( __( 'The feed sources have been paused!!', WPRSS_TEXT_DOMAIN ) ) ?>
125
- </div>
126
- <?php
127
- }
128
-
129
-
130
-
131
-
132
-
133
-
134
- /**
135
- * Activates the feed source. Runs on a schedule.
136
- *
137
- * @param $feed_id The of of the wprss_feed
138
- * @since 3.7
139
- */
140
- function wprss_activate_feed_source( $feed_id ) {
141
- update_post_meta( $feed_id, 'wprss_state', 'active' );
142
- update_post_meta( $feed_id, 'wprss_activate_feed', '' );
143
-
144
- // Add an action hook, so functions can be run when a feed source is activated
145
- do_action( 'wprss_on_feed_source_activated', $feed_id );
146
- }
147
-
148
-
149
- /**
150
- * Pauses the feed source. Runs on a schedule.
151
- *
152
- * @param $feed_id The of of the wprss_feed
153
- * @since 3.7
154
- */
155
- function wprss_pause_feed_source( $feed_id ) {
156
- update_post_meta( $feed_id, 'wprss_state', 'paused' );
157
- update_post_meta( $feed_id, 'wprss_pause_feed', '' );
158
-
159
- // Add an action hook, so functions can be run when a feed source is paused
160
- do_action( 'wprss_on_feed_source_paused', $feed_id );
161
- }
162
-
163
-
164
-
165
-
166
-
167
-
168
- /**
169
- * Returns whether or not a feed source is active.
170
- *
171
- * @param $source_id The ID of the feed soruce
172
- * @return boolean
173
- * @since 3.7
174
- */
175
- function wprss_is_feed_source_active( $source_id ) {
176
- $state = get_post_meta( $source_id, 'wprss_state', TRUE );
177
- return ( $state === '' || $state === 'active' );
178
  }
1
+ <?php
2
+ /**
3
+ * Functions relating to feed source states
4
+ *
5
+ * @package WPRSSAggregator
6
+ */
7
+
8
+
9
+ add_action( 'admin_init', 'wprss_change_feed_state' );
10
+ /**
11
+ * Changes the state of a feed source, using POST data
12
+ *
13
+ * @since 3.7
14
+ */
15
+ function wprss_change_feed_state() {
16
+ // If the id and state are in POST data
17
+ if ( isset( $_GET['wprss-feed-id'] ) ) {
18
+ // Get the id and state
19
+ $feed_ID = $_GET['wprss-feed-id'];
20
+ // Change the state
21
+ if ( wprss_is_feed_source_active( $feed_ID ) ) {
22
+ wprss_pause_feed_source( $feed_ID );
23
+ } else {
24
+ wprss_activate_feed_source( $feed_ID );
25
+ }
26
+ // Check for a redirect
27
+ if ( isset( $_GET['wprss-redirect'] ) && $_GET['wprss-redirect'] == '1' ) {
28
+ wp_redirect( admin_url( 'edit.php?post_type=wprss_feed', 301 ) );
29
+ exit();
30
+ }
31
+ }
32
+ }
33
+
34
+
35
+
36
+
37
+ add_action( 'admin_init', 'wprss_bulk_change_state', 2 );
38
+ /**
39
+ * Changes the state of feed sources selected from the table bulk actions.
40
+ *
41
+ * @since 4.1
42
+ */
43
+ function wprss_bulk_change_state() {
44
+ // If the id and state are in POST data
45
+ if ( isset( $_GET['post_type'] ) && (isset( $_GET['action'] ) || isset( $_GET['action2'] )) && isset( $_GET['post'] ) ) {
46
+ // Get the action and post ids from GET request
47
+ $action = isset($_GET['action']) && $_GET['action'] !== '-1' ? $_GET['action'] : $_GET['action2'];
48
+ $post_ids = $_GET['post'];
49
+
50
+ // check the action
51
+ switch ( $action ) {
52
+ // Activate all feed sources in $post_ids
53
+ case 'activate':
54
+ foreach( $post_ids as $post_id ) {
55
+ wprss_activate_feed_source( $post_id );
56
+ }
57
+ // Set a transient to show the admin notice, after redirection
58
+ set_transient( 'wprss_notify_bulk_change_state', 'activated', 0 );
59
+ break;
60
+
61
+ // Pause all feed sources in $post_ids
62
+ case 'pause':
63
+ foreach( $post_ids as $post_id ) {
64
+ wprss_pause_feed_source( $post_id );
65
+ }
66
+ // Set a transient to show the admin notice, after redirection
67
+ set_transient( 'wprss_notify_bulk_change_state', 'paused', 0 );
68
+ break;
69
+ }
70
+
71
+ /* Note:
72
+ * Transients are used since bulk actions will, after processing, case a redirect to the same page.
73
+ * Thus, using add_action( 'all_admin_notices', ... ) will result in the notice appearing on the first request,
74
+ * and not be shown after redirection.
75
+ * The transient is set to show the notification AFTER redirection.
76
+ */
77
+ }
78
+ }
79
+
80
+
81
+
82
+ add_action( 'admin_init', 'check_for_state_notice_after_redirect', 1 );
83
+ /**
84
+ * Checks if the 'wprss_notify_bulk_change_state' transient is set.
85
+ * If it is, it will show the appropriate admin notice
86
+ *
87
+ * @since 4.1
88
+ */
89
+ function check_for_state_notice_after_redirect() {
90
+ $transient = get_transient( 'wprss_notify_bulk_change_state' );
91
+ if ( $transient !== FALSE ) {
92
+ switch ( $transient ) {
93
+ case 'activated': add_action( 'all_admin_notices', 'wprss_notify_feed_sources_activated' ); break;
94
+ case 'paused': add_action( 'all_admin_notices', 'wprss_notify_feed_sources_paused' ); break;
95
+ }
96
+ delete_transient( 'wprss_notify_bulk_change_state' );
97
+ }
98
+ }
99
+
100
+
101
+
102
+ /**
103
+ * Shows an admin notice to notify that feed sources have been activated.
104
+ *
105
+ * @since 4.1
106
+ */
107
+ function wprss_notify_feed_sources_activated() {
108
+ ?>
109
+ <div class="updated">
110
+ <?php echo wpautop( __( 'The feed sources have been activated!', WPRSS_TEXT_DOMAIN ) ) ?>
111
+ </div>
112
+ <?php
113
+ }
114
+
115
+
116
+ /**
117
+ * Shows an admin notice to notify that feed sources have been activated.
118
+ *
119
+ * @since 4.1
120
+ */
121
+ function wprss_notify_feed_sources_paused() {
122
+ ?>
123
+ <div class="updated">
124
+ <?php echo wpautop( __( 'The feed sources have been paused!!', WPRSS_TEXT_DOMAIN ) ) ?>
125
+ </div>
126
+ <?php
127
+ }
128
+
129
+
130
+
131
+
132
+
133
+
134
+ /**
135
+ * Activates the feed source. Runs on a schedule.
136
+ *
137
+ * @param $feed_id The of of the wprss_feed
138
+ * @since 3.7
139
+ */
140
+ function wprss_activate_feed_source( $feed_id ) {
141
+ update_post_meta( $feed_id, 'wprss_state', 'active' );
142
+ update_post_meta( $feed_id, 'wprss_activate_feed', '' );
143
+
144
+ // Add an action hook, so functions can be run when a feed source is activated
145
+ do_action( 'wprss_on_feed_source_activated', $feed_id );
146
+ }
147
+
148
+
149
+ /**
150
+ * Pauses the feed source. Runs on a schedule.
151
+ *
152
+ * @param $feed_id The of of the wprss_feed
153
+ * @since 3.7
154
+ */
155
+ function wprss_pause_feed_source( $feed_id ) {
156
+ update_post_meta( $feed_id, 'wprss_state', 'paused' );
157
+ update_post_meta( $feed_id, 'wprss_pause_feed', '' );
158
+
159
+ // Add an action hook, so functions can be run when a feed source is paused
160
+ do_action( 'wprss_on_feed_source_paused', $feed_id );
161
+ }
162
+
163
+
164
+
165
+
166
+
167
+
168
+ /**
169
+ * Returns whether or not a feed source is active.
170
+ *
171
+ * @param $source_id The ID of the feed soruce
172
+ * @return boolean
173
+ * @since 3.7
174
+ */
175
+ function wprss_is_feed_source_active( $source_id ) {
176
+ $state = get_post_meta( $source_id, 'wprss_state', TRUE );
177
+ return ( $state === '' || $state === 'active' );
178
  }
includes/image-caching.php CHANGED
@@ -1,290 +1,461 @@
1
- <?php
2
-
3
- class WPRSS_Image_Cache {
4
-
5
- protected $_download_request_timeout = 300;
6
-
7
- protected $_image_class_name = 'WPRSS_Image_Cache_Image';
8
- protected $_images = array();
9
-
10
- public function __construct() {
11
- }
12
-
13
- public function set_image_class_name( $class_name ) {
14
- $this->_image_class_name = $class_name;
15
- return $this;
16
- }
17
-
18
- public function get_image_class_name() {
19
- return trim($this->_image_class_name);
20
- }
21
-
22
- public function set_download_request_timeout( $timeout ) {
23
- $this->_download_request_timeout = intval( $timeout );
24
- return $this;
25
- }
26
-
27
- public function get_download_request_timeout() {
28
- return $this->_download_request_timeout;
29
- }
30
-
31
- /**
32
- * @param string $url
33
- * @return \WPRSS_Image_Cache_Image
34
- * @throws Exception If class invalid, or not found
35
- */
36
- public function get_new_image( $url = null ) {
37
- $error_caption = 'Could not create new cache image';
38
- $class_name = $this->get_image_class_name();
39
- if ( empty( $class_name ) ) throw new Exception( sprintf( '%1$s: class name must not be empty' ) );
40
- if ( !class_exists( $class_name ) ) throw new Exception( sprintf( '%1$s: class "%2$s" does not exist', $class_name ) );
41
-
42
- $image = new $class_name();
43
- $this->_prepare_image( $image );
44
- /* @var $image WPRSS_Image_Cache_Image */
45
- if ( !is_null( $url ) ) $image->set_url( $url );
46
-
47
- return $image;
48
- }
49
-
50
- /**
51
- *
52
- * @param WPRSS_Image_Cache_Image $image
53
- */
54
- protected function _prepare_image( $image ) {
55
- $image->set_download_request_timeout( $this->get_download_request_timeout() );
56
- }
57
-
58
- public function get_images( $url = null ) {
59
- if ( is_null( $url ) ) return $this->_images;
60
-
61
- // Gotta cache one
62
- if ( !isset( $this->_images[ $url ] ) ) {
63
- try {
64
- $image = $this->_download_image($url);
65
- } catch ( Exception $e ) {
66
- return new WP_Error( 'image_cache_cannot_download', $e->getMessage() );
67
- }
68
-
69
- $this->_images[ $url ] = $image;
70
- }
71
-
72
- return $this->_images[ $url ];
73
- }
74
-
75
- public function purge() {
76
- $image_class_name = $this->get_image_class_name();
77
- foreach( $this->get_images() as $_url => $_image ) {
78
- /* @var $_image WPRSS_Image_Cache_Image */
79
- if ( is_a( $_image, $image_class_name ) )
80
- $_image->delete();
81
- }
82
-
83
- $this->_images = array();
84
- return $this;
85
- }
86
-
87
- /**
88
- * @param string $url
89
- * @return WPRSS_Image_Cache_Image
90
- */
91
- protected function _download_image( $url ) {
92
- $image = $this->get_new_image( $url );
93
- $image->download();
94
-
95
- return $image;
96
- }
97
- }
98
-
99
- class WPRSS_Image_Cache_Image {
100
-
101
- protected $_url;
102
- protected $_local_path;
103
- protected $_unique_name;
104
- protected $_size;
105
- protected $_download_request_timeout;
106
- protected $_is_attempted;
107
- protected $_is_fall_back_to_unsecure;
108
-
109
- public function __construct( $data = null ) {
110
- $this->reset();
111
-
112
- if ( is_string( $data ) && !empty( $data ) )
113
- $this->_set_url( $data );
114
- }
115
-
116
- public function reset() {
117
- $this->_url = null;
118
- $this->_local_path = null;
119
- $this->_unique_name = null;
120
- $this->_size = null;
121
- $this->_download_request_timeout = 300;
122
- $this->_is_attempted = false;
123
- $this->_is_fall_back_to_unsecure = true;
124
-
125
- return $this;
126
- }
127
-
128
- protected function _set_url( $url ) {
129
- $this->_url = $url;
130
- return $this;
131
- }
132
-
133
- public function set_url( $url ) {
134
- $this->reset();
135
- $this->_set_url($url);
136
-
137
- return $this;
138
- }
139
-
140
- public function get_url() {
141
- return $this->_url;
142
- }
143
-
144
- public function has_url() {
145
- return isset( $this->_url );
146
- }
147
-
148
- protected function _set_local_path( $path ) {
149
- $this->_local_path = $path;
150
- return $this;
151
- }
152
-
153
- public function get_local_path() {
154
- return $this->_local_path;
155
- }
156
-
157
- public function has_local_path() {
158
- return isset( $this->_local_path );
159
- }
160
-
161
- public function set_download_request_timeout( $timeout ) {
162
- $this->_download_request_timeout = intval( $timeout );
163
- return $this;
164
- }
165
-
166
- public function get_download_request_timeout() {
167
- return $this->_download_request_timeout;
168
- }
169
-
170
- protected function _is_attempted( $is_attempted = null ) {
171
- if ( is_null( $is_attempted ) )
172
- return (bool)$this->_is_attempted;
173
-
174
- $this->_is_attempted = (bool) $is_attempted;
175
- return $this;
176
- }
177
-
178
- public function is_attempted() {
179
- return $this->_is_attempted();
180
- }
181
-
182
- public function is_fall_back_to_unsecure( $is_fall_back = null ) {
183
- if ( is_null( $is_fall_back ) )
184
- return (bool) $this->_is_fall_back_to_unsecure;
185
-
186
- $this->_is_fall_back_to_unsecure = (bool) $is_fall_back;
187
- return $this;
188
- }
189
-
190
- public function download() {
191
- $error_caption = 'Could not download image';
192
- if ( !$this->has_url() ) throw new Exception ( sprintf( '%1$s: a URL must be supplied' ) );
193
-
194
- // Getting file download lib
195
- $file_lib_path = ABSPATH . 'wp-admin/includes/file.php';
196
- if ( !is_readable( $file_lib_path ) ) throw new Exception( sprintf( '%1$s: the file library cannot be read from %2$s', $error_caption, $file_lib_path ) );
197
- require_once( $file_lib_path );
198
-
199
- // Downloading the image
200
- $url = $this->get_url();
201
- $timeout = $this->get_download_request_timeout();
202
- $tmp_path = $this->_download( $url, $timeout );
203
- if ( is_wp_error( $tmp_path ) ) throw new Exception ( sprintf( '%1$s: %2$s', $error_caption, $tmp_path->get_error_message() ) );
204
- // wprss_log( sprintf( 'Image saved to "%1$s"', $tmp_path ), null, WPRSS_LOG_LEVEL_SYSTEM );
205
- $this->_set_local_path( $tmp_path );
206
-
207
- return $this->get_local_path();
208
- }
209
-
210
- protected function _download( $url, $timeout ) {
211
- // wprss_log( sprintf( 'Downloading from "%1$s"', $url ), null, WPRSS_LOG_LEVEL_SYSTEM );
212
- $tmp_path = download_url( $url, $timeout );
213
- if ( is_wp_error( $tmp_path ) ) {
214
- $https = 'https';
215
- if ( $this->is_fall_back_to_unsecure() && (stripos( $url, $https ) === 0) ) {
216
- $url = 'http' . substr( $url, strlen( $https ) );
217
- // wprss_log( sprintf( 'Downloading from "%1$s"', $url ), null, WPRSS_LOG_LEVEL_SYSTEM );
218
- $tmp_path = $this->_download( $url, $timeout );
219
- }
220
- }
221
-
222
- return $tmp_path;
223
- }
224
-
225
- public function delete() {
226
- if ( $path = $this->get_local_path() )
227
- unlink( $path );
228
-
229
- return $this;
230
- }
231
-
232
- public function get_unique_name() {
233
- if( !isset($this->_unique_name) ) {
234
- $url = $this->get_url();
235
- // Extract filename from url for title (ignoring query string)
236
- // One of more character that is not a '?', followed by an image extension
237
- preg_match( '/[^\?]+\.(jpg|JPG|jpe|JPE|jpeg|JPEG|gif|GIF|png|PNG)/', $url, $matches );
238
- $url_filename = basename( urldecode( $matches[0] ) );
239
- // Check for extension. If not found, use last component of the URL
240
- if ( !isset( $matches[1] ) ) {
241
- $matches = array();
242
- // Get the path to the image, without the domain. ex. /news/132456/image
243
- preg_match_all( '/[^:]+:\/\/[^\/]+\/(.+)/', $url, $matches );
244
- // If found
245
- if ( isset( $matches[1][0] ) ) {
246
- // Replace all '/' into '.' for the filename
247
- $url_filename = str_replace( '/', '-', $matches[1][0] );
248
- }
249
- // If not found
250
- else {
251
- // Use a random string as a fallback, with length of 16 characters
252
- $url_filename = wprss_ftp_generate_random_string( 16 );
253
- }
254
- }
255
- // determine file type (ext and mime/type)
256
- $url_type = wp_check_filetype($url_filename);
257
- }
258
-
259
- return $this->_unique_name;
260
- }
261
-
262
- public function get_size() {
263
- if ( !isset( $this->_size ) ) {
264
- $error_caption = 'Could not get image size';
265
- if ( !$this->is_readable() ) throw new Exception( sprintf( '%1$s: image file is not readable' ) );
266
- $path = $this->get_local_path();
267
-
268
- // Trying simplest way
269
- if ( $size = getimagesize( $path ) )
270
- $this->_size = array( 0 => $size[0], 1 => $size[1] );
271
-
272
- wprss_log( sprintf( 'Tried `getimagesize()`: %1$s', empty($this->_size) ? 'failure' : 'success' ), __METHOD__, WPRSS_LOG_LEVEL_SYSTEM );
273
-
274
- if( !$this->_size && function_exists( 'gd_info' ) ) {
275
- $image = file_get_contents( $path );
276
- $image = imagecreatefromstring( $image );
277
- $width = imagesx( $image );
278
- $height = imagesy( $image );
279
- $this->_size = array( 0 => $width, 1 => $height );
280
- wprss_log( sprintf( 'Tried GD: %1$s', empty($this->_size) ? 'failure' : 'success' ), __METHOD__, WPRSS_LOG_LEVEL_SYSTEM );
281
- }
282
- }
283
-
284
- return $this->_size;
285
- }
286
-
287
- public function is_readable() {
288
- return is_readable( $this->get_local_path() );
289
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  }
1
+ <?php
2
+
3
+ /**
4
+ * @since 4.6.10
5
+ */
6
+ class WPRSS_Image_Cache {
7
+
8
+ protected $_download_request_timeout = 300;
9
+
10
+ protected $_image_class_name = 'WPRSS_Image_Cache_Image';
11
+ protected $_images = array();
12
+
13
+ /**
14
+ * @since 4.6.10
15
+ */
16
+ public function __construct() {
17
+ }
18
+
19
+
20
+ /**
21
+ * @since 4.6.10
22
+ * @param string $class_name
23
+ * @return \WPRSS_Image_Cache This instance.
24
+ */
25
+ public function set_image_class_name( $class_name ) {
26
+ $this->_image_class_name = $class_name;
27
+ return $this;
28
+ }
29
+
30
+
31
+ /**
32
+ * @since 4.6.10
33
+ * @return string
34
+ */
35
+ public function get_image_class_name() {
36
+ return trim($this->_image_class_name);
37
+ }
38
+
39
+
40
+ /**
41
+ * @since 4.6.10
42
+ * @param int $timeout
43
+ * @return \WPRSS_Image_Cache This instance.
44
+ */
45
+ public function set_download_request_timeout( $timeout ) {
46
+ $this->_download_request_timeout = intval( $timeout );
47
+ return $this;
48
+ }
49
+
50
+
51
+ /**
52
+ * @since 4.6.10
53
+ * @return int
54
+ */
55
+ public function get_download_request_timeout() {
56
+ return $this->_download_request_timeout;
57
+ }
58
+
59
+
60
+ /**
61
+ * @since 4.6.10
62
+ * @param string $url
63
+ * @return \WPRSS_Image_Cache_Image
64
+ * @throws Exception If class invalid, or not found
65
+ */
66
+ public function get_new_image( $url = null ) {
67
+ $error_caption = 'Could not create new cache image';
68
+ $class_name = $this->get_image_class_name();
69
+ if ( empty( $class_name ) ) throw new Exception( sprintf( '%1$s: class name must not be empty' ) );
70
+ if ( !class_exists( $class_name ) ) throw new Exception( sprintf( '%1$s: class "%2$s" does not exist', $class_name ) );
71
+
72
+ $image = new $class_name();
73
+ $this->_prepare_image( $image );
74
+ /* @var $image WPRSS_Image_Cache_Image */
75
+ if ( !is_null( $url ) ) $image->set_url( $url );
76
+
77
+ return $image;
78
+ }
79
+
80
+
81
+ /**
82
+ * @since 4.6.10
83
+ * @param WPRSS_Image_Cache_Image $image
84
+ */
85
+ protected function _prepare_image( $image ) {
86
+ $image->set_download_request_timeout( $this->get_download_request_timeout() );
87
+ }
88
+
89
+
90
+ /**
91
+ * @since 4.6.10
92
+ * @param string|null $url
93
+ * @return array|WPRSS_Image_Cache_Image|\WP_Error
94
+ */
95
+ public function get_images( $url = null ) {
96
+ if ( is_null( $url ) ) return $this->_images;
97
+
98
+ // Gotta cache one
99
+ if ( !isset( $this->_images[ $url ] ) ) {
100
+ try {
101
+ $image = $this->_download_image($url);
102
+ } catch ( Exception $e ) {
103
+ return new WP_Error( 'image_cache_cannot_download', $e->getMessage() );
104
+ }
105
+
106
+ $this->_images[ $url ] = $image;
107
+ }
108
+
109
+ return $this->_images[ $url ];
110
+ }
111
+
112
+
113
+ /**
114
+ * @since 4.6.10
115
+ * @return \WPRSS_Image_Cache This instance
116
+ */
117
+ public function purge() {
118
+ $image_class_name = $this->get_image_class_name();
119
+ foreach( $this->get_images() as $_url => $_image ) {
120
+ /* @var $_image WPRSS_Image_Cache_Image */
121
+ if ( is_a( $_image, $image_class_name ) )
122
+ $_image->delete();
123
+ }
124
+
125
+ $this->_images = array();
126
+ return $this;
127
+ }
128
+
129
+
130
+ /**
131
+ * @since 4.6.10
132
+ * @param string $url
133
+ * @return WPRSS_Image_Cache_Image
134
+ */
135
+ protected function _download_image( $url ) {
136
+ $image = $this->get_new_image( $url );
137
+ $image->download();
138
+
139
+ return $image;
140
+ }
141
+ }
142
+
143
+
144
+ /**
145
+ * @since 4.6.10
146
+ */
147
+ class WPRSS_Image_Cache_Image {
148
+
149
+ protected $_url;
150
+ protected $_local_path;
151
+ protected $_unique_name;
152
+ protected $_size;
153
+ protected $_download_request_timeout;
154
+ protected $_is_attempted;
155
+ protected $_is_fall_back_to_unsecure;
156
+
157
+
158
+ /**
159
+ * @since 4.6.10
160
+ * @param string|null $data
161
+ */
162
+ public function __construct( $data = null ) {
163
+ $this->reset();
164
+
165
+ if ( is_string( $data ) && !empty( $data ) )
166
+ $this->_set_url( $data );
167
+ }
168
+
169
+
170
+ /**
171
+ * @since 4.6.10
172
+ * @return \WPRSS_Image_Cache_Image This instance.
173
+ */
174
+ public function reset() {
175
+ $this->_url = null;
176
+ $this->_local_path = null;
177
+ $this->_unique_name = null;
178
+ $this->_size = null;
179
+ $this->_download_request_timeout = 300;
180
+ $this->_is_attempted = false;
181
+ $this->_is_fall_back_to_unsecure = true;
182
+
183
+ return $this;
184
+ }
185
+
186
+
187
+ /**
188
+ * @since 4.6.10
189
+ * @param string $url
190
+ * @return \WPRSS_Image_Cache_Image This instance.
191
+ */
192
+ protected function _set_url( $url ) {
193
+ $this->_url = $url;
194
+ return $this;
195
+ }
196
+
197
+
198
+ /**
199
+ * @since 4.6.10
200
+ * @param string $url
201
+ * @return \WPRSS_Image_Cache_Image This instance.
202
+ */
203
+ public function set_url( $url ) {
204
+ $this->reset();
205
+ $this->_set_url($url);
206
+
207
+ return $this;
208
+ }
209
+
210
+
211
+ /**
212
+ * @since 4.6.10
213
+ * @return string
214
+ */
215
+ public function get_url() {
216
+ return $this->_url;
217
+ }
218
+
219
+
220
+ /**
221
+ * @since 4.6.10
222
+ * @return boolean
223
+ */
224
+ public function has_url() {
225
+ return isset( $this->_url );
226
+ }
227
+
228
+
229
+ /**
230
+ * @since 4.6.10
231
+ * @param string $path
232
+ * @return \WPRSS_Image_Cache_Image This instance.
233
+ */
234
+ protected function _set_local_path( $path ) {
235
+ $this->_local_path = $path;
236
+ return $this;
237
+ }
238
+
239
+
240
+ /**
241
+ * @since 4.6.10
242
+ * @return string
243
+ */
244
+ public function get_local_path() {
245
+ return $this->_local_path;
246
+ }
247
+
248
+
249
+ /**
250
+ * @since 4.6.10
251
+ * @return boolean
252
+ */
253
+ public function has_local_path() {
254
+ return isset( $this->_local_path );
255
+ }
256
+
257
+
258
+ /**
259
+ * @since 4.6.10
260
+ * @param int $timeout
261
+ * @return \WPRSS_Image_Cache_Image This instance.
262
+ */
263
+ public function set_download_request_timeout( $timeout ) {
264
+ $this->_download_request_timeout = intval( $timeout );
265
+ return $this;
266
+ }
267
+
268
+
269
+ /**
270
+ * @since 4.6.10
271
+ * @return int
272
+ */
273
+ public function get_download_request_timeout() {
274
+ return $this->_download_request_timeout;
275
+ }
276
+
277
+
278
+ /**
279
+ * @since 4.6.10
280
+ * @param boolean|null $is_attempted
281
+ * @return \WPRSS_Image_Cache_Image|boolean Whether was attempted, or this instance.
282
+ */
283
+ protected function _is_attempted( $is_attempted = null ) {
284
+ if ( is_null( $is_attempted ) )
285
+ return (bool)$this->_is_attempted;
286
+
287
+ $this->_is_attempted = (bool) $is_attempted;
288
+ return $this;
289
+ }
290
+
291
+
292
+ /**
293
+ * @since 4.6.10
294
+ * @return boolean
295
+ */
296
+ public function is_attempted() {
297
+ return $this->_is_attempted();
298
+ }
299
+
300
+
301
+ /**
302
+ * @since 4.6.10
303
+ * @param boolean|null $is_fall_back
304
+ * @return \WPRSS_Image_Cache_Image|boolean Whether will fall back to unsecure, or this instance.
305
+ */
306
+ public function is_fall_back_to_unsecure( $is_fall_back = null ) {
307
+ if ( is_null( $is_fall_back ) )
308
+ return (bool) $this->_is_fall_back_to_unsecure;
309
+
310
+ $this->_is_fall_back_to_unsecure = (bool) $is_fall_back;
311
+ return $this;
312
+ }
313
+
314
+
315
+ /**
316
+ * @since 4.6.10
317
+ * @return string {@see get_local_path()}
318
+ * @throws Exception If no URL is set, or the resource is unreadable, or something went wrong.
319
+ */
320
+ public function download() {
321
+ $error_caption = 'Could not download image';
322
+ if ( !$this->has_url() ) throw new Exception ( sprintf( '%1$s: a URL must be supplied' ) );
323
+
324
+ // Getting file download lib
325
+ $file_lib_path = ABSPATH . 'wp-admin/includes/file.php';
326
+ if ( !is_readable( $file_lib_path ) ) throw new Exception( sprintf( '%1$s: the file library cannot be read from %2$s', $error_caption, $file_lib_path ) );
327
+ require_once( $file_lib_path );
328
+
329
+ // Downloading the image
330
+ $url = $this->get_url();
331
+ $timeout = $this->get_download_request_timeout();
332
+ $tmp_path = $this->_download( $url, $timeout );
333
+ if ( is_wp_error( $tmp_path ) ) throw new Exception ( sprintf( '%1$s: %2$s', $error_caption, $tmp_path->get_error_message() ) );
334
+ // wprss_log( sprintf( 'Image saved to "%1$s"', $tmp_path ), null, WPRSS_LOG_LEVEL_SYSTEM );
335
+ $this->_set_local_path( $tmp_path );
336
+
337
+ return $this->get_local_path();
338
+ }
339
+
340
+
341
+ /**
342
+ * @since 4.6.10
343
+ * @param string $url
344
+ * @param int $timeout
345
+ * @return string|WP_Error The local path to the downloaded image, if successful; an error instance if download failed.
346
+ */
347
+ protected function _download( $url, $timeout ) {
348
+ // wprss_log( sprintf( 'Downloading from "%1$s"', $url ), null, WPRSS_LOG_LEVEL_SYSTEM );
349
+ $tmp_path = download_url( $url, $timeout );
350
+ if ( is_wp_error( $tmp_path ) ) {
351
+ $https = 'https';
352
+ if ( $this->is_fall_back_to_unsecure() && (stripos( $url, $https ) === 0) ) {
353
+ $url = 'http' . substr( $url, strlen( $https ) );
354
+ // wprss_log( sprintf( 'Downloading from "%1$s"', $url ), null, WPRSS_LOG_LEVEL_SYSTEM );
355
+ $tmp_path = $this->_download( $url, $timeout );
356
+ }
357
+ }
358
+
359
+ return $tmp_path;
360
+ }
361
+
362
+
363
+ /**
364
+ * @since 4.6.10
365
+ * @return \WPRSS_Image_Cache_Image This instance.
366
+ */
367
+ public function delete() {
368
+ if ( $path = $this->get_local_path() ) {
369
+ if ( file_exists( $path ) ) {
370
+ unlink( $path );
371
+ }
372
+ }
373
+
374
+ return $this;
375
+ }
376
+
377
+
378
+ /**
379
+ * @since 4.6.10
380
+ * @return string
381
+ */
382
+ public function get_unique_name() {
383
+ if( !isset($this->_unique_name) ) {
384
+ $url = $this->get_url();
385
+ // Extract filename from url for title (ignoring query string)
386
+ // One of more character that is not a '?', followed by an image extension
387
+ preg_match( '/[^\?]+\.(jpg|JPG|jpe|JPE|jpeg|JPEG|gif|GIF|png|PNG)/', $url, $matches );
388
+ $url_filename = basename( urldecode( $matches[0] ) );
389
+ // Check for extension. If not found, use last component of the URL
390
+ if ( !isset( $matches[1] ) ) {
391
+ $matches = array();
392
+ // Get the path to the image, without the domain. ex. /news/132456/image
393
+ preg_match_all( '/[^:]+:\/\/[^\/]+\/(.+)/', $url, $matches );
394
+ // If found
395
+ if ( isset( $matches[1][0] ) ) {
396
+ // Replace all '/' into '.' for the filename
397
+ $url_filename = str_replace( '/', '-', $matches[1][0] );
398
+ }
399
+ // If not found
400
+ else {
401
+ // Use a random string as a fallback, with length of 16 characters
402
+ $url_filename = wprss_ftp_generate_random_string( 16 );
403
+ }
404
+ }
405
+ $this->_set_unique_name( $url_filename );
406
+ }
407
+
408
+ return $this->_unique_name;
409
+ }
410
+
411
+
412
+ /**
413
+ * @since 4.6.10
414
+ * @param string $name
415
+ * @return \WPRSS_Image_Cache_Image This instance.
416
+ */
417
+ protected function _set_unique_name( $name ) {
418
+ $this->_unique_name = $name;
419
+ return $this;
420
+ }
421
+
422
+
423
+ /**
424
+ * @since 4.6.10
425
+ * @return array A numeric array, where index 0 holds image width, and index 1 holds image height.
426
+ * @throws Exception If image file is unreadable.
427
+ */
428
+ public function get_size() {
429
+ if ( !isset( $this->_size ) ) {
430
+ $error_caption = 'Could not get image size';
431
+ if ( !$this->is_readable() ) throw new Exception( sprintf( '%1$s: image file is not readable' ) );
432
+ $path = $this->get_local_path();
433
+
434
+ // Trying simplest way
435
+ if ( $size = getimagesize( $path ) )
436
+ $this->_size = array( 0 => $size[0], 1 => $size[1] );
437
+
438
+ wprss_log( sprintf( 'Tried `getimagesize()`: %1$s', empty($this->_size) ? 'failure' : 'success' ), __METHOD__, WPRSS_LOG_LEVEL_SYSTEM );
439
+
440
+ if( !$this->_size && function_exists( 'gd_info' ) ) {
441
+ $image = file_get_contents( $path );
442
+ $image = imagecreatefromstring( $image );
443
+ $width = imagesx( $image );
444
+ $height = imagesy( $image );
445
+ $this->_size = array( 0 => $width, 1 => $height );
446
+ wprss_log( sprintf( 'Tried GD: %1$s', empty($this->_size) ? 'failure' : 'success' ), __METHOD__, WPRSS_LOG_LEVEL_SYSTEM );
447
+ }
448
+ }
449
+
450
+ return $this->_size;
451
+ }
452
+
453
+
454
+ /**
455
+ * @since 4.6.10
456
+ * @return boolean
457
+ */
458
+ public function is_readable() {
459
+ return is_readable( $this->get_local_path() );
460
+ }
461
  }
includes/libraries/EDD_licensing/EDD_SL_Plugin_Updater.php CHANGED
@@ -1,314 +1,337 @@
1
- <?php
2
-
3
- // uncomment this line for testing
4
- //set_site_transient( 'update_plugins', null );
5
-
6
- /**
7
- * Allows plugins to use their own update API.
8
- *
9
- * @author Pippin Williamson
10
- * @version 1.5
11
- */
12
- class EDD_SL_Plugin_Updater {
13
- private $api_url = '';
14
- private $api_data = array();
15
- private $name = '';
16
- private $slug = '';
17
-
18
- /**
19
- * Class constructor.
20
- *
21
- * @uses plugin_basename()
22
- * @uses hook()
23
- *
24
- * @param string $_api_url The URL pointing to the custom API endpoint.
25
- * @param string $_plugin_file Path to the plugin file.
26
- * @param array $_api_data Optional data to send with API calls.
27
- * @return void
28
- */
29
- function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
30
- $this->api_url = trailingslashit( $_api_url );
31
- $this->api_data = $_api_data;
32
- $this->name = plugin_basename( $_plugin_file );
33
- $this->slug = basename( $_plugin_file, '.php' );
34
- $this->version = $_api_data['version'];
35
-
36
- // Set up hooks.
37
- $this->init();
38
- add_action( 'admin_init', array( $this, 'show_changelog' ) );
39
- }
40
-
41
- /**
42
- * Set up WordPress filters to hook into WP's update process.
43
- *
44
- * @uses add_filter()
45
- *
46
- * @return void
47
- */
48
- public function init() {
49
-
50
- add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
51
- add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
52
-
53
- add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
54
- }
55
-
56
- /**
57
- * Check for Updates at the defined API endpoint and modify the update array.
58
- *
59
- * This function dives into the update API just when WordPress creates its update array,
60
- * then adds a custom API call and injects the custom plugin data retrieved from the API.
61
- * It is reassembled from parts of the native WordPress plugin update code.
62
- * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
63
- *
64
- * @uses api_request()
65
- *
66
- * @param array $_transient_data Update array build by WordPress.
67
- * @return array Modified update array with custom plugin data.
68
- */
69
- function check_update( $_transient_data ) {
70
-
71
- if( ! is_object( $_transient_data ) ) {
72
- $_transient_data = new stdClass;
73
- }
74
-
75
- if ( empty( $_transient_data->response ) || empty( $_transient_data->response[ $this->name ] ) ) {
76
-
77
- $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
78
-
79
- if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
80
-
81
- $this->did_check = true;
82
-
83
- if( version_compare( $this->version, $version_info->new_version, '<' ) ) {
84
-
85
- $_transient_data->response[ $this->name ] = $version_info;
86
-
87
- }
88
-
89
- $_transient_data->last_checked = time();
90
- $_transient_data->checked[ $this->name ] = $this->version;
91
-
92
- }
93
-
94
- }
95
-
96
- return $_transient_data;
97
- }
98
-
99
- /**
100
- * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
101
- *
102
- * @param string $file
103
- * @param array $plugin
104
- */
105
- public function show_update_notification( $file, $plugin ) {
106
-
107
- if( ! current_user_can( 'update_plugins' ) ) {
108
- return;
109
- }
110
-
111
- if( ! is_multisite() ) {
112
- return;
113
- }
114
-
115
- if ( $this->name != $file ) {
116
- return;
117
- }
118
-
119
- // Remove our filter on the site transient
120
- remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
121
-
122
- $update_cache = get_site_transient( 'update_plugins' );
123
-
124
- if ( empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
125
-
126
- $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
127
-
128
- if( version_compare( $this->version, $version_info->new_version, '<' ) ) {
129
-
130
- $update_cache->response[ $this->name ] = $version_info;
131
-
132
- }
133
-
134
- $update_cache->last_checked = time();
135
- $update_cache->checked[ $this->name ] = $this->version;
136
-
137
- set_site_transient( 'update_plugins', $update_cache );
138
-
139
- }
140
-
141
- // Restore our filter
142
- add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
143
-
144
- if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
145
-
146
- // build a plugin list row, with update notification
147
- $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
148
- echo '<tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
149
-
150
- $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
151
-
152
- if ( empty( $version_info->download_link ) ) {
153
- printf(
154
- __( 'There is a new version of %1$s available. <a target="_blank" class="thickbox" href="%2$s">View version %3$s details</a>.' ),
155
- esc_html( $version_info->name ),
156
- esc_url( $changelog_link ),
157
- esc_html( $version_info->new_version )
158
- );
159
- } else {
160
- printf(
161
- __( 'There is a new version of %1$s available. <a target="_blank" class="thickbox" href="%2$s">View version %3$s details</a> or <a href="%4$s">update now</a>.' ),
162
- esc_html( $version_info->name ),
163
- esc_url( $changelog_link ),
164
- esc_html( $version_info->new_version ),
165
- esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) )
166
- );
167
- }
168
-
169
- echo '</div></td></tr>';
170
- }
171
- }
172
-
173
-
174
- /**
175
- * Updates information on the "View version x.x details" page with custom data.
176
- *
177
- * @uses api_request()
178
- *
179
- * @param mixed $_data
180
- * @param string $_action
181
- * @param object $_args
182
- * @return object $_data
183
- */
184
- function plugins_api_filter( $_data, $_action = '', $_args = null ) {
185
-
186
-
187
- if ( $_action != 'plugin_information' ) {
188
-
189
- return $_data;
190
-
191
- }
192
-
193
- if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
194
-
195
- return $_data;
196
-
197
- }
198
-
199
- $to_send = array(
200
- 'slug' => $this->slug,
201
- 'is_ssl' => is_ssl(),
202
- 'fields' => array(
203
- 'banners' => false, // These will be supported soon hopefully
204
- 'reviews' => false
205
- )
206
- );
207
-
208
- $api_response = $this->api_request( 'plugin_information', $to_send );
209
-
210
- if ( false !== $api_response ) {
211
- $_data = $api_response;
212
- }
213
-
214
- return $_data;
215
- }
216
-
217
-
218
- /**
219
- * Disable SSL verification in order to prevent download update failures
220
- *
221
- * @param array $args
222
- * @param string $url
223
- * @return object $array
224
- */
225
- function http_request_args( $args, $url ) {
226
- // If it is an https request and we are performing a package download, disable ssl verification
227
- if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
228
- $args['sslverify'] = false;
229
- }
230
- return $args;
231
- }
232
-
233
- /**
234
- * Calls the API and, if successfull, returns the object delivered by the API.
235
- *
236
- * @uses get_bloginfo()
237
- * @uses wp_remote_post()
238
- * @uses is_wp_error()
239
- *
240
- * @param string $_action The requested action.
241
- * @param array $_data Parameters for the API action.
242
- * @return false||object
243
- */
244
- private function api_request( $_action, $_data ) {
245
-
246
- global $wp_version;
247
-
248
- $data = array_merge( $this->api_data, $_data );
249
-
250
- if ( $data['slug'] != $this->slug )
251
- return;
252
-
253
- if ( empty( $data['license'] ) )
254
- return;
255
-
256
- if( $this->api_url == home_url() ) {
257
- return false; // Don't allow a plugin to ping itself
258
- }
259
-
260
- $api_params = array(
261
- 'edd_action' => 'get_version',
262
- 'license' => $data['license'],
263
- 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
264
- 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
265
- 'slug' => $this->slug,
266
- 'author' => $data['author'],
267
- 'url' => home_url()
268
- );
269
-
270
- $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
271
-
272
- if ( ! is_wp_error( $request ) ) {
273
- $request = json_decode( wp_remote_retrieve_body( $request ) );
274
- }
275
-
276
- if ( $request && isset( $request->sections ) ) {
277
- $request->sections = maybe_unserialize( $request->sections );
278
- } else {
279
- $request = false;
280
- }
281
-
282
- return $request;
283
- }
284
-
285
- public function show_changelog() {
286
-
287
-
288
- if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
289
- return;
290
- }
291
-
292
- if( empty( $_REQUEST['plugin'] ) ) {
293
- return;
294
- }
295
-
296
- if( empty( $_REQUEST['slug'] ) ) {
297
- return;
298
- }
299
-
300
- if( ! current_user_can( 'update_plugins' ) ) {
301
- wp_die( __( 'You do not have permission to install plugin updates' ), __( 'Error', 'edd_sl' ), array( 'response' => 401 ) );
302
- }
303
-
304
- $response = $this->api_request( 'plugin_latest_version', array( 'slug' => $_REQUEST['slug'] ) );
305
-
306
- if( $response && isset( $response->sections['changelog'] ) ) {
307
- echo '<div style="background:#fff;padding:10px;">' . $response->sections['changelog'] . '</div>';
308
- }
309
-
310
-
311
- exit;
312
- }
313
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
314
  }
1
+ <?php
2
+
3
+ // uncomment this line for testing
4
+ //set_site_transient( 'update_plugins', null );
5
+
6
+ /**
7
+ * Allows plugins to use their own update API.
8
+ *
9
+ * @author Pippin Williamson
10
+ * @version 1.6
11
+ */
12
+ class EDD_SL_Plugin_Updater {
13
+ private $api_url = '';
14
+ private $api_data = array();
15
+ private $name = '';
16
+ private $slug = '';
17
+
18
+ /**
19
+ * Class constructor.
20
+ *
21
+ * @uses plugin_basename()
22
+ * @uses hook()
23
+ *
24
+ * @param string $_api_url The URL pointing to the custom API endpoint.
25
+ * @param string $_plugin_file Path to the plugin file.
26
+ * @param array $_api_data Optional data to send with API calls.
27
+ * @return void
28
+ */
29
+ function __construct( $_api_url, $_plugin_file, $_api_data = null ) {
30
+ $this->api_url = trailingslashit( $_api_url );
31
+ $this->api_data = $_api_data;
32
+ $this->name = plugin_basename( $_plugin_file );
33
+ $this->slug = basename( $_plugin_file, '.php' );
34
+ $this->version = $_api_data['version'];
35
+
36
+ // Set up hooks.
37
+ $this->init();
38
+ add_action( 'admin_init', array( $this, 'show_changelog' ) );
39
+ }
40
+
41
+ /**
42
+ * Set up WordPress filters to hook into WP's update process.
43
+ *
44
+ * @uses add_filter()
45
+ *
46
+ * @return void
47
+ */
48
+ public function init() {
49
+
50
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
51
+ add_filter( 'plugins_api', array( $this, 'plugins_api_filter' ), 10, 3 );
52
+
53
+ add_action( 'after_plugin_row_' . $this->name, array( $this, 'show_update_notification' ), 10, 2 );
54
+ }
55
+
56
+ /**
57
+ * Check for Updates at the defined API endpoint and modify the update array.
58
+ *
59
+ * This function dives into the update API just when WordPress creates its update array,
60
+ * then adds a custom API call and injects the custom plugin data retrieved from the API.
61
+ * It is reassembled from parts of the native WordPress plugin update code.
62
+ * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
63
+ *
64
+ * @uses api_request()
65
+ *
66
+ * @param array $_transient_data Update array build by WordPress.
67
+ * @return array Modified update array with custom plugin data.
68
+ */
69
+ function check_update( $_transient_data ) {
70
+
71
+ global $pagenow;
72
+
73
+ if( ! is_object( $_transient_data ) ) {
74
+ $_transient_data = new stdClass;
75
+ }
76
+
77
+ if( 'plugins.php' == $pagenow && is_multisite() ) {
78
+ return $_transient_data;
79
+ }
80
+
81
+ if ( empty( $_transient_data->response ) || empty( $_transient_data->response[ $this->name ] ) ) {
82
+
83
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
84
+
85
+ if ( false !== $version_info && is_object( $version_info ) && isset( $version_info->new_version ) ) {
86
+
87
+ $this->did_check = true;
88
+
89
+ if( version_compare( $this->version, $version_info->new_version, '<' ) ) {
90
+
91
+ $_transient_data->response[ $this->name ] = $version_info;
92
+
93
+ }
94
+
95
+ $_transient_data->last_checked = time();
96
+ $_transient_data->checked[ $this->name ] = $this->version;
97
+
98
+ }
99
+
100
+ }
101
+
102
+ return $_transient_data;
103
+ }
104
+
105
+ /**
106
+ * show update nofication row -- needed for multisite subsites, because WP won't tell you otherwise!
107
+ *
108
+ * @param string $file
109
+ * @param array $plugin
110
+ */
111
+ public function show_update_notification( $file, $plugin ) {
112
+
113
+ if( ! current_user_can( 'update_plugins' ) ) {
114
+ return;
115
+ }
116
+
117
+ if( ! is_multisite() ) {
118
+ return;
119
+ }
120
+
121
+ if ( $this->name != $file ) {
122
+ return;
123
+ }
124
+
125
+ // Remove our filter on the site transient
126
+ remove_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ), 10 );
127
+
128
+ $update_cache = get_site_transient( 'update_plugins' );
129
+
130
+ if ( ! is_object( $update_cache ) || empty( $update_cache->response ) || empty( $update_cache->response[ $this->name ] ) ) {
131
+
132
+ $cache_key = md5( 'edd_plugin_' .sanitize_key( $this->name ) . '_version_info' );
133
+ $version_info = get_transient( $cache_key );
134
+
135
+ if( false === $version_info ) {
136
+
137
+ $version_info = $this->api_request( 'plugin_latest_version', array( 'slug' => $this->slug ) );
138
+
139
+ set_transient( $cache_key, $version_info, 3600 );
140
+ }
141
+
142
+
143
+ if( ! is_object( $version_info ) ) {
144
+ return;
145
+ }
146
+
147
+ if( version_compare( $this->version, $version_info->new_version, '<' ) ) {
148
+
149
+ $update_cache->response[ $this->name ] = $version_info;
150
+
151
+ }
152
+
153
+ $update_cache->last_checked = time();
154
+ $update_cache->checked[ $this->name ] = $this->version;
155
+
156
+ set_site_transient( 'update_plugins', $update_cache );
157
+
158
+ } else {
159
+
160
+ $version_info = $update_cache->response[ $this->name ];
161
+
162
+ }
163
+
164
+ // Restore our filter
165
+ add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_update' ) );
166
+
167
+ if ( ! empty( $update_cache->response[ $this->name ] ) && version_compare( $this->version, $version_info->new_version, '<' ) ) {
168
+
169
+ // build a plugin list row, with update notification
170
+ $wp_list_table = _get_list_table( 'WP_Plugins_List_Table' );
171
+ echo '<tr class="plugin-update-tr"><td colspan="' . $wp_list_table->get_column_count() . '" class="plugin-update colspanchange"><div class="update-message">';
172
+
173
+ $changelog_link = self_admin_url( 'index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911' );
174
+
175
+ if ( empty( $version_info->download_link ) ) {
176
+ printf(
177
+ __( 'There is a new version of %1$s available. <a target="_blank" class="thickbox" href="%2$s">View version %3$s details</a>.', 'edd' ),
178
+ esc_html( $version_info->name ),
179
+ esc_url( $changelog_link ),
180
+ esc_html( $version_info->new_version )
181
+ );
182
+ } else {
183
+ printf(
184
+ __( 'There is a new version of %1$s available. <a target="_blank" class="thickbox" href="%2$s">View version %3$s details</a> or <a href="%4$s">update now</a>.', 'edd' ),
185
+ esc_html( $version_info->name ),
186
+ esc_url( $changelog_link ),
187
+ esc_html( $version_info->new_version ),
188
+ esc_url( wp_nonce_url( self_admin_url( 'update.php?action=upgrade-plugin&plugin=' ) . $this->name, 'upgrade-plugin_' . $this->name ) )
189
+ );
190
+ }
191
+
192
+ echo '</div></td></tr>';
193
+ }
194
+ }
195
+
196
+
197
+ /**
198
+ * Updates information on the "View version x.x details" page with custom data.
199
+ *
200
+ * @uses api_request()
201
+ *
202
+ * @param mixed $_data
203
+ * @param string $_action
204
+ * @param object $_args
205
+ * @return object $_data
206
+ */
207
+ function plugins_api_filter( $_data, $_action = '', $_args = null ) {
208
+
209
+
210
+ if ( $_action != 'plugin_information' ) {
211
+
212
+ return $_data;
213
+
214
+ }
215
+
216
+ if ( ! isset( $_args->slug ) || ( $_args->slug != $this->slug ) ) {
217
+
218
+ return $_data;
219
+
220
+ }
221
+
222
+ $to_send = array(
223
+ 'slug' => $this->slug,
224
+ 'is_ssl' => is_ssl(),
225
+ 'fields' => array(
226
+ 'banners' => false, // These will be supported soon hopefully
227
+ 'reviews' => false
228
+ )
229
+ );
230
+
231
+ $api_response = $this->api_request( 'plugin_information', $to_send );
232
+
233
+ if ( false !== $api_response ) {
234
+ $_data = $api_response;
235
+ }
236
+
237
+ return $_data;
238
+ }
239
+
240
+
241
+ /**
242
+ * Disable SSL verification in order to prevent download update failures
243
+ *
244
+ * @param array $args
245
+ * @param string $url
246
+ * @return object $array
247
+ */
248
+ function http_request_args( $args, $url ) {
249
+ // If it is an https request and we are performing a package download, disable ssl verification
250
+ if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'edd_action=package_download' ) ) {
251
+ $args['sslverify'] = false;
252
+ }
253
+ return $args;
254
+ }
255
+
256
+ /**
257
+ * Calls the API and, if successfull, returns the object delivered by the API.
258
+ *
259
+ * @uses get_bloginfo()
260
+ * @uses wp_remote_post()
261
+ * @uses is_wp_error()
262
+ *
263
+ * @param string $_action The requested action.
264
+ * @param array $_data Parameters for the API action.
265
+ * @return false||object
266
+ */
267
+ private function api_request( $_action, $_data ) {
268
+
269
+ global $wp_version;
270
+
271
+ $data = array_merge( $this->api_data, $_data );
272
+
273
+ if ( $data['slug'] != $this->slug )
274
+ return;
275
+
276
+ if ( empty( $data['license'] ) )
277
+ return;
278
+
279
+ if( $this->api_url == home_url() ) {
280
+ return false; // Don't allow a plugin to ping itself
281
+ }
282
+
283
+ $api_params = array(
284
+ 'edd_action' => 'get_version',
285
+ 'license' => $data['license'],
286
+ 'item_name' => isset( $data['item_name'] ) ? $data['item_name'] : false,
287
+ 'item_id' => isset( $data['item_id'] ) ? $data['item_id'] : false,
288
+ 'slug' => $data['slug'],
289
+ 'author' => $data['author'],
290
+ 'url' => home_url()
291
+ );
292
+
293
+ $request = wp_remote_post( $this->api_url, array( 'timeout' => 15, 'sslverify' => false, 'body' => $api_params ) );
294
+
295
+ if ( ! is_wp_error( $request ) ) {
296
+ $request = json_decode( wp_remote_retrieve_body( $request ) );
297
+ }
298
+
299
+ if ( $request && isset( $request->sections ) ) {
300
+ $request->sections = maybe_unserialize( $request->sections );
301
+ } else {
302
+ $request = false;
303
+ }
304
+
305
+ return $request;
306
+ }
307
+
308
+ public function show_changelog() {
309
+
310
+
311
+ if( empty( $_REQUEST['edd_sl_action'] ) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action'] ) {
312
+ return;
313
+ }
314
+
315
+ if( empty( $_REQUEST['plugin'] ) ) {
316
+ return;
317
+ }
318
+
319
+ if( empty( $_REQUEST['slug'] ) ) {
320
+ return;
321
+ }
322
+
323
+ if( ! current_user_can( 'update_plugins' ) ) {
324
+ wp_die( __( 'You do not have permission to install plugin updates', 'edd' ), __( 'Error', 'edd' ), array( 'response' => 403 ) );
325
+ }
326
+
327
+ $response = $this->api_request( 'plugin_latest_version', array( 'slug' => $_REQUEST['slug'] ) );
328
+
329
+ if( $response && isset( $response->sections['changelog'] ) ) {
330
+ echo '<div style="background:#fff;padding:10px;">' . $response->sections['changelog'] . '</div>';
331
+ }
332
+
333
+
334
+ exit;
335
+ }
336
+
337
  }
includes/libraries/WordPress-Readme-Parser/ReadmeParser.php CHANGED
@@ -1,329 +1,329 @@
1
- <?php
2
- /**
3
- * Custom readme parser
4
- *
5
- * Based on Automattic_Readme from http://code.google.com/p/wordpress-plugin-readme-parser/
6
- *
7
- * Relies on Markdown_Extra
8
- *
9
- * @todo Handle screenshots section properly
10
- * @todo Create validator for this based on http://code.google.com/p/wordpress-plugin-readme-parser/source/browse/trunk/validator.php
11
- */
12
- class Baikonur_ReadmeParser {
13
-
14
- public static function parse_readme($file) {
15
- $contents = file($file);
16
- return self::parse_readme_contents($contents);
17
- }
18
-
19
- public static function parse_readme_contents($contents) {
20
- if (is_string($contents)) {
21
- $contents = explode("\n", $contents);
22
- }
23
-
24
- $this_class = __CLASS__;
25
- if (function_exists('get_called_class')) {
26
- $this_class = get_called_class();
27
- }
28
-
29
- $contents = array_map(array($this_class, 'strip_newlines'), $contents);
30
-
31
- // Strip BOM
32
- if (strpos($contents[0], "\xEF\xBB\xBF") === 0) {
33
- $contents[0] = substr($contents[0], 3);
34
- }
35
-
36
- $data = new stdClass;
37
-
38
- // Defaults
39
- $data->is_excerpt = false;
40
- $data->is_truncated = false;
41
- $data->tags = array();
42
- $data->requires = '';
43
- $data->tested = '';
44
- $data->contributors = array();
45
- $data->stable_tag = '';
46
- $data->version = '';
47
- $data->donate_link = '';
48
- $data->short_description = '';
49
- $data->sections = array();
50
- $data->changelog = array();
51
- $data->upgrade_notice = array();
52
- $data->screenshots = array();
53
- $data->remaining_content = array();
54
-
55
- $line = call_user_func_array(array($this_class, 'get_first_nonwhitespace'), array(&$contents));
56
- $data->name = $line;
57
- $data->name = trim($data->name, "#= ");
58
-
59
- // Parse headers
60
- $headers = array();
61
-
62
- $line = call_user_func_array(array($this_class, 'get_first_nonwhitespace'), array(&$contents));
63
- do {
64
- $key = $value = null;
65
- if (strpos($line, ':') === false) {
66
- break;
67
- }
68
- $bits = explode(':', $line, 2);
69
- list($key, $value) = $bits;
70
- $key = strtolower(str_replace(array(' ', "\t"), '_', trim($key)));
71
- if ($key === 'tags' && isset($headers['tags'])) {
72
- $headers[$key] .= ',' . trim($value);
73
- }
74
- else {
75
- $headers[$key] = trim($value);
76
- }
77
- }
78
- while (($line = array_shift($contents)) !== null && ($line = trim($line)) && !empty($line));
79
- array_unshift($contents, $line);
80
-
81
- if (!empty($headers['tags'])) {
82
- $data->tags = explode(',', $headers['tags']);
83
- $data->tags = array_map('trim', $data->tags);
84
- }
85
- if (!empty($headers['requires'])) {
86
- $data->requires = $headers['requires'];
87
- }
88
- if (!empty($headers['requires_at_least'])) {
89
- $data->requires = $headers['requires_at_least'];
90
- }
91
- if (!empty($headers['tested'])) {
92
- $data->tested = $headers['tested'];
93
- }
94
- if (!empty($headers['tested_up_to'])) {
95
- $data->tested = $headers['tested_up_to'];
96
- }
97
- if (!empty($headers['contributors'])) {
98
- $data->contributors = explode(',', $headers['contributors']);
99
- $data->contributors = array_map('trim', $data->contributors);
100
- }
101
- if (!empty($headers['stable_tag'])) {
102
- $data->stable_tag = $headers['stable_tag'];
103
- }
104
- if (!empty($headers['donate_link'])) {
105
- $data->donate_link = $headers['donate_link'];
106
- }
107
- if (!empty($headers['version'])) {
108
- $data->version = $headers['version'];
109
- }
110
- else {
111
- $data->version = $data->stable_tag;
112
- }
113
-
114
- // Parse the short description
115
- while (($line = array_shift($contents)) !== null) {
116
- $trimmed = trim($line);
117
- if (empty($trimmed)) {
118
- $data->short_description .= "\n";
119
- continue;
120
- }
121
- if ($trimmed[0] === '=' && isset($trimmed[1]) && $trimmed[1] === '=') {
122
- array_unshift($contents, $line);
123
- break;
124
- }
125
-
126
- $data->short_description .= $line . "\n";
127
- }
128
- $data->short_description = trim($data->short_description);
129
-
130
- $data->is_truncated = call_user_func_array(array($this_class, 'trim_short_desc'), array(&$data->short_description));
131
-
132
- // Parse the rest of the body
133
- $current = '';
134
- $special = array('description', 'installation', 'faq', 'frequently_asked_questions', 'screenshots', 'changelog', 'upgrade_notice');
135
-
136
- while (($line = array_shift($contents)) !== null) {
137
- $trimmed = trim($line);
138
- if (empty($trimmed)) {
139
- $current .= "\n";
140
- continue;
141
- }
142
-
143
- if ($trimmed[0] === '=' && isset($trimmed[1]) && $trimmed[1] === '=') {
144
- if (!empty($title)) {
145
- $data->sections[$title] = trim($current);
146
- }
147
-
148
- $current = '';
149
- $real_title = trim($line, "#= \t");
150
- $title = strtolower(str_replace(' ', '_', $real_title));
151
- if ($title === 'faq') {
152
- $title = 'frequently_asked_questions';
153
- }
154
- elseif ($title === 'change_log') {
155
- $title = 'changelog';
156
- }
157
- if (!in_array($title, $special)) {
158
- $current .= '<h3>' . $real_title . "</h3>";
159
- }
160
- continue;
161
- }
162
-
163
- $current .= $line . "\n";
164
- }
165
-
166
- if (!empty($title)) {
167
- $data->sections[$title] = trim($current);
168
- }
169
- $title = null;
170
- $current = null;
171
-
172
- if (empty($data->sections['description'])) {
173
- $data->sections['description'] = call_user_func(array($this_class, 'parse_markdown'), $data->short_description);
174
- }
175
-
176
- // Parse changelog
177
- if (!empty($data->sections['changelog'])) {
178
- $lines = explode("\n", $data->sections['changelog']);
179
- while (($line = array_shift($lines)) !== null) {
180
- $trimmed = trim($line);
181
- if (empty($trimmed)) {
182
- continue;
183
- }
184
-
185
- if ($trimmed[0] === '=') {
186
- if (!empty($current)) {
187
- $data->changelog[$title] = trim($current);
188
- }
189
-
190
- $current = '';
191
- $title = trim($line, "#= \t");
192
- continue;
193
- }
194
-
195
- $current .= $line . "\n";
196
- }
197
-
198
- $data->changelog[$title] = trim($current);
199
- }
200
- $title = null;
201
- $current = null;
202
-
203
- if (isset($data->sections['upgrade_notice'])) {
204
- $lines = explode("\n", $data->sections['upgrade_notice']);
205
- while (($line = array_shift($lines)) !== null) {
206
- $trimmed = trim($line);
207
- if (empty($trimmed)) {
208
- continue;
209
- }
210
-
211
- if ($trimmed[0] === '=') {
212
- if (!empty($current)) {
213
- $data->upgrade_notice[$title] = trim($current);
214
- }
215
-
216
- $current = '';
217
- $title = trim($line, "#= \t");
218
- continue;
219
- }
220
-
221
- $current .= $line . "\n";
222
- }
223
-
224
- if (!empty($title) && !empty($current)) {
225
- $data->upgrade_notice[$title] = trim($current);
226
- }
227
- unset($data->sections['upgrade_notice']);
228
- }
229
-
230
- // Markdownify!
231
-
232
- $data->sections = array_map(array($this_class, 'parse_markdown'), $data->sections);
233
- $data->changelog = array_map(array($this_class, 'parse_markdown'), $data->changelog);
234
- $data->upgrade_notice = array_map(array($this_class, 'parse_markdown'), $data->upgrade_notice);
235
-
236
- if (isset($data->sections['screenshots'])) {
237
- preg_match_all('#<li>(.*?)</li>#is', $data->sections['screenshots'], $screenshots, PREG_SET_ORDER);
238
- if ($screenshots) {
239
- foreach ((array) $screenshots as $ss) {
240
- $data->screenshots[] = trim($ss[1]);
241
- }
242
- }
243
- }
244
-
245
- // Rearrange stuff
246
-
247
- $data->remaining_content = $data->sections;
248
- $data->sections = array();
249
-
250
- foreach ($special as $spec) {
251
- if (isset($data->remaining_content[$spec])) {
252
- $data->sections[$spec] = $data->remaining_content[$spec];
253
- unset($data->remaining_content[$spec]);
254
- }
255
- }
256
-
257
- return $data;
258
- }
259
-
260
- protected static function get_first_nonwhitespace(&$contents) {
261
- while (($line = array_shift($contents)) !== null) {
262
- $trimmed = trim($line);
263
- if (!empty($line)) {
264
- break;
265
- }
266
- }
267
-
268
- return $line;
269
- }
270
-
271
- protected static function strip_newlines($line) {
272
- return rtrim($line, "\r\n");
273
- }
274
-
275
- protected static function trim_short_desc(&$desc) {
276
- if (function_exists('mb_strlen') && function_exists('mb_substr')) {
277
- if (mb_strlen($desc) > 150) {
278
- $desc = mb_substr($desc, 0, 150);
279
- $desc = trim($desc);
280
- return true;
281
- }
282
- }
283
- else {
284
- if (strlen($desc) > 150) {
285
- $desc = substr($desc, 0, 150);
286
- $desc = trim($desc);
287
- return true;
288
- }
289
- }
290
-
291
- return false;
292
- }
293
-
294
- protected static function parse_markdown($text) {
295
- $text = self::code_trick($text);
296
- $text = preg_replace('/^[\s]*=[\s]+(.+?)[\s]+=/m', "\n" . '<h4>$1</h4>' . "\n", $text);
297
- $text = Markdown(trim($text));
298
- return trim($text);
299
- }
300
-
301
- protected static function code_trick($text) {
302
- // If doing markdown, first take any user formatted code blocks and turn them into backticks so that
303
- // markdown will preserve things like underscores in code blocks
304
- $text = preg_replace_callback("!(<pre><code>|<code>)(.*?)(</code></pre>|</code>)!s", array(__CLASS__, 'decodeit'), $text);
305
-
306
- $text = str_replace(array("\r\n", "\r"), "\n", $text);
307
- // Markdown can do inline code, we convert bbPress style block level code to Markdown style
308
- $text = preg_replace_callback("!(^|\n)([ \t]*?)`(.*?)`!s", array(__CLASS__, 'indent'), $text);
309
- return $text;
310
- }
311
-
312
- protected static function indent($matches) {
313
- $text = $matches[3];
314
- $text = preg_replace('|^|m', $matches[2] . ' ', $text);
315
- return $matches[1] . $text;
316
- }
317
-
318
- protected static function decodeit($matches) {
319
- $text = $matches[2];
320
- $trans_table = array_flip(get_html_translation_table(HTML_ENTITIES));
321
- $text = strtr($text, $trans_table);
322
- $text = str_replace('<br />', '', $text);
323
- $text = str_replace('&#38;', '&', $text);
324
- $text = str_replace('&#39;', "'", $text);
325
- if ( '<pre><code>' == $matches[1] )
326
- $text = "\n$text\n";
327
- return "`$text`";
328
- }
329
  }
1
+ <?php
2
+ /**
3
+ * Custom readme parser
4
+ *
5
+ * Based on Automattic_Readme from http://code.google.com/p/wordpress-plugin-readme-parser/
6
+ *
7
+ * Relies on Markdown_Extra
8
+ *
9
+ * @todo Handle screenshots section properly
10
+ * @todo Create validator for this based on http://code.google.com/p/wordpress-plugin-readme-parser/source/browse/trunk/validator.php
11
+ */
12
+ class Baikonur_ReadmeParser {
13
+
14
+ public static function parse_readme($file) {
15
+ $contents = file($file);
16
+ return self::parse_readme_contents($contents);
17
+ }
18
+
19
+ public static function parse_readme_contents($contents) {
20
+ if (is_string($contents)) {
21
+ $contents = explode("\n", $contents);
22
+ }
23
+
24
+ $this_class = __CLASS__;
25
+ if (function_exists('get_called_class')) {
26
+ $this_class = get_called_class();
27
+ }
28
+
29
+ $contents = array_map(array($this_class, 'strip_newlines'), $contents);
30
+
31
+ // Strip BOM
32
+ if (strpos($contents[0], "\xEF\xBB\xBF") === 0) {
33
+ $contents[0] = substr($contents[0], 3);
34
+ }
35
+
36
+ $data = new stdClass;
37
+
38
+ // Defaults
39
+ $data->is_excerpt = false;
40
+ $data->is_truncated = false;
41
+ $data->tags = array();
42
+ $data->requires = '';
43
+ $data->tested = '';
44
+ $data->contributors = array();
45
+ $data->stable_tag = '';
46
+ $data->version = '';
47
+ $data->donate_link = '';
48
+ $data->short_description = '';
49
+ $data->sections = array();
50
+ $data->changelog = array();
51
+ $data->upgrade_notice = array();
52
+ $data->screenshots = array();
53
+ $data->remaining_content = array();
54
+
55
+ $line = call_user_func_array(array($this_class, 'get_first_nonwhitespace'), array(&$contents));
56
+ $data->name = $line;
57
+ $data->name = trim($data->name, "#= ");
58
+
59
+ // Parse headers
60
+ $headers = array();
61
+
62
+ $line = call_user_func_array(array($this_class, 'get_first_nonwhitespace'), array(&$contents));
63
+ do {
64
+ $key = $value = null;
65
+ if (strpos($line, ':') === false) {
66
+ break;
67
+ }
68
+ $bits = explode(':', $line, 2);
69
+ list($key, $value) = $bits;
70
+ $key = strtolower(str_replace(array(' ', "\t"), '_', trim($key)));
71
+ if ($key === 'tags' && isset($headers['tags'])) {
72
+ $headers[$key] .= ',' . trim($value);
73
+ }
74
+ else {
75
+ $headers[$key] = trim($value);
76
+ }
77
+ }
78
+ while (($line = array_shift($contents)) !== null && ($line = trim($line)) && !empty($line));
79
+ array_unshift($contents, $line);
80
+
81
+ if (!empty($headers['tags'])) {
82
+ $data->tags = explode(',', $headers['tags']);
83
+ $data->tags = array_map('trim', $data->tags);
84
+ }
85
+ if (!empty($headers['requires'])) {
86
+ $data->requires = $headers['requires'];
87
+ }
88
+ if (!empty($headers['requires_at_least'])) {
89
+ $data->requires = $headers['requires_at_least'];
90
+ }
91
+ if (!empty($headers['tested'])) {
92
+ $data->tested = $headers['tested'];
93
+ }
94
+ if (!empty($headers['tested_up_to'])) {
95
+ $data->tested = $headers['tested_up_to'];
96
+ }
97
+ if (!empty($headers['contributors'])) {
98
+ $data->contributors = explode(',', $headers['contributors']);
99
+ $data->contributors = array_map('trim', $data->contributors);
100
+ }
101
+ if (!empty($headers['stable_tag'])) {
102
+ $data->stable_tag = $headers['stable_tag'];
103
+ }
104
+ if (!empty($headers['donate_link'])) {
105
+ $data->donate_link = $headers['donate_link'];
106
+ }
107
+ if (!empty($headers['version'])) {
108
+ $data->version = $headers['version'];
109
+ }
110
+ else {
111
+ $data->version = $data->stable_tag;
112
+ }
113
+
114
+ // Parse the short description
115
+ while (($line = array_shift($contents)) !== null) {
116
+ $trimmed = trim($line);
117
+ if (empty($trimmed)) {
118
+ $data->short_description .= "\n";
119
+ continue;
120
+ }
121
+ if ($trimmed[0] === '=' && isset($trimmed[1]) && $trimmed[1] === '=') {
122
+ array_unshift($contents, $line);
123
+ break;
124
+ }
125
+
126
+ $data->short_description .= $line . "\n";
127
+ }
128
+ $data->short_description = trim($data->short_description);
129
+
130
+ $data->is_truncated = call_user_func_array(array($this_class, 'trim_short_desc'), array(&$data->short_description));
131
+
132
+ // Parse the rest of the body
133
+ $current = '';
134
+ $special = array('description', 'installation', 'faq', 'frequently_asked_questions', 'screenshots', 'changelog', 'upgrade_notice');
135
+
136
+ while (($line = array_shift($contents)) !== null) {
137
+ $trimmed = trim($line);
138
+ if (empty($trimmed)) {
139
+ $current .= "\n";
140
+ continue;
141
+ }
142
+
143
+ if ($trimmed[0] === '=' && isset($trimmed[1]) && $trimmed[1] === '=') {
144
+ if (!empty($title)) {
145
+ $data->sections[$title] = trim($current);
146
+ }
147
+
148
+ $current = '';
149
+ $real_title = trim($line, "#= \t");
150
+ $title = strtolower(str_replace(' ', '_', $real_title));
151
+ if ($title === 'faq') {
152
+ $title = 'frequently_asked_questions';
153
+ }
154
+ elseif ($title === 'change_log') {
155
+ $title = 'changelog';
156
+ }
157
+ if (!in_array($title, $special)) {
158
+ $current .= '<h3>' . $real_title . "</h3>";
159
+ }
160
+ continue;
161
+ }
162
+
163
+ $current .= $line . "\n";
164
+ }
165
+
166
+ if (!empty($title)) {
167
+ $data->sections[$title] = trim($current);
168
+ }
169
+ $title = null;
170
+ $current = null;
171
+
172
+ if (empty($data->sections['description'])) {
173
+ $data->sections['description'] = call_user_func(array($this_class, 'parse_markdown'), $data->short_description);
174
+ }
175
+
176
+ // Parse changelog
177
+ if (!empty($data->sections['changelog'])) {
178
+ $lines = explode("\n", $data->sections['changelog']);
179
+ while (($line = array_shift($lines)) !== null) {
180
+ $trimmed = trim($line);
181
+ if (empty($trimmed)) {
182
+ continue;
183
+ }
184
+
185
+ if ($trimmed[0] === '=') {
186
+ if (!empty($current)) {
187
+ $data->changelog[$title] = trim($current);
188
+ }
189
+
190
+ $current = '';
191
+ $title = trim($line, "#= \t");
192
+ continue;
193
+ }
194
+
195
+ $current .= $line . "\n";
196
+ }
197
+
198
+ $data->changelog[$title] = trim($current);
199
+ }
200
+ $title = null;
201
+ $current = null;
202
+
203
+ if (isset($data->sections['upgrade_notice'])) {
204
+ $lines = explode("\n", $data->sections['upgrade_notice']);
205
+ while (($line = array_shift($lines)) !== null) {
206
+ $trimmed = trim($line);
207
+ if (empty($trimmed)) {
208
+ continue;
209
+ }
210
+
211
+ if ($trimmed[0] === '=') {
212
+ if (!empty($current)) {
213
+ $data->upgrade_notice[$title] = trim($current);
214
+ }
215
+
216
+ $current = '';
217
+ $title = trim($line, "#= \t");
218
+ continue;
219
+ }
220
+
221
+ $current .= $line . "\n";
222
+ }
223
+
224
+ if (!empty($title) && !empty($current)) {
225
+ $data->upgrade_notice[$title] = trim($current);
226
+ }
227
+ unset($data->sections['upgrade_notice']);
228
+ }
229
+
230
+ // Markdownify!
231
+
232
+ $data->sections = array_map(array($this_class, 'parse_markdown'), $data->sections);
233
+ $data->changelog = array_map(array($this_class, 'parse_markdown'), $data->changelog);
234
+ $data->upgrade_notice = array_map(array($this_class, 'parse_markdown'), $data->upgrade_notice);
235
+
236
+ if (isset($data->sections['screenshots'])) {
237
+ preg_match_all('#<li>(.*?)</li>#is', $data->sections['screenshots'], $screenshots, PREG_SET_ORDER);
238
+ if ($screenshots) {
239
+ foreach ((array) $screenshots as $ss) {
240
+ $data->screenshots[] = trim($ss[1]);
241
+ }
242
+ }
243
+ }
244
+
245
+ // Rearrange stuff
246
+
247
+ $data->remaining_content = $data->sections;
248
+ $data->sections = array();
249
+
250
+ foreach ($special as $spec) {
251
+ if (isset($data->remaining_content[$spec])) {
252
+ $data->sections[$spec] = $data->remaining_content[$spec];
253
+ unset($data->remaining_content[$spec]);
254
+ }
255
+ }
256
+
257
+ return $data;
258
+ }
259
+
260
+ protected static function get_first_nonwhitespace(&$contents) {
261
+ while (($line = array_shift($contents)) !== null) {
262
+ $trimmed = trim($line);
263
+ if (!empty($line)) {
264
+ break;
265
+ }
266
+ }
267
+
268
+ return $line;
269
+ }
270
+
271
+ protected static function strip_newlines($line) {
272
+ return rtrim($line, "\r\n");
273
+ }
274
+
275
+ protected static function trim_short_desc(&$desc) {
276
+ if (function_exists('mb_strlen') && function_exists('mb_substr')) {
277
+ if (mb_strlen($desc) > 150) {
278
+ $desc = mb_substr($desc, 0, 150);
279
+ $desc = trim($desc);
280
+ return true;
281
+ }
282
+ }
283
+ else {
284
+ if (strlen($desc) > 150) {
285
+ $desc = substr($desc, 0, 150);
286
+ $desc = trim($desc);
287
+ return true;
288
+ }
289
+ }
290
+
291
+ return false;
292
+ }
293
+
294
+ protected static function parse_markdown($text) {
295
+ $text = self::code_trick($text);
296
+ $text = preg_replace('/^[\s]*=[\s]+(.+?)[\s]+=/m', "\n" . '<h4>$1</h4>' . "\n", $text);
297
+ $text = Markdown(trim($text));
298
+ return trim($text);
299
+ }
300
+
301
+ protected static function code_trick($text) {
302
+ // If doing markdown, first take any user formatted code blocks and turn them into backticks so that
303
+ // markdown will preserve things like underscores in code blocks
304
+ $text = preg_replace_callback("!(<pre><code>|<code>)(.*?)(</code></pre>|</code>)!s", array(__CLASS__, 'decodeit'), $text);
305
+
306
+ $text = str_replace(array("\r\n", "\r"), "\n", $text);
307
+ // Markdown can do inline code, we convert bbPress style block level code to Markdown style
308
+ $text = preg_replace_callback("!(^|\n)([ \t]*?)`(.*?)`!s", array(__CLASS__, 'indent'), $text);
309
+ return $text;
310
+ }
311
+
312
+ protected static function indent($matches) {
313
+ $text = $matches[3];
314
+ $text = preg_replace('|^|m', $matches[2] . ' ', $text);
315
+ return $matches[1] . $text;
316
+ }
317
+
318
+ protected static function decodeit($matches) {
319
+ $text = $matches[2];
320
+ $trans_table = array_flip(get_html_translation_table(HTML_ENTITIES));
321
+ $text = strtr($text, $trans_table);
322
+ $text = str_replace('<br />', '', $text);
323
+ $text = str_replace('&#38;', '&', $text);
324
+ $text = str_replace('&#39;', "'", $text);
325
+ if ( '<pre><code>' == $matches[1] )
326
+ $text = "\n$text\n";
327
+ return "`$text`";
328
+ }
329
  }
includes/libraries/browser.php CHANGED
@@ -1,1082 +1,1082 @@
1
- <?php
2
- /**
3
- * Modified to remove var
4
- * Chris Christoff on 12/26/2012
5
- * Changes: Changes vars to publics
6
- *
7
- * Modified to work for EDD by
8
- * Chris Christoff on 12/23/2012
9
- * Changes: Removed the browser string return and added spacing. Also removed return HTML formatting.
10
- *
11
- * Modified to add formatted User Agent string for EDD System Info by
12
- * Chris Christoff on 12/23/2012
13
- * Changes: Split user string and add formatting so we can print a nicely
14
- * formatted user agent string on the EDD System Info
15
- *
16
- * File: Browser.php
17
- * Author: Chris Schuld (http://chrisschuld.com/)
18
- * Last Modified: August 20th, 2010
19
- * @version 1.9
20
- * @package PegasusPHP
21
- *
22
- * Copyright (C) 2008-2010 Chris Schuld (chris@chrisschuld.com)
23
- *
24
- * This program is free software; you can redistribute it and/or
25
- * modify it under the terms of the GNU General Public License as
26
- * published by the Free Software Foundation; either version 2 of
27
- * the License, or (at your option) any later version.
28
- *
29
- * This program is distributed in the hope that it will be useful,
30
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
- * GNU General Public License for more details at:
33
- * http://www.gnu.org/copyleft/gpl.html
34
- *
35
- *
36
- * Typical Usage:
37
- *
38
- * $browser = new Browser();
39
- * if( $browser->getBrowser() == Browser::BROWSER_FIREFOX && $browser->getVersion() >= 2 ) {
40
- * echo 'You have FireFox version 2 or greater';
41
- * }
42
- *
43
- * User Agents Sampled from: http://www.useragentstring.com/
44
- *
45
- * This implementation is based on the original work from Gary White
46
- * http://apptools.com/phptools/browser/
47
- *
48
- * UPDATES:
49
- *
50
- * 2010-08-20 (v1.9):
51
- * + Added MSN Explorer Browser (legacy)
52
- * + Added Bing/MSN Robot (Thanks Rob MacDonald)
53
- * + Added the Android Platform (PLATFORM_ANDROID)
54
- * + Fixed issue with Android 1.6/2.2 (Thanks Tom Hirashima)
55
- *
56
- * 2010-04-27 (v1.8):
57
- * + Added iPad Support
58
- *
59
- * 2010-03-07 (v1.7):
60
- * + *MAJOR* Rebuild (preg_match and other "slow" routine removal(s))
61
- * + Almost allof Gary's original code has been replaced
62
- * + Large PHPUNIT testing environment created to validate new releases and additions
63
- * + Added FreeBSD Platform
64
- * + Added OpenBSD Platform
65
- * + Added NetBSD Platform
66
- * + Added SunOS Platform
67
- * + Added OpenSolaris Platform
68
- * + Added support of the Iceweazel Browser
69
- * + Added isChromeFrame() call to check if chromeframe is in use
70
- * + Moved the Opera check in front of the Firefox check due to legacy Opera User Agents
71
- * + Added the __toString() method (Thanks Deano)
72
- *
73
- * 2009-11-15:
74
- * + Updated the checkes for Firefox
75
- * + Added the NOKIA platform
76
- * + Added Checks for the NOKIA brower(s)
77
- *
78
- * 2009-11-08:
79
- * + PHP 5.3 Support
80
- * + Added support for BlackBerry OS and BlackBerry browser
81
- * + Added support for the Opera Mini browser
82
- * + Added additional documenation
83
- * + Added support for isRobot() and isMobile()
84
- * + Added support for Opera version 10
85
- * + Added support for deprecated Netscape Navigator version 9
86
- * + Added support for IceCat
87
- * + Added support for Shiretoko
88
- *
89
- * 2010-04-27 (v1.8):
90
- * + Added iPad Support
91
- *
92
- * 2009-08-18:
93
- * + Updated to support PHP 5.3 - removed all deprecated function calls
94
- * + Updated to remove all double quotes (") -- converted to single quotes (')
95
- *
96
- * 2009-04-27:
97
- * + Updated the IE check to remove a typo and bug (thanks John)
98
- *
99
- * 2009-04-22:
100
- * + Added detection for GoogleBot
101
- * + Added detection for the W3C Validator.
102
- * + Added detection for Yahoo! Slurp
103
- *
104
- * 2009-03-14:
105
- * + Added detection for iPods.
106
- * + Added Platform detection for iPhones
107
- * + Added Platform detection for iPods
108
- *
109
- * 2009-02-16: (Rick Hale)
110
- * + Added version detection for Android phones.
111
- *
112
- * 2008-12-09:
113
- * + Removed unused constant
114
- *
115
- * 2008-11-07:
116
- * + Added Google's Chrome to the detection list
117
- * + Added isBrowser(string) to the list of functions special thanks to
118
- * Daniel 'mavrick' Lang for the function concept (http://mavrick.id.au)
119
- *
120
- *
121
- * Gary White noted: "Since browser detection is so unreliable, I am
122
- * no longer maintaining this script. You are free to use and or
123
- * modify/update it as you want, however the author assumes no
124
- * responsibility for the accuracy of the detected values."
125
- *
126
- * Anyone experienced with Gary's script might be interested in these notes:
127
- *
128
- * Added class constants
129
- * Added detection and version detection for Google's Chrome
130
- * Updated the version detection for Amaya
131
- * Updated the version detection for Firefox
132
- * Updated the version detection for Lynx
133
- * Updated the version detection for WebTV
134
- * Updated the version detection for NetPositive
135
- * Updated the version detection for IE
136
- * Updated the version detection for OmniWeb
137
- * Updated the version detection for iCab
138
- * Updated the version detection for Safari
139
- * Updated Safari to remove mobile devices (iPhone)
140
- * Added detection for iPhone
141
- * Added detection for robots
142
- * Added detection for mobile devices
143
- * Added detection for BlackBerry
144
- * Removed Netscape checks (matches heavily with firefox & mozilla)
145
- *
146
- */
147
-
148
- class Browser {
149
- public $_agent = '';
150
- public $_browser_name = '';
151
- public $_version = '';
152
- public $_platform = '';
153
- public $_os = '';
154
- public $_is_aol = false;
155
- public $_is_mobile = false;
156
- public $_is_robot = false;
157
- public $_aol_version = '';
158
-
159
- public $BROWSER_UNKNOWN = 'unknown';
160
- public $VERSION_UNKNOWN = 'unknown';
161
-
162
- public $BROWSER_OPERA = 'Opera'; // Http://www.opera.com/
163
- public $BROWSER_OPERA_MINI = 'Opera Mini'; // Http://www.opera.com/mini/
164
- public $BROWSER_WEBTV = 'WebTV'; // Http://www.webtv.net/pc/
165
- public $BROWSER_IE = 'Internet Explorer'; // Http://www.microsoft.com/ie/
166
- public $BROWSER_POCKET_IE = 'Pocket Internet Explorer'; // Http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
167
- public $BROWSER_KONQUEROR = 'Konqueror'; // Http://www.konqueror.org/
168
- public $BROWSER_ICAB = 'iCab'; // Http://www.icab.de/
169
- public $BROWSER_OMNIWEB = 'OmniWeb'; // Http://www.omnigroup.com/applications/omniweb/
170
- public $BROWSER_FIREBIRD = 'Firebird'; // Http://www.ibphoenix.com/
171
- public $BROWSER_FIREFOX = 'Firefox'; // Http://www.mozilla.com/en-US/firefox/firefox.html
172
- public $BROWSER_ICEWEASEL = 'Iceweasel'; // Http://www.geticeweasel.org/
173
- public $BROWSER_SHIRETOKO = 'Shiretoko'; // Http://wiki.mozilla.org/Projects/shiretoko
174
- public $BROWSER_MOZILLA = 'Mozilla'; // Http://www.mozilla.com/en-US/
175
- public $BROWSER_AMAYA = 'Amaya'; // Http://www.w3.org/Amaya/
176
- public $BROWSER_LYNX = 'Lynx'; // Http://en.wikipedia.org/wiki/Lynx
177
- public $BROWSER_SAFARI = 'Safari'; // Http://apple.com
178
- public $BROWSER_IPHONE = 'iPhone'; // Http://apple.com
179
- public $BROWSER_IPOD = 'iPod'; // Http://apple.com
180
- public $BROWSER_IPAD = 'iPad'; // Http://apple.com
181
- public $BROWSER_CHROME = 'Chrome'; // Http://www.google.com/chrome
182
- public $BROWSER_ANDROID = 'Android'; // Http://www.android.com/
183
- public $BROWSER_GOOGLEBOT = 'GoogleBot'; // Http://en.wikipedia.org/wiki/Googlebot
184
- public $BROWSER_SLURP = 'Yahoo! Slurp'; // Http://en.wikipedia.org/wiki/Yahoo!_Slurp
185
- public $BROWSER_W3CVALIDATOR = 'W3C Validator'; // Http://validator.w3.org/
186
- public $BROWSER_BLACKBERRY = 'BlackBerry'; // Http://www.blackberry.com/
187
- public $BROWSER_ICECAT = 'IceCat'; // Http://en.wikipedia.org/wiki/GNU_IceCat
188
- public $BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser'; // Http://en.wikipedia.org/wiki/Web_Browser_for_S60
189
- public $BROWSER_NOKIA = 'Nokia Browser'; // * all other WAP-based browsers on the Nokia Platform
190
- public $BROWSER_MSN = 'MSN Browser'; // Http://explorer.msn.com/
191
- public $BROWSER_MSNBOT = 'MSN Bot'; // Http://search.msn.com/msnbot.htm
192
- // Http://en.wikipedia.org/wiki/Msnbot (used for Bing as well)
193
-
194
- public $BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator'; // Http://browser.netscape.com/ (DEPRECATED)
195
- public $BROWSER_GALEON = 'Galeon'; // Http://galeon.sourceforge.net/ (DEPRECATED)
196
- public $BROWSER_NETPOSITIVE = 'NetPositive'; // Http://en.wikipedia.org/wiki/NetPositive (DEPRECATED)
197
- public $BROWSER_PHOENIX = 'Phoenix'; // Http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox (DEPRECATED)
198
-
199
- public $PLATFORM_UNKNOWN = 'unknown';
200
- public $PLATFORM_WINDOWS = 'Windows';
201
- public $PLATFORM_WINDOWS_CE = 'Windows CE';
202
- public $PLATFORM_APPLE = 'Apple';
203
- public $PLATFORM_LINUX = 'Linux';
204
- public $PLATFORM_OS2 = 'OS/2';
205
- public $PLATFORM_BEOS = 'BeOS';
206
- public $PLATFORM_IPHONE = 'iPhone';
207
- public $PLATFORM_IPOD = 'iPod';
208
- public $PLATFORM_IPAD = 'iPad';
209
- public $PLATFORM_BLACKBERRY = 'BlackBerry';
210
- public $PLATFORM_NOKIA = 'Nokia';
211
- public $PLATFORM_FREEBSD = 'FreeBSD';
212
- public $PLATFORM_OPENBSD = 'OpenBSD';
213
- public $PLATFORM_NETBSD = 'NetBSD';
214
- public $PLATFORM_SUNOS = 'SunOS';
215
- public $PLATFORM_OPENSOLARIS = 'OpenSolaris';
216
- public $PLATFORM_ANDROID = 'Android';
217
-
218
- public $OPERATING_SYSTEM_UNKNOWN = 'unknown';
219
-
220
- function Browser($useragent="") {
221
- $this->reset();
222
- if( $useragent != "" ) {
223
- $this->setUserAgent($useragent);
224
- }
225
- else {
226
- $this->determine();
227
- }
228
- }
229
-
230
- /**
231
- * Reset all properties
232
- */
233
- function reset() {
234
- $this->_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "";
235
- $this->_browser_name = $this->BROWSER_UNKNOWN;
236
- $this->_version = $this->VERSION_UNKNOWN;
237
- $this->_platform = $this->PLATFORM_UNKNOWN;
238
- $this->_os = $this->OPERATING_SYSTEM_UNKNOWN;
239
- $this->_is_aol = false;
240
- $this->_is_mobile = false;
241
- $this->_is_robot = false;
242
- $this->_aol_version = $this->VERSION_UNKNOWN;
243
- }
244
-
245
- /**
246
- * Check to see if the specific browser is valid
247
- * @param string $browserName
248
- * @return True if the browser is the specified browser
249
- */
250
- function isBrowser($browserName) { return( 0 == strcasecmp($this->_browser_name, trim($browserName))); }
251
-
252
- /**
253
- * The name of the browser. All return types are from the class contants
254
- * @return string Name of the browser
255
- */
256
- function getBrowser() { return $this->_browser_name; }
257
- /**
258
- * Set the name of the browser
259
- * @param $browser The name of the Browser
260
- */
261
- function setBrowser($browser) { return $this->_browser_name = $browser; }
262
- /**
263
- * The name of the platform. All return types are from the class contants
264
- * @return string Name of the browser
265
- */
266
- function getPlatform() { return $this->_platform; }
267
- /**
268
- * Set the name of the platform
269
- * @param $platform The name of the Platform
270
- */
271
- function setPlatform($platform) { return $this->_platform = $platform; }
272
- /**
273
- * The version of the browser.
274
- * @return string Version of the browser (will only contain alpha-numeric characters and a period)
275
- */
276
- function getVersion() { return $this->_version; }
277
- /**
278
- * Set the version of the browser
279
- * @param $version The version of the Browser
280
- */
281
- function setVersion($version) { $this->_version = preg_replace('/[^0-9,.,a-z,A-Z-]/','',$version); }
282
- /**
283
- * The version of AOL.
284
- * @return string Version of AOL (will only contain alpha-numeric characters and a period)
285
- */
286
- function getAolVersion() { return $this->_aol_version; }
287
- /**
288
- * Set the version of AOL
289
- * @param $version The version of AOL
290
- */
291
- function setAolVersion($version) { $this->_aol_version = preg_replace('/[^0-9,.,a-z,A-Z]/','',$version); }
292
- /**
293
- * Is the browser from AOL?
294
- * @return boolean True if the browser is from AOL otherwise false
295
- */
296
- function isAol() { return $this->_is_aol; }
297
- /**
298
- * Is the browser from a mobile device?
299
- * @return boolean True if the browser is from a mobile device otherwise false
300
- */
301
- function isMobile() { return $this->_is_mobile; }
302
- /**
303
- * Is the browser from a robot (ex Slurp,GoogleBot)?
304
- * @return boolean True if the browser is from a robot otherwise false
305
- */
306
- function isRobot() { return $this->_is_robot; }
307
- /**
308
- * Set the browser to be from AOL
309
- * @param $isAol
310
- */
311
- function setAol($isAol) { $this->_is_aol = $isAol; }
312
- /**
313
- * Set the Browser to be mobile
314
- * @param boolean $value is the browser a mobile brower or not
315
- */
316
- function setMobile($value=true) { $this->_is_mobile = $value; }
317
- /**
318
- * Set the Browser to be a robot
319
- * @param boolean $value is the browser a robot or not
320
- */
321
- function setRobot($value=true) { $this->_is_robot = $value; }
322
- /**
323
- * Get the user agent value in use to determine the browser
324
- * @return string The user agent from the HTTP header
325
- */
326
- function getUserAgent() { return $this->_agent; }
327
- /**
328
- * Set the user agent value (the construction will use the HTTP header value - this will overwrite it)
329
- * @param $agent_string The value for the User Agent
330
- */
331
- function setUserAgent($agent_string) {
332
- $this->reset();
333
- $this->_agent = $agent_string;
334
- $this->determine();
335
- }
336
- /**
337
- * Used to determine if the browser is actually "chromeframe"
338
- * @since 1.7
339
- * @return boolean True if the browser is using chromeframe
340
- */
341
- function isChromeFrame() {
342
- return( strpos($this->_agent,"chromeframe") !== false );
343
- }
344
- /**
345
- * Returns a formatted string with a summary of the details of the browser.
346
- * @return string formatted string with a summary of the browser
347
- */
348
- function __toString() {
349
- $text1 = $this->getUserAgent(); //grabs the UA (user agent) string
350
- $UAline1 = substr($text1, 0, 32); //the first line we print should only be the first 32 characters of the UA string
351
- $text2 = $this->getUserAgent();//now we grab it again and save it to a string
352
- $towrapUA = str_replace($UAline1, '', $text2);//the rest of the printoff (other than first line) is equivolent
353
- // To the whole string minus the part we printed off. IE
354
- // User Agent: thefirst32charactersfromUAline1
355
- // the rest of it is now stored in
356
- // $text2 to be printed off
357
- // But we need to add spaces before each line that is split other than line 1
358
- $space = '';
359
- for($i = 0; $i < 25; $i++) {
360
- $space .= ' ';
361
- }
362
- // Now we split the remaining string of UA ($text2) into lines that are prefixed by spaces for formatting
363
- $wordwrapped = chunk_split($towrapUA, 32, "\n $space");
364
- return "Platform: {$this->getPlatform()} \n".
365
- "Browser Name: {$this->getBrowser()} \n" .
366
- "Browser Version: {$this->getVersion()} \n" .
367
- "User Agent String: $UAline1 \n\t\t\t " .
368
- "$wordwrapped";
369
- }
370
- /**
371
- * Protected routine to calculate and determine what the browser is in use (including platform)
372
- */
373
- function determine() {
374
- $this->checkPlatform();
375
- $this->checkBrowsers();
376
- $this->checkForAol();
377
- }
378
- /**
379
- * Protected routine to determine the browser type
380
- * @return boolean True if the browser was detected otherwise false
381
- */
382
- function checkBrowsers() {
383
- return (
384
- // Well-known, well-used
385
- // Special Notes:
386
- // (1) Opera must be checked before FireFox due to the odd
387
- // user agents used in some older versions of Opera
388
- // (2) WebTV is strapped onto Internet Explorer so we must
389
- // check for WebTV before IE
390
- // (3) (deprecated) Galeon is based on Firefox and needs to be
391
- // tested before Firefox is tested
392
- // (4) OmniWeb is based on Safari so OmniWeb check must occur
393
- // before Safari
394
- // (5) Netscape 9+ is based on Firefox so Netscape checks
395
- // before FireFox are necessary
396
- $this->checkBrowserWebTv() ||
397
- $this->checkBrowserInternetExplorer() ||
398
- $this->checkBrowserOpera() ||
399
- $this->checkBrowserGaleon() ||
400
- $this->checkBrowserNetscapeNavigator9Plus() ||
401
- $this->checkBrowserFirefox() ||
402
- $this->checkBrowserChrome() ||
403
- $this->checkBrowserOmniWeb() ||
404
-
405
- // Common mobile
406
- $this->checkBrowserAndroid() ||
407
- $this->checkBrowseriPad() ||
408
- $this->checkBrowseriPod() ||
409
- $this->checkBrowseriPhone() ||
410
- $this->checkBrowserBlackBerry() ||
411
- $this->checkBrowserNokia() ||
412
-
413
- // Common bots
414
- $this->checkBrowserGoogleBot() ||
415
- $this->checkBrowserMSNBot() ||
416
- $this->checkBrowserSlurp() ||
417
-
418
- // WebKit base check (post mobile and others)
419
- $this->checkBrowserSafari() ||
420
-
421
- // Everyone else
422
- $this->checkBrowserNetPositive() ||
423
- $this->checkBrowserFirebird() ||
424
- $this->checkBrowserKonqueror() ||
425
- $this->checkBrowserIcab() ||
426
- $this->checkBrowserPhoenix() ||
427
- $this->checkBrowserAmaya() ||
428
- $this->checkBrowserLynx() ||
429
-
430
- $this->checkBrowserShiretoko() ||
431
- $this->checkBrowserIceCat() ||
432
- $this->checkBrowserW3CValidator() ||
433
- $this->checkBrowserMozilla() /* Mozilla is such an open standard that you must check it last */
434
- );
435
- }
436
-
437
- /**
438
- * Determine if the user is using a BlackBerry (last updated 1.7)
439
- * @return boolean True if the browser is the BlackBerry browser otherwise false
440
- */
441
- function checkBrowserBlackBerry() {
442
- if( stripos($this->_agent,'blackberry') !== false ) {
443
- $aresult = explode("/",stristr($this->_agent,"BlackBerry"));
444
- $aversion = explode(' ',$aresult[1]);
445
- $this->setVersion($aversion[0]);
446
- $this->_browser_name = $this->BROWSER_BLACKBERRY;
447
- $this->setMobile(true);
448
- return true;
449
- }
450
- return false;
451
- }
452
-
453
- /**
454
- * Determine if the user is using an AOL User Agent (last updated 1.7)
455
- * @return boolean True if the browser is from AOL otherwise false
456
- */
457
- function checkForAol() {
458
- $this->setAol(false);
459
- $this->setAolVersion($this->VERSION_UNKNOWN);
460
-
461
- if( stripos($this->_agent,'aol') !== false ) {
462
- $aversion = explode(' ',stristr($this->_agent, 'AOL'));
463
- $this->setAol(true);
464
- $this->setAolVersion(preg_replace('/[^0-9\.a-z]/i', '', $aversion[1]));
465
- return true;
466
- }
467
- return false;
468
- }
469
-
470
- /**
471
- * Determine if the browser is the GoogleBot or not (last updated 1.7)
472
- * @return boolean True if the browser is the GoogletBot otherwise false
473
- */
474
- function checkBrowserGoogleBot() {
475
- if( stripos($this->_agent,'googlebot') !== false ) {
476
- $aresult = explode('/',stristr($this->_agent,'googlebot'));
477
- $aversion = explode(' ',$aresult[1]);
478
- $this->setVersion(str_replace(';','',$aversion[0]));
479
- $this->_browser_name = $this->BROWSER_GOOGLEBOT;
480
- $this->setRobot(true);
481
- return true;
482
- }
483
- return false;
484
- }
485
-
486
- /**
487
- * Determine if the browser is the MSNBot or not (last updated 1.9)
488
- * @return boolean True if the browser is the MSNBot otherwise false
489
- */
490
- function checkBrowserMSNBot() {
491
- if( stripos($this->_agent,"msnbot") !== false ) {
492
- $aresult = explode("/",stristr($this->_agent,"msnbot"));
493
- $aversion = explode(" ",$aresult[1]);
494
- $this->setVersion(str_replace(";","",$aversion[0]));
495
- $this->_browser_name = $this->BROWSER_MSNBOT;
496
- $this->setRobot(true);
497
- return true;
498
- }
499
- return false;
500
- }
501
-
502
- /**
503
- * Determine if the browser is the W3C Validator or not (last updated 1.7)
504
- * @return boolean True if the browser is the W3C Validator otherwise false
505
- */
506
- function checkBrowserW3CValidator() {
507
- if( stripos($this->_agent,'W3C-checklink') !== false ) {
508
- $aresult = explode('/',stristr($this->_agent,'W3C-checklink'));
509
- $aversion = explode(' ',$aresult[1]);
510
- $this->setVersion($aversion[0]);
511
- $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
512
- return true;
513
- }
514
- else if( stripos($this->_agent,'W3C_Validator') !== false ) {
515
- // Some of the Validator versions do not delineate w/ a slash - add it back in
516
- $ua = str_replace("W3C_Validator ", "W3C_Validator/", $this->_agent);
517
- $aresult = explode('/',stristr($ua,'W3C_Validator'));
518
- $aversion = explode(' ',$aresult[1]);
519
- $this->setVersion($aversion[0]);
520
- $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
521
- return true;
522
- }
523
- return false;
524
- }
525
-
526
- /**
527
- * Determine if the browser is the Yahoo! Slurp Robot or not (last updated 1.7)
528
- * @return boolean True if the browser is the Yahoo! Slurp Robot otherwise false
529
- */
530
- function checkBrowserSlurp() {
531
- if( stripos($this->_agent,'slurp') !== false ) {
532
- $aresult = explode('/',stristr($this->_agent,'Slurp'));
533
- $aversion = explode(' ',$aresult[1]);
534
- $this->setVersion($aversion[0]);
535
- $this->_browser_name = $this->BROWSER_SLURP;
536
- $this->setRobot(true);
537
- $this->setMobile(false);
538
- return true;
539
- }
540
- return false;
541
- }
542
-
543
- /**
544
- * Determine if the browser is Internet Explorer or not (last updated 1.7)
545
- * @return boolean True if the browser is Internet Explorer otherwise false
546
- */
547
- function checkBrowserInternetExplorer() {
548
-
549
- // Test for v1 - v1.5 IE
550
- if( stripos($this->_agent,'microsoft internet explorer') !== false ) {
551
- $this->setBrowser($this->BROWSER_IE);
552
- $this->setVersion('1.0');
553
- $aresult = stristr($this->_agent, '/');
554
- if( preg_match('/308|425|426|474|0b1/i', $aresult) ) {
555
- $this->setVersion('1.5');
556
- }
557
- return true;
558
- }
559
- // Test for versions > 1.5
560
- else if( stripos($this->_agent,'msie') !== false && stripos($this->_agent,'opera') === false ) {
561
- // See if the browser is the odd MSN Explorer
562
- if( stripos($this->_agent,'msnb') !== false ) {
563
- $aresult = explode(' ',stristr(str_replace(';','; ',$this->_agent),'MSN'));
564
- $this->setBrowser( $this->BROWSER_MSN );
565
- $this->setVersion(str_replace(array('(',')',';'),'',$aresult[1]));
566
- return true;
567
- }
568
- $aresult = explode(' ',stristr(str_replace(';','; ',$this->_agent),'msie'));
569
- $this->setBrowser( $this->BROWSER_IE );
570
- $this->setVersion(str_replace(array('(',')',';'),'',$aresult[1]));
571
- return true;
572
- }
573
- // Test for Pocket IE
574
- else if( stripos($this->_agent,'mspie') !== false || stripos($this->_agent,'pocket') !== false ) {
575
- $aresult = explode(' ',stristr($this->_agent,'mspie'));
576
- $this->setPlatform( $this->PLATFORM_WINDOWS_CE );
577
- $this->setBrowser( $this->BROWSER_POCKET_IE );
578
- $this->setMobile(true);
579
-
580
- if( stripos($this->_agent,'mspie') !== false ) {
581
- $this->setVersion($aresult[1]);
582
- }
583
- else {
584
- $aversion = explode('/',$this->_agent);
585
- $this->setVersion($aversion[1]);
586
- }
587
- return true;
588
- }
589
- return false;
590
- }
591
-
592
- /**
593
- * Determine if the browser is Opera or not (last updated 1.7)
594
- * @return boolean True if the browser is Opera otherwise false
595
- */
596
- function checkBrowserOpera() {
597
- if( stripos($this->_agent,'opera mini') !== false ) {
598
- $resultant = stristr($this->_agent, 'opera mini');
599
- if( preg_match('/\//',$resultant) ) {
600
- $aresult = explode('/',$resultant);
601
- $aversion = explode(' ',$aresult[1]);
602
- $this->setVersion($aversion[0]);
603
- }
604
- else {
605
- $aversion = explode(' ',stristr($resultant,'opera mini'));
606
- $this->setVersion($aversion[1]);
607
- }
608
- $this->_browser_name = $this->BROWSER_OPERA_MINI;
609
- $this->setMobile(true);
610
- return true;
611
- }
612
- else if( stripos($this->_agent,'opera') !== false ) {
613
- $resultant = stristr($this->_agent, 'opera');
614
- if( preg_match('/Version\/(10.*)$/',$resultant,$matches) ) {
615
- $this->setVersion($matches[1]);
616
- }
617
- else if( preg_match('/\//',$resultant) ) {
618
- $aresult = explode('/',str_replace("("," ",$resultant));
619
- $aversion = explode(' ',$aresult[1]);
620
- $this->setVersion($aversion[0]);
621
- }
622
- else {
623
- $aversion = explode(' ',stristr($resultant,'opera'));
624
- $this->setVersion(isset($aversion[1])?$aversion[1]:"");
625
- }
626
- $this->_browser_name = $this->BROWSER_OPERA;
627
- return true;
628
- }
629
- return false;
630
- }
631
-
632
- /**
633
- * Determine if the browser is Chrome or not (last updated 1.7)
634
- * @return boolean True if the browser is Chrome otherwise false
635
- */
636
- function checkBrowserChrome() {
637
- if( stripos($this->_agent,'Chrome') !== false ) {
638
- $aresult = explode('/',stristr($this->_agent,'Chrome'));
639
- $aversion = explode(' ',$aresult[1]);
640
- $this->setVersion($aversion[0]);
641
- $this->setBrowser($this->BROWSER_CHROME);
642
- return true;
643
- }
644
- return false;
645
- }
646
-
647
-
648
- /**
649
- * Determine if the browser is WebTv or not (last updated 1.7)
650
- * @return boolean True if the browser is WebTv otherwise false
651
- */
652
- function checkBrowserWebTv() {
653
- if( stripos($this->_agent,'webtv') !== false ) {
654
- $aresult = explode('/',stristr($this->_agent,'webtv'));
655
- $aversion = explode(' ',$aresult[1]);
656
- $this->setVersion($aversion[0]);
657
- $this->setBrowser($this->BROWSER_WEBTV);
658
- return true;
659
- }
660
- return false;
661
- }
662
-
663
- /**
664
- * Determine if the browser is NetPositive or not (last updated 1.7)
665
- * @return boolean True if the browser is NetPositive otherwise false
666
- */
667
- function checkBrowserNetPositive() {
668
- if( stripos($this->_agent,'NetPositive') !== false ) {
669
- $aresult = explode('/',stristr($this->_agent,'NetPositive'));
670
- $aversion = explode(' ',$aresult[1]);
671
- $this->setVersion(str_replace(array('(',')',';'),'',$aversion[0]));
672
- $this->setBrowser($this->BROWSER_NETPOSITIVE);
673
- return true;
674
- }
675
- return false;
676
- }
677
-
678
- /**
679
- * Determine if the browser is Galeon or not (last updated 1.7)
680
- * @return boolean True if the browser is Galeon otherwise false
681
- */
682
- function checkBrowserGaleon() {
683
- if( stripos($this->_agent,'galeon') !== false ) {
684
- $aresult = explode(' ',stristr($this->_agent,'galeon'));
685
- $aversion = explode('/',$aresult[0]);
686
- $this->setVersion($aversion[1]);
687
- $this->setBrowser($this->BROWSER_GALEON);
688
- return true;
689
- }
690
- return false;
691
- }
692
-
693
- /**
694
- * Determine if the browser is Konqueror or not (last updated 1.7)
695
- * @return boolean True if the browser is Konqueror otherwise false
696
- */
697
- function checkBrowserKonqueror() {
698
- if( stripos($this->_agent,'Konqueror') !== false ) {
699
- $aresult = explode(' ',stristr($this->_agent,'Konqueror'));
700
- $aversion = explode('/',$aresult[0]);
701
- $this->setVersion($aversion[1]);
702
- $this->setBrowser($this->BROWSER_KONQUEROR);
703
- return true;
704
- }
705
- return false;
706
- }
707
-
708
- /**
709
- * Determine if the browser is iCab or not (last updated 1.7)
710
- * @return boolean True if the browser is iCab otherwise false
711
- */
712
- function checkBrowserIcab() {
713
- if( stripos($this->_agent,'icab') !== false ) {
714
- $aversion = explode(' ',stristr(str_replace('/',' ',$this->_agent),'icab'));
715
- $this->setVersion($aversion[1]);
716
- $this->setBrowser($this->BROWSER_ICAB);
717
- return true;
718
- }
719
- return false;
720
- }
721
-
722
- /**
723
- * Determine if the browser is OmniWeb or not (last updated 1.7)
724
- * @return boolean True if the browser is OmniWeb otherwise false
725
- */
726
- function checkBrowserOmniWeb() {
727
- if( stripos($this->_agent,'omniweb') !== false ) {
728
- $aresult = explode('/',stristr($this->_agent,'omniweb'));
729
- $aversion = explode(' ',isset($aresult[1])?$aresult[1]:"");
730
- $this->setVersion($aversion[0]);
731
- $this->setBrowser($this->BROWSER_OMNIWEB);
732
- return true;
733
- }
734
- return false;
735
- }
736
-
737
- /**
738
- * Determine if the browser is Phoenix or not (last updated 1.7)
739
- * @return boolean True if the browser is Phoenix otherwise false
740
- */
741
- function checkBrowserPhoenix() {
742
- if( stripos($this->_agent,'Phoenix') !== false ) {
743
- $aversion = explode('/',stristr($this->_agent,'Phoenix'));
744
- $this->setVersion($aversion[1]);
745
- $this->setBrowser($this->BROWSER_PHOENIX);
746
- return true;
747
- }
748
- return false;
749
- }
750
-
751
- /**
752
- * Determine if the browser is Firebird or not (last updated 1.7)
753
- * @return boolean True if the browser is Firebird otherwise false
754
- */
755
- function checkBrowserFirebird() {
756
- if( stripos($this->_agent,'Firebird') !== false ) {
757
- $aversion = explode('/',stristr($this->_agent,'Firebird'));
758
- $this->setVersion($aversion[1]);
759
- $this->setBrowser($this->BROWSER_FIREBIRD);
760
- return true;
761
- }
762
- return false;
763
- }
764
-
765
- /**
766
- * Determine if the browser is Netscape Navigator 9+ or not (last updated 1.7)
767
- * NOTE: (http://browser.netscape.com/ - Official support ended on March 1st, 2008)
768
- * @return boolean True if the browser is Netscape Navigator 9+ otherwise false
769
- */
770
- function checkBrowserNetscapeNavigator9Plus() {
771
- if( stripos($this->_agent,'Firefox') !== false && preg_match('/Navigator\/([^ ]*)/i',$this->_agent,$matches) ) {
772
- $this->setVersion($matches[1]);
773
- $this->setBrowser($this->BROWSER_NETSCAPE_NAVIGATOR);
774
- return true;
775
- }
776
- else if( stripos($this->_agent,'Firefox') === false && preg_match('/Netscape6?\/([^ ]*)/i',$this->_agent,$matches) ) {
777
- $this->setVersion($matches[1]);
778
- $this->setBrowser($this->BROWSER_NETSCAPE_NAVIGATOR);
779
- return true;
780
- }
781
- return false;
782
- }
783
-
784
- /**
785
- * Determine if the browser is Shiretoko or not (https://wiki.mozilla.org/Projects/shiretoko) (last updated 1.7)
786
- * @return boolean True if the browser is Shiretoko otherwise false
787
- */
788
- function checkBrowserShiretoko() {
789
- if( stripos($this->_agent,'Mozilla') !== false && preg_match('/Shiretoko\/([^ ]*)/i',$this->_agent,$matches) ) {
790
- $this->setVersion($matches[1]);
791
- $this->setBrowser($this->BROWSER_SHIRETOKO);
792
- return true;
793
- }
794
- return false;
795
- }
796
-
797
- /**
798
- * Determine if the browser is Ice Cat or not (http://en.wikipedia.org/wiki/GNU_IceCat) (last updated 1.7)
799
- * @return boolean True if the browser is Ice Cat otherwise false
800
- */
801
- function checkBrowserIceCat() {
802
- if( stripos($this->_agent,'Mozilla') !== false && preg_match('/IceCat\/([^ ]*)/i',$this->_agent,$matches) ) {
803
- $this->setVersion($matches[1]);
804
- $this->setBrowser($this->BROWSER_ICECAT);
805
- return true;
806
- }
807
- return false;
808
- }
809
-
810
- /**
811
- * Determine if the browser is Nokia or not (last updated 1.7)
812
- * @return boolean True if the browser is Nokia otherwise false
813
- */
814
- function checkBrowserNokia() {
815
- if( preg_match("/Nokia([^\/]+)\/([^ SP]+)/i",$this->_agent,$matches) ) {
816
- $this->setVersion($matches[2]);
817
- if( stripos($this->_agent,'Series60') !== false || strpos($this->_agent,'S60') !== false ) {
818
- $this->setBrowser($this->BROWSER_NOKIA_S60);
819
- }
820
- else {
821
- $this->setBrowser( $this->BROWSER_NOKIA );
822
- }
823
- $this->setMobile(true);
824
- return true;
825
- }
826
- return false;
827
- }
828
-
829
- /**
830
- * Determine if the browser is Firefox or not (last updated 1.7)
831
- * @return boolean True if the browser is Firefox otherwise false
832
- */
833
- function checkBrowserFirefox() {
834
- if( stripos($this->_agent,'safari') === false ) {
835
- if( preg_match("/Firefox[\/ \(]([^ ;\)]+)/i",$this->_agent,$matches) ) {
836
- $this->setVersion($matches[1]);
837
- $this->setBrowser($this->BROWSER_FIREFOX);
838
- return true;
839
- }
840
- else if( preg_match("/Firefox$/i",$this->_agent,$matches) ) {
841
- $this->setVersion("");
842
- $this->setBrowser($this->BROWSER_FIREFOX);
843
- return true;
844
- }
845
- }
846
- return false;
847
- }
848
-
849
- /**
850
- * Determine if the browser is Firefox or not (last updated 1.7)
851
- * @return boolean True if the browser is Firefox otherwise false
852
- */
853
- function checkBrowserIceweasel() {
854
- if( stripos($this->_agent,'Iceweasel') !== false ) {
855
- $aresult = explode('/',stristr($this->_agent,'Iceweasel'));
856
- $aversion = explode(' ',$aresult[1]);
857
- $this->setVersion($aversion[0]);
858
- $this->setBrowser($this->BROWSER_ICEWEASEL);
859
- return true;
860
- }
861
- return false;
862
- }
863
- /**
864
- * Determine if the browser is Mozilla or not (last updated 1.7)
865
- * @return boolean True if the browser is Mozilla otherwise false
866
- */
867
- function checkBrowserMozilla() {
868
- if( stripos($this->_agent,'mozilla') !== false && preg_match('/rv:[0-9].[0-9][a-b]?/i',$this->_agent) && stripos($this->_agent,'netscape') === false) {
869
- $aversion = explode(' ',stristr($this->_agent,'rv:'));
870
- preg_match('/rv:[0-9].[0-9][a-b]?/i',$this->_agent,$aversion);
871
- $this->setVersion(str_replace('rv:','',$aversion[0]));
872
- $this->setBrowser($this->BROWSER_MOZILLA);
873
- return true;
874
- }
875
- else if( stripos($this->_agent,'mozilla') !== false && preg_match('/rv:[0-9]\.[0-9]/i',$this->_agent) && stripos($this->_agent,'netscape') === false ) {
876
- $aversion = explode('',stristr($this->_agent,'rv:'));
877
- $this->setVersion(str_replace('rv:','',$aversion[0]));
878
- $this->setBrowser($this->BROWSER_MOZILLA);
879
- return true;
880
- }
881
- else if( stripos($this->_agent,'mozilla') !== false && preg_match('/mozilla\/([^ ]*)/i',$this->_agent,$matches) && stripos($this->_agent,'netscape') === false ) {
882
- $this->setVersion($matches[1]);
883
- $this->setBrowser($this->BROWSER_MOZILLA);
884
- return true;
885
- }
886
- return false;
887
- }
888
-
889
- /**
890
- * Determine if the browser is Lynx or not (last updated 1.7)
891
- * @return boolean True if the browser is Lynx otherwise false
892
- */
893
- function checkBrowserLynx() {
894
- if( stripos($this->_agent,'lynx') !== false ) {
895
- $aresult = explode('/',stristr($this->_agent,'Lynx'));
896
- $aversion = explode(' ',(isset($aresult[1])?$aresult[1]:""));
897
- $this->setVersion($aversion[0]);
898
- $this->setBrowser($this->BROWSER_LYNX);
899
- return true;
900
- }
901
- return false;
902
- }
903
-
904
- /**
905
- * Determine if the browser is Amaya or not (last updated 1.7)
906
- * @return boolean True if the browser is Amaya otherwise false
907
- */
908
- function checkBrowserAmaya() {
909
- if( stripos($this->_agent,'amaya') !== false ) {
910
- $aresult = explode('/',stristr($this->_agent,'Amaya'));
911
- $aversion = explode(' ',$aresult[1]);
912
- $this->setVersion($aversion[0]);
913
- $this->setBrowser($this->BROWSER_AMAYA);
914
- return true;
915
- }
916
- return false;
917
- }
918
-
919
- /**
920
- * Determine if the browser is Safari or not (last updated 1.7)
921
- * @return boolean True if the browser is Safari otherwise false
922
- */
923
- function checkBrowserSafari() {
924
- if( stripos($this->_agent,'Safari') !== false && stripos($this->_agent,'iPhone') === false && stripos($this->_agent,'iPod') === false ) {
925
- $aresult = explode('/',stristr($this->_agent,'Version'));
926
- if( isset($aresult[1]) ) {
927
- $aversion = explode(' ',$aresult[1]);
928
- $this->setVersion($aversion[0]);
929
- }
930
- else {
931
- $this->setVersion($this->VERSION_UNKNOWN);
932
- }
933
- $this->setBrowser($this->BROWSER_SAFARI);
934
- return true;
935
- }
936
- return false;
937
- }
938
-
939
- /**
940
- * Determine if the browser is iPhone or not (last updated 1.7)
941
- * @return boolean True if the browser is iPhone otherwise false
942
- */
943
- function checkBrowseriPhone() {
944
- if( stripos($this->_agent,'iPhone') !== false ) {
945
- $aresult = explode('/',stristr($this->_agent,'Version'));
946
- if( isset($aresult[1]) ) {
947
- $aversion = explode(' ',$aresult[1]);
948
- $this->setVersion($aversion[0]);
949
- }
950
- else {
951
- $this->setVersion($this->VERSION_UNKNOWN);
952
- }
953
- $this->setMobile(true);
954
- $this->setBrowser($this->BROWSER_IPHONE);
955
- return true;
956
- }
957
- return false;
958
- }
959
-
960
- /**
961
- * Determine if the browser is iPod or not (last updated 1.7)
962
- * @return boolean True if the browser is iPod otherwise false
963
- */
964
- function checkBrowseriPad() {
965
- if( stripos($this->_agent,'iPad') !== false ) {
966
- $aresult = explode('/',stristr($this->_agent,'Version'));
967
- if( isset($aresult[1]) ) {
968
- $aversion = explode(' ',$aresult[1]);
969
- $this->setVersion($aversion[0]);
970
- }
971
- else {
972
- $this->setVersion($this->VERSION_UNKNOWN);
973
- }
974
- $this->setMobile(true);
975
- $this->setBrowser($this->BROWSER_IPAD);
976
- return true;
977
- }
978
- return false;
979
- }
980
-
981
- /**
982
- * Determine if the browser is iPod or not (last updated 1.7)
983
- * @return boolean True if the browser is iPod otherwise false
984
- */
985
- function checkBrowseriPod() {
986
- if( stripos($this->_agent,'iPod') !== false ) {
987
- $aresult = explode('/',stristr($this->_agent,'Version'));
988
- if( isset($aresult[1]) ) {
989
- $aversion = explode(' ',$aresult[1]);
990
- $this->setVersion($aversion[0]);
991
- }
992
- else {
993
- $this->setVersion($this->VERSION_UNKNOWN);
994
- }
995
- $this->setMobile(true);
996
- $this->setBrowser($this->BROWSER_IPOD);
997
- return true;
998
- }
999
- return false;
1000
- }
1001
-
1002
- /**
1003
- * Determine if the browser is Android or not (last updated 1.7)
1004
- * @return boolean True if the browser is Android otherwise false
1005
- */
1006
- function checkBrowserAndroid() {
1007
- if( stripos($this->_agent,'Android') !== false ) {
1008
- $aresult = explode(' ',stristr($this->_agent,'Android'));
1009
- if( isset($aresult[1]) ) {
1010
- $aversion = explode(' ',$aresult[1]);
1011
- $this->setVersion($aversion[0]);
1012
- }
1013
- else {
1014
- $this->setVersion($this->VERSION_UNKNOWN);
1015
- }
1016
- $this->setMobile(true);
1017
- $this->setBrowser($this->BROWSER_ANDROID);
1018
- return true;
1019
- }
1020
- return false;
1021
- }
1022
-
1023
- /**
1024
- * Determine the user's platform (last updated 1.7)
1025
- */
1026
- function checkPlatform() {
1027
- if( stripos($this->_agent, 'windows') !== false ) {
1028
- $this->_platform = $this->PLATFORM_WINDOWS;
1029
- }
1030
- else if( stripos($this->_agent, 'iPad') !== false ) {
1031
- $this->_platform = $this->PLATFORM_IPAD;
1032
- }
1033
- else if( stripos($this->_agent, 'iPod') !== false ) {
1034
- $this->_platform = $this->PLATFORM_IPOD;
1035
- }
1036
- else if( stripos($this->_agent, 'iPhone') !== false ) {
1037
- $this->_platform = $this->PLATFORM_IPHONE;
1038
- }
1039
- elseif( stripos($this->_agent, 'mac') !== false ) {
1040
- $this->_platform = $this->PLATFORM_APPLE;
1041
- }
1042
- elseif( stripos($this->_agent, 'android') !== false ) {
1043
- $this->_platform = $this->PLATFORM_ANDROID;
1044
- }
1045
- elseif( stripos($this->_agent, 'linux') !== false ) {
1046
- $this->_platform = $this->PLATFORM_LINUX;
1047
- }
1048
- else if( stripos($this->_agent, 'Nokia') !== false ) {
1049
- $this->_platform = $this->PLATFORM_NOKIA;
1050
- }
1051
- else if( stripos($this->_agent, 'BlackBerry') !== false ) {
1052
- $this->_platform = $this->PLATFORM_BLACKBERRY;
1053
- }
1054
- elseif( stripos($this->_agent,'FreeBSD') !== false ) {
1055
- $this->_platform = $this->PLATFORM_FREEBSD;
1056
- }
1057
- elseif( stripos($this->_agent,'OpenBSD') !== false ) {
1058
- $this->_platform = $this->PLATFORM_OPENBSD;
1059
- }
1060
- elseif( stripos($this->_agent,'NetBSD') !== false ) {
1061
- $this->_platform = $this->PLATFORM_NETBSD;
1062
- }
1063
- elseif( stripos($this->_agent, 'OpenSolaris') !== false ) {
1064
- $this->_platform = $this->PLATFORM_OPENSOLARIS;
1065
- }
1066
- elseif( stripos($this->_agent, 'SunOS') !== false ) {
1067
- $this->_platform = $this->PLATFORM_SUNOS;
1068
- }
1069
- elseif( stripos($this->_agent, 'OS\/2') !== false ) {
1070
- $this->_platform = $this->PLATFORM_OS2;
1071
- }
1072
- elseif( stripos($this->_agent, 'BeOS') !== false ) {
1073
- $this->_platform = $this->PLATFORM_BEOS;
1074
- }
1075
- elseif( stripos($this->_agent, 'win') !== false ) {
1076
- $this->_platform = $this->PLATFORM_WINDOWS;
1077
- }
1078
-
1079
- }
1080
- }
1081
-
1082
- ?>
1
+ <?php
2
+ /**
3
+ * Modified to remove var
4
+ * Chris Christoff on 12/26/2012
5
+ * Changes: Changes vars to publics
6
+ *
7
+ * Modified to work for EDD by
8
+ * Chris Christoff on 12/23/2012
9
+ * Changes: Removed the browser string return and added spacing. Also removed return HTML formatting.
10
+ *
11
+ * Modified to add formatted User Agent string for EDD System Info by
12
+ * Chris Christoff on 12/23/2012
13
+ * Changes: Split user string and add formatting so we can print a nicely
14
+ * formatted user agent string on the EDD System Info
15
+ *
16
+ * File: Browser.php
17
+ * Author: Chris Schuld (http://chrisschuld.com/)
18
+ * Last Modified: August 20th, 2010
19
+ * @version 1.9
20
+ * @package PegasusPHP
21
+ *
22
+ * Copyright (C) 2008-2010 Chris Schuld (chris@chrisschuld.com)
23
+ *
24
+ * This program is free software; you can redistribute it and/or
25
+ * modify it under the terms of the GNU General Public License as
26
+ * published by the Free Software Foundation; either version 2 of
27
+ * the License, or (at your option) any later version.
28
+ *
29
+ * This program is distributed in the hope that it will be useful,
30
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
+ * GNU General Public License for more details at:
33
+ * http://www.gnu.org/copyleft/gpl.html
34
+ *
35
+ *
36
+ * Typical Usage:
37
+ *
38
+ * $browser = new Browser();
39
+ * if( $browser->getBrowser() == Browser::BROWSER_FIREFOX && $browser->getVersion() >= 2 ) {
40
+ * echo 'You have FireFox version 2 or greater';
41
+ * }
42
+ *
43
+ * User Agents Sampled from: http://www.useragentstring.com/
44
+ *
45
+ * This implementation is based on the original work from Gary White
46
+ * http://apptools.com/phptools/browser/
47
+ *
48
+ * UPDATES:
49
+ *
50
+ * 2010-08-20 (v1.9):
51
+ * + Added MSN Explorer Browser (legacy)
52
+ * + Added Bing/MSN Robot (Thanks Rob MacDonald)
53
+ * + Added the Android Platform (PLATFORM_ANDROID)
54
+ * + Fixed issue with Android 1.6/2.2 (Thanks Tom Hirashima)
55
+ *
56
+ * 2010-04-27 (v1.8):
57
+ * + Added iPad Support
58
+ *
59
+ * 2010-03-07 (v1.7):
60
+ * + *MAJOR* Rebuild (preg_match and other "slow" routine removal(s))
61
+ * + Almost allof Gary's original code has been replaced
62
+ * + Large PHPUNIT testing environment created to validate new releases and additions
63
+ * + Added FreeBSD Platform
64
+ * + Added OpenBSD Platform
65
+ * + Added NetBSD Platform
66
+ * + Added SunOS Platform
67
+ * + Added OpenSolaris Platform
68
+ * + Added support of the Iceweazel Browser
69
+ * + Added isChromeFrame() call to check if chromeframe is in use
70
+ * + Moved the Opera check in front of the Firefox check due to legacy Opera User Agents
71
+ * + Added the __toString() method (Thanks Deano)
72
+ *
73
+ * 2009-11-15:
74
+ * + Updated the checkes for Firefox
75
+ * + Added the NOKIA platform
76
+ * + Added Checks for the NOKIA brower(s)
77
+ *
78
+ * 2009-11-08:
79
+ * + PHP 5.3 Support
80
+ * + Added support for BlackBerry OS and BlackBerry browser
81
+ * + Added support for the Opera Mini browser
82
+ * + Added additional documenation
83
+ * + Added support for isRobot() and isMobile()
84
+ * + Added support for Opera version 10
85
+ * + Added support for deprecated Netscape Navigator version 9
86
+ * + Added support for IceCat
87
+ * + Added support for Shiretoko
88
+ *
89
+ * 2010-04-27 (v1.8):
90
+ * + Added iPad Support
91
+ *
92
+ * 2009-08-18:
93
+ * + Updated to support PHP 5.3 - removed all deprecated function calls
94
+ * + Updated to remove all double quotes (") -- converted to single quotes (')
95
+ *
96
+ * 2009-04-27:
97
+ * + Updated the IE check to remove a typo and bug (thanks John)
98
+ *
99
+ * 2009-04-22:
100
+ * + Added detection for GoogleBot
101
+ * + Added detection for the W3C Validator.
102
+ * + Added detection for Yahoo! Slurp
103
+ *
104
+ * 2009-03-14:
105
+ * + Added detection for iPods.
106
+ * + Added Platform detection for iPhones
107
+ * + Added Platform detection for iPods
108
+ *
109
+ * 2009-02-16: (Rick Hale)
110
+ * + Added version detection for Android phones.
111
+ *
112
+ * 2008-12-09:
113
+ * + Removed unused constant
114
+ *
115
+ * 2008-11-07:
116
+ * + Added Google's Chrome to the detection list
117
+ * + Added isBrowser(string) to the list of functions special thanks to
118
+ * Daniel 'mavrick' Lang for the function concept (http://mavrick.id.au)
119
+ *
120
+ *
121
+ * Gary White noted: "Since browser detection is so unreliable, I am
122
+ * no longer maintaining this script. You are free to use and or
123
+ * modify/update it as you want, however the author assumes no
124
+ * responsibility for the accuracy of the detected values."
125
+ *
126
+ * Anyone experienced with Gary's script might be interested in these notes:
127
+ *
128
+ * Added class constants
129
+ * Added detection and version detection for Google's Chrome
130
+ * Updated the version detection for Amaya
131
+ * Updated the version detection for Firefox
132
+ * Updated the version detection for Lynx
133
+ * Updated the version detection for WebTV
134
+ * Updated the version detection for NetPositive
135
+ * Updated the version detection for IE
136
+ * Updated the version detection for OmniWeb
137
+ * Updated the version detection for iCab
138
+ * Updated the version detection for Safari
139
+ * Updated Safari to remove mobile devices (iPhone)
140
+ * Added detection for iPhone
141
+ * Added detection for robots
142
+ * Added detection for mobile devices
143
+ * Added detection for BlackBerry
144
+ * Removed Netscape checks (matches heavily with firefox & mozilla)
145
+ *
146
+ */
147
+
148
+ class Browser {
149
+ public $_agent = '';
150
+ public $_browser_name = '';
151
+ public $_version = '';
152
+ public $_platform = '';
153
+ public $_os = '';
154
+ public $_is_aol = false;
155
+ public $_is_mobile = false;
156
+ public $_is_robot = false;
157
+ public $_aol_version = '';
158
+
159
+ public $BROWSER_UNKNOWN = 'unknown';
160
+ public $VERSION_UNKNOWN = 'unknown';
161
+
162
+ public $BROWSER_OPERA = 'Opera'; // Http://www.opera.com/
163
+ public $BROWSER_OPERA_MINI = 'Opera Mini'; // Http://www.opera.com/mini/
164
+ public $BROWSER_WEBTV = 'WebTV'; // Http://www.webtv.net/pc/
165
+ public $BROWSER_IE = 'Internet Explorer'; // Http://www.microsoft.com/ie/
166
+ public $BROWSER_POCKET_IE = 'Pocket Internet Explorer'; // Http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
167
+ public $BROWSER_KONQUEROR = 'Konqueror'; // Http://www.konqueror.org/
168
+ public $BROWSER_ICAB = 'iCab'; // Http://www.icab.de/
169
+ public $BROWSER_OMNIWEB = 'OmniWeb'; // Http://www.omnigroup.com/applications/omniweb/
170
+ public $BROWSER_FIREBIRD = 'Firebird'; // Http://www.ibphoenix.com/
171
+ public $BROWSER_FIREFOX = 'Firefox'; // Http://www.mozilla.com/en-US/firefox/firefox.html
172
+ public $BROWSER_ICEWEASEL = 'Iceweasel'; // Http://www.geticeweasel.org/
173
+ public $BROWSER_SHIRETOKO = 'Shiretoko'; // Http://wiki.mozilla.org/Projects/shiretoko
174
+ public $BROWSER_MOZILLA = 'Mozilla'; // Http://www.mozilla.com/en-US/
175
+ public $BROWSER_AMAYA = 'Amaya'; // Http://www.w3.org/Amaya/
176
+ public $BROWSER_LYNX = 'Lynx'; // Http://en.wikipedia.org/wiki/Lynx
177
+ public $BROWSER_SAFARI = 'Safari'; // Http://apple.com
178
+ public $BROWSER_IPHONE = 'iPhone'; // Http://apple.com
179
+ public $BROWSER_IPOD = 'iPod'; // Http://apple.com
180
+ public $BROWSER_IPAD = 'iPad'; // Http://apple.com
181
+ public $BROWSER_CHROME = 'Chrome'; // Http://www.google.com/chrome
182
+ public $BROWSER_ANDROID = 'Android'; // Http://www.android.com/
183
+ public $BROWSER_GOOGLEBOT = 'GoogleBot'; // Http://en.wikipedia.org/wiki/Googlebot
184
+ public $BROWSER_SLURP = 'Yahoo! Slurp'; // Http://en.wikipedia.org/wiki/Yahoo!_Slurp
185
+ public $BROWSER_W3CVALIDATOR = 'W3C Validator'; // Http://validator.w3.org/
186
+ public $BROWSER_BLACKBERRY = 'BlackBerry'; // Http://www.blackberry.com/
187
+ public $BROWSER_ICECAT = 'IceCat'; // Http://en.wikipedia.org/wiki/GNU_IceCat
188
+ public $BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser'; // Http://en.wikipedia.org/wiki/Web_Browser_for_S60
189
+ public $BROWSER_NOKIA = 'Nokia Browser'; // * all other WAP-based browsers on the Nokia Platform
190
+ public $BROWSER_MSN = 'MSN Browser'; // Http://explorer.msn.com/
191
+ public $BROWSER_MSNBOT = 'MSN Bot'; // Http://search.msn.com/msnbot.htm
192
+ // Http://en.wikipedia.org/wiki/Msnbot (used for Bing as well)
193
+
194
+ public $BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator'; // Http://browser.netscape.com/ (DEPRECATED)
195
+ public $BROWSER_GALEON = 'Galeon'; // Http://galeon.sourceforge.net/ (DEPRECATED)
196
+ public $BROWSER_NETPOSITIVE = 'NetPositive'; // Http://en.wikipedia.org/wiki/NetPositive (DEPRECATED)
197
+ public $BROWSER_PHOENIX = 'Phoenix'; // Http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox (DEPRECATED)
198
+
199
+ public $PLATFORM_UNKNOWN = 'unknown';
200
+ public $PLATFORM_WINDOWS = 'Windows';
201
+ public $PLATFORM_WINDOWS_CE = 'Windows CE';
202
+ public $PLATFORM_APPLE = 'Apple';
203
+ public $PLATFORM_LINUX = 'Linux';
204
+ public $PLATFORM_OS2 = 'OS/2';
205
+ public $PLATFORM_BEOS = 'BeOS';
206
+ public $PLATFORM_IPHONE = 'iPhone';
207
+ public $PLATFORM_IPOD = 'iPod';
208
+ public $PLATFORM_IPAD = 'iPad';
209
+ public $PLATFORM_BLACKBERRY = 'BlackBerry';
210
+ public $PLATFORM_NOKIA = 'Nokia';
211
+ public $PLATFORM_FREEBSD = 'FreeBSD';
212
+ public $PLATFORM_OPENBSD = 'OpenBSD';
213
+ public $PLATFORM_NETBSD = 'NetBSD';
214
+ public $PLATFORM_SUNOS = 'SunOS';
215
+ public $PLATFORM_OPENSOLARIS = 'OpenSolaris';
216
+ public $PLATFORM_ANDROID = 'Android';
217
+
218
+ public $OPERATING_SYSTEM_UNKNOWN = 'unknown';
219
+
220
+ function Browser($useragent="") {
221
+ $this->reset();
222
+ if( $useragent != "" ) {
223
+ $this->setUserAgent($useragent);
224
+ }
225
+ else {
226
+ $this->determine();
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Reset all properties
232
+ */
233
+ function reset() {
234
+ $this->_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "";
235
+ $this->_browser_name = $this->BROWSER_UNKNOWN;
236
+ $this->_version = $this->VERSION_UNKNOWN;
237
+ $this->_platform = $this->PLATFORM_UNKNOWN;
238
+ $this->_os = $this->OPERATING_SYSTEM_UNKNOWN;
239
+ $this->_is_aol = false;
240
+ $this->_is_mobile = false;
241
+ $this->_is_robot = false;
242
+ $this->_aol_version = $this->VERSION_UNKNOWN;
243
+ }
244
+
245
+ /**
246
+ * Check to see if the specific browser is valid
247
+ * @param string $browserName
248
+ * @return True if the browser is the specified browser
249
+ */
250
+ function isBrowser($browserName) { return( 0 == strcasecmp($this->_browser_name, trim($browserName))); }
251
+
252
+ /**
253
+ * The name of the browser. All return types are from the class contants
254
+ * @return string Name of the browser
255
+ */
256
+ function getBrowser() { return $this->_browser_name; }
257
+ /**
258
+ * Set the name of the browser
259
+ * @param $browser The name of the Browser
260
+ */
261
+ function setBrowser($browser) { return $this->_browser_name = $browser; }
262
+ /**
263
+ * The name of the platform. All return types are from the class contants
264
+ * @return string Name of the browser
265
+ */
266
+ function getPlatform() { return $this->_platform; }
267
+ /**
268
+ * Set the name of the platform
269
+ * @param $platform The name of the Platform
270
+ */
271
+ function setPlatform($platform) { return $this->_platform = $platform; }
272
+ /**
273
+ * The version of the browser.
274
+ * @return string Version of the browser (will only contain alpha-numeric characters and a period)
275
+ */
276
+ function getVersion() { return $this->_version; }
277
+ /**
278
+ * Set the version of the browser
279
+ * @param $version The version of the Browser
280
+ */
281
+ function setVersion($version) { $this->_version = preg_replace('/[^0-9,.,a-z,A-Z-]/','',$version); }
282
+ /**
283
+ * The version of AOL.
284
+ * @return string Version of AOL (will only contain alpha-numeric characters and a period)
285
+ */
286
+ function getAolVersion() { return $this->_aol_version; }
287
+ /**
288
+ * Set the version of AOL
289
+ * @param $version The version of AOL
290
+ */
291
+ function setAolVersion($version) { $this->_aol_version = preg_replace('/[^0-9,.,a-z,A-Z]/','',$version); }
292
+ /**
293
+ * Is the browser from AOL?
294
+ * @return boolean True if the browser is from AOL otherwise false
295
+ */
296
+ function isAol() { return $this->_is_aol; }
297
+ /**
298
+ * Is the browser from a mobile device?
299
+ * @return boolean True if the browser is from a mobile device otherwise false
300
+ */
301
+ function isMobile() { return $this->_is_mobile; }
302
+ /**
303
+ * Is the browser from a robot (ex Slurp,GoogleBot)?
304
+ * @return boolean True if the browser is from a robot otherwise false
305
+ */
306
+ function isRobot() { return $this->_is_robot; }
307
+ /**
308
+ * Set the browser to be from AOL
309
+ * @param $isAol
310
+ */
311
+ function setAol($isAol) { $this->_is_aol = $isAol; }
312
+ /**
313
+ * Set the Browser to be mobile
314
+ * @param boolean $value is the browser a mobile brower or not
315
+ */
316
+ function setMobile($value=true) { $this->_is_mobile = $value; }
317
+ /**
318
+ * Set the Browser to be a robot
319
+ * @param boolean $value is the browser a robot or not
320
+ */
321
+ function setRobot($value=true) { $this->_is_robot = $value; }
322
+ /**
323
+ * Get the user agent value in use to determine the browser
324
+ * @return string The user agent from the HTTP header
325
+ */
326
+ function getUserAgent() { return $this->_agent; }
327
+ /**
328
+ * Set the user agent value (the construction will use the HTTP header value - this will overwrite it)
329
+ * @param $agent_string The value for the User Agent
330
+ */
331
+ function setUserAgent($agent_string) {
332
+ $this->reset();
333
+ $this->_agent = $agent_string;
334
+ $this->determine();
335
+ }
336
+ /**
337
+ * Used to determine if the browser is actually "chromeframe"
338
+ * @since 1.7
339
+ * @return boolean True if the browser is using chromeframe
340
+ */
341
+ function isChromeFrame() {
342
+ return( strpos($this->_agent,"chromeframe") !== false );
343
+ }
344
+ /**
345
+ * Returns a formatted string with a summary of the details of the browser.
346
+ * @return string formatted string with a summary of the browser
347
+ */
348
+ function __toString() {
349
+ $text1 = $this->getUserAgent(); //grabs the UA (user agent) string
350
+ $UAline1 = substr($text1, 0, 32); //the first line we print should only be the first 32 characters of the UA string
351
+ $text2 = $this->getUserAgent();//now we grab it again and save it to a string
352
+ $towrapUA = str_replace($UAline1, '', $text2);//the rest of the printoff (other than first line) is equivolent
353
+ // To the whole string minus the part we printed off. IE
354
+ // User Agent: thefirst32charactersfromUAline1
355
+ // the rest of it is now stored in
356
+ // $text2 to be printed off
357
+ // But we need to add spaces before each line that is split other than line 1
358
+ $space = '';
359
+ for($i = 0; $i < 25; $i++) {
360
+ $space .= ' ';
361
+ }
362
+ // Now we split the remaining string of UA ($text2) into lines that are prefixed by spaces for formatting
363
+ $wordwrapped = chunk_split($towrapUA, 32, "\n $space");
364
+ return "Platform: {$this->getPlatform()} \n".
365
+ "Browser Name: {$this->getBrowser()} \n" .
366
+ "Browser Version: {$this->getVersion()} \n" .
367
+ "User Agent String: $UAline1 \n\t\t\t " .
368
+ "$wordwrapped";
369
+ }
370
+ /**
371
+ * Protected routine to calculate and determine what the browser is in use (including platform)
372
+ */
373
+ function determine() {
374
+ $this->checkPlatform();
375
+ $this->checkBrowsers();
376
+ $this->checkForAol();
377
+ }
378
+ /**
379
+ * Protected routine to determine the browser type
380
+ * @return boolean True if the browser was detected otherwise false
381
+ */
382
+ function checkBrowsers() {
383
+ return (
384
+ // Well-known, well-used
385
+ // Special Notes:
386
+ // (1) Opera must be checked before FireFox due to the odd
387
+ // user agents used in some older versions of Opera
388
+ // (2) WebTV is strapped onto Internet Explorer so we must
389
+ // check for WebTV before IE
390
+ // (3) (deprecated) Galeon is based on Firefox and needs to be
391
+ // tested before Firefox is tested
392
+ // (4) OmniWeb is based on Safari so OmniWeb check must occur
393
+ // before Safari
394
+ // (5) Netscape 9+ is based on Firefox so Netscape checks
395
+ // before FireFox are necessary
396
+ $this->checkBrowserWebTv() ||
397
+ $this->checkBrowserInternetExplorer() ||
398
+ $this->checkBrowserOpera() ||
399
+ $this->checkBrowserGaleon() ||
400
+ $this->checkBrowserNetscapeNavigator9Plus() ||
401
+ $this->checkBrowserFirefox() ||
402
+ $this->checkBrowserChrome() ||
403
+ $this->checkBrowserOmniWeb() ||
404
+
405
+ // Common mobile
406
+ $this->checkBrowserAndroid() ||
407
+ $this->checkBrowseriPad() ||
408
+ $this->checkBrowseriPod() ||
409
+ $this->checkBrowseriPhone() ||
410
+ $this->checkBrowserBlackBerry() ||
411
+ $this->checkBrowserNokia() ||
412
+
413
+ // Common bots
414
+ $this->checkBrowserGoogleBot() ||
415
+ $this->checkBrowserMSNBot() ||
416
+ $this->checkBrowserSlurp() ||
417
+
418
+ // WebKit base check (post mobile and others)
419
+ $this->checkBrowserSafari() ||
420
+
421
+ // Everyone else
422
+ $this->checkBrowserNetPositive() ||
423
+ $this->checkBrowserFirebird() ||
424
+ $this->checkBrowserKonqueror() ||
425
+ $this->checkBrowserIcab() ||
426
+ $this->checkBrowserPhoenix() ||
427
+ $this->checkBrowserAmaya() ||
428
+ $this->checkBrowserLynx() ||
429
+
430
+ $this->checkBrowserShiretoko() ||
431
+ $this->checkBrowserIceCat() ||
432
+ $this->checkBrowserW3CValidator() ||
433
+ $this->checkBrowserMozilla() /* Mozilla is such an open standard that you must check it last */
434
+ );
435
+ }
436
+
437
+ /**
438
+ * Determine if the user is using a BlackBerry (last updated 1.7)
439
+ * @return boolean True if the browser is the BlackBerry browser otherwise false
440
+ */
441
+ function checkBrowserBlackBerry() {
442
+ if( stripos($this->_agent,'blackberry') !== false ) {
443
+ $aresult = explode("/",stristr($this->_agent,"BlackBerry"));
444
+ $aversion = explode(' ',$aresult[1]);
445
+ $this->setVersion($aversion[0]);
446
+ $this->_browser_name = $this->BROWSER_BLACKBERRY;
447
+ $this->setMobile(true);
448
+ return true;
449
+ }
450
+ return false;
451
+ }
452
+
453
+ /**
454
+ * Determine if the user is using an AOL User Agent (last updated 1.7)
455
+ * @return boolean True if the browser is from AOL otherwise false
456
+ */
457
+ function checkForAol() {
458
+ $this->setAol(false);
459
+ $this->setAolVersion($this->VERSION_UNKNOWN);
460
+
461
+ if( stripos($this->_agent,'aol') !== false ) {
462
+ $aversion = explode(' ',stristr($this->_agent, 'AOL'));
463
+ $this->setAol(true);
464
+ $this->setAolVersion(preg_replace('/[^0-9\.a-z]/i', '', $aversion[1]));
465
+ return true;
466
+ }
467
+ return false;
468
+ }
469
+
470
+ /**
471
+ * Determine if the browser is the GoogleBot or not (last updated 1.7)
472
+ * @return boolean True if the browser is the GoogletBot otherwise false
473
+ */
474
+ function checkBrowserGoogleBot() {
475
+ if( stripos($this->_agent,'googlebot') !== false ) {
476
+ $aresult = explode('/',stristr($this->_agent,'googlebot'));
477
+ $aversion = explode(' ',$aresult[1]);
478
+ $this->setVersion(str_replace(';','',$aversion[0]));
479
+ $this->_browser_name = $this->BROWSER_GOOGLEBOT;
480
+ $this->setRobot(true);
481
+ return true;
482
+ }
483
+ return false;
484
+ }
485
+
486
+ /**
487
+ * Determine if the browser is the MSNBot or not (last updated 1.9)
488
+ * @return boolean True if the browser is the MSNBot otherwise false
489
+ */
490
+ function checkBrowserMSNBot() {
491
+ if( stripos($this->_agent,"msnbot") !== false ) {
492
+ $aresult = explode("/",stristr($this->_agent,"msnbot"));
493
+ $aversion = explode(" ",$aresult[1]);
494
+ $this->setVersion(str_replace(";","",$aversion[0]));
495
+ $this->_browser_name = $this->BROWSER_MSNBOT;
496
+ $this->setRobot(true);
497
+ return true;
498
+ }
499
+ return false;
500
+ }
501
+
502
+ /**
503
+ * Determine if the browser is the W3C Validator or not (last updated 1.7)
504
+ * @return boolean True if the browser is the W3C Validator otherwise false
505
+ */
506
+ function checkBrowserW3CValidator() {
507
+ if( stripos($this->_agent,'W3C-checklink') !== false ) {
508
+ $aresult = explode('/',stristr($this->_agent,'W3C-checklink'));
509
+ $aversion = explode(' ',$aresult[1]);
510
+ $this->setVersion($aversion[0]);
511
+ $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
512
+ return true;
513
+ }
514
+ else if( stripos($this->_agent,'W3C_Validator') !== false ) {
515
+ // Some of the Validator versions do not delineate w/ a slash - add it back in
516
+ $ua = str_replace("W3C_Validator ", "W3C_Validator/", $this->_agent);
517
+ $aresult = explode('/',stristr($ua,'W3C_Validator'));
518
+ $aversion = explode(' ',$aresult[1]);
519
+ $this->setVersion($aversion[0]);
520
+ $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
521
+ return true;
522
+ }
523
+ return false;
524
+ }
525
+
526
+ /**
527
+ * Determine if the browser is the Yahoo! Slurp Robot or not (last updated 1.7)
528
+ * @return boolean True if the browser is the Yahoo! Slurp Robot otherwise false
529
+ */
530
+ function checkBrowserSlurp() {
531
+ if( stripos($this->_agent,'slurp') !== false ) {
532
+ $aresult = explode('/',stristr($this->_agent,'Slurp'));
533
+ $aversion = explode(' ',$aresult[1]);
534
+ $this->setVersion($aversion[0]);
535
+ $this->_browser_name = $this->BROWSER_SLURP;
536
+ $this->setRobot(true);
537
+ $this->setMobile(false);
538
+ return true;
539
+ }
540
+ return false;
541
+ }
542
+
543
+ /**
544
+ * Determine if the browser is Internet Explorer or not (last updated 1.7)
545
+ * @return boolean True if the browser is Internet Explorer otherwise false
546
+ */
547
+ function checkBrowserInternetExplorer() {
548
+
549
+ // Test for v1 - v1.5 IE
550
+ if( stripos($this->_agent,'microsoft internet explorer') !== false ) {
551
+ $this->setBrowser($this->BROWSER_IE);
552
+ $this->setVersion('1.0');
553
+ $aresult = stristr($this->_agent, '/');
554
+ if( preg_match('/308|425|426|474|0b1/i', $aresult) ) {
555
+ $this->setVersion('1.5');
556
+ }
557
+ return true;
558
+ }
559
+ // Test for versions > 1.5
560
+ else if( stripos($this->_agent,'msie') !== false && stripos($this->_agent,'opera') === false ) {
561
+ // See if the browser is the odd MSN Explorer
562
+ if( stripos($this->_agent,'msnb') !== false ) {
563
+ $aresult = explode(' ',stristr(str_replace(';','; ',$this->_agent),'MSN'));
564
+ $this->setBrowser( $this->BROWSER_MSN );
565
+ $this->setVersion(str_replace(array('(',')',';'),'',$aresult[1]));
566
+ return true;
567
+ }
568
+ $aresult = explode(' ',stristr(str_replace(';','; ',$this->_agent),'msie'));
569
+ $this->setBrowser( $this->BROWSER_IE );
570
+ $this->setVersion(str_replace(array('(',')',';'),'',$aresult[1]));
571
+ return true;
572
+ }
573
+ // Test for Pocket IE
574
+ else if( stripos($this->_agent,'mspie') !== false || stripos($this->_agent,'pocket') !== false ) {
575
+ $aresult = explode(' ',stristr($this->_agent,'mspie'));
576
+ $this->setPlatform( $this->PLATFORM_WINDOWS_CE );
577
+ $this->setBrowser( $this->BROWSER_POCKET_IE );
578
+ $this->setMobile(true);
579
+
580
+ if( stripos($this->_agent,'mspie') !== false ) {
581
+ $this->setVersion($aresult[1]);
582
+ }
583
+ else {
584
+ $aversion = explode('/',$this->_agent);
585
+ $this->setVersion($aversion[1]);
586
+ }
587
+ return true;
588
+ }
589
+ return false;
590
+ }
591
+
592
+ /**
593
+ * Determine if the browser is Opera or not (last updated 1.7)
594
+ * @return boolean True if the browser is Opera otherwise false
595
+ */
596
+ function checkBrowserOpera() {
597
+ if( stripos($this->_agent,'opera mini') !== false ) {
598
+ $resultant = stristr($this->_agent, 'opera mini');
599
+ if( preg_match('/\//',$resultant) ) {
600
+ $aresult = explode('/',$resultant);
601
+ $aversion = explode(' ',$aresult[1]);
602
+ $this->setVersion($aversion[0]);
603
+ }
604
+ else {
605
+ $aversion = explode(' ',stristr($resultant,'opera mini'));
606
+ $this->setVersion($aversion[1]);
607
+ }
608
+ $this->_browser_name = $this->BROWSER_OPERA_MINI;
609
+ $this->setMobile(true);
610
+ return true;
611
+ }
612
+ else if( stripos($this->_agent,'opera') !== false ) {
613
+ $resultant = stristr($this->_agent, 'opera');
614
+ if( preg_match('/Version\/(10.*)$/',$resultant,$matches) ) {
615
+ $this->setVersion($matches[1]);
616
+ }
617
+ else if( preg_match('/\//',$resultant) ) {
618
+ $aresult = explode('/',str_replace("("," ",$resultant));
619
+ $aversion = explode(' ',$aresult[1]);
620
+ $this->setVersion($aversion[0]);
621
+ }
622
+ else {
623
+ $aversion = explode(' ',stristr($resultant,'opera'));
624
+ $this->setVersion(isset($aversion[1])?$aversion[1]:"");
625
+ }
626
+ $this->_browser_name = $this->BROWSER_OPERA;
627
+ return true;
628
+ }
629
+ return false;
630
+ }
631
+
632
+ /**
633
+ * Determine if the browser is Chrome or not (last updated 1.7)
634
+ * @return boolean True if the browser is Chrome otherwise false
635
+ */
636
+ function checkBrowserChrome() {
637
+ if( stripos($this->_agent,'Chrome') !== false ) {
638
+ $aresult = explode('/',stristr($this->_agent,'Chrome'));
639
+ $aversion = explode(' ',$aresult[1]);
640
+ $this->setVersion($aversion[0]);
641
+ $this->setBrowser($this->BROWSER_CHROME);
642
+ return true;
643
+ }
644
+ return false;
645
+ }
646
+
647
+
648
+ /**
649
+ * Determine if the browser is WebTv or not (last updated 1.7)
650
+ * @return boolean True if the browser is WebTv otherwise false
651
+ */
652
+ function checkBrowserWebTv() {
653
+ if( stripos($this->_agent,'webtv') !== false ) {
654
+ $aresult = explode('/',stristr($this->_agent,'webtv'));
655
+ $aversion = explode(' ',$aresult[1]);
656
+ $this->setVersion($aversion[0]);
657
+ $this->setBrowser($this->BROWSER_WEBTV);
658
+ return true;
659
+ }
660
+ return false;
661
+ }
662
+
663
+ /**
664
+ * Determine if the browser is NetPositive or not (last updated 1.7)
665
+ * @return boolean True if the browser is NetPositive otherwise false
666
+ */
667
+ function checkBrowserNetPositive() {
668
+ if( stripos($this->_agent,'NetPositive') !== false ) {
669
+ $aresult = explode('/',stristr($this->_agent,'NetPositive'));
670
+ $aversion = explode(' ',$aresult[1]);
671
+ $this->setVersion(str_replace(array('(',')',';'),'',$aversion[0]));
672
+ $this->setBrowser($this->BROWSER_NETPOSITIVE);
673
+ return true;
674
+ }
675
+ return false;
676
+ }
677
+
678
+ /**
679
+ * Determine if the browser is Galeon or not (last updated 1.7)
680
+ * @return boolean True if the browser is Galeon otherwise false
681
+ */
682
+ function checkBrowserGaleon() {
683
+ if( stripos($this->_agent,'galeon') !== false ) {
684
+ $aresult = explode(' ',stristr($this->_agent,'galeon'));
685
+ $aversion = explode('/',$aresult[0]);
686
+ $this->setVersion($aversion[1]);
687
+ $this->setBrowser($this->BROWSER_GALEON);
688
+ return true;
689
+ }
690
+ return false;
691
+ }
692
+
693
+ /**
694
+ * Determine if the browser is Konqueror or not (last updated 1.7)
695
+ * @return boolean True if the browser is Konqueror otherwise false
696
+ */
697
+ function checkBrowserKonqueror() {
698
+ if( stripos($this->_agent,'Konqueror') !== false ) {
699
+ $aresult = explode(' ',stristr($this->_agent,'Konqueror'));
700
+ $aversion = explode('/',$aresult[0]);
701
+ $this->setVersion($aversion[1]);
702
+ $this->setBrowser($this->BROWSER_KONQUEROR);
703
+ return true;
704
+ }
705
+ return false;
706
+ }
707
+
708
+ /**
709
+ * Determine if the browser is iCab or not (last updated 1.7)
710
+ * @return boolean True if the browser is iCab otherwise false
711
+ */
712
+ function checkBrowserIcab() {
713
+ if( stripos($this->_agent,'icab') !== false ) {
714
+ $aversion = explode(' ',stristr(str_replace('/',' ',$this->_agent),'icab'));
715
+ $this->setVersion($aversion[1]);
716
+ $this->setBrowser($this->BROWSER_ICAB);
717
+ return true;
718
+ }
719
+ return false;
720
+ }
721
+
722
+ /**
723
+ * Determine if the browser is OmniWeb or not (last updated 1.7)
724
+ * @return boolean True if the browser is OmniWeb otherwise false
725
+ */
726
+ function checkBrowserOmniWeb() {
727
+ if( stripos($this->_agent,'omniweb') !== false ) {
728
+ $aresult = explode('/',stristr($this->_agent,'omniweb'));
729
+ $aversion = explode(' ',isset($aresult[1])?$aresult[1]:"");
730
+ $this->setVersion($aversion[0]);
731
+ $this->setBrowser($this->BROWSER_OMNIWEB);
732
+ return true;
733
+ }
734
+ return false;
735
+ }
736
+
737
+ /**
738
+ * Determine if the browser is Phoenix or not (last updated 1.7)
739
+ * @return boolean True if the browser is Phoenix otherwise false
740
+ */
741
+ function checkBrowserPhoenix() {
742
+ if( stripos($this->_agent,'Phoenix') !== false ) {
743
+ $aversion = explode('/',stristr($this->_agent,'Phoenix'));
744
+ $this->setVersion($aversion[1]);
745
+ $this->setBrowser($this->BROWSER_PHOENIX);
746
+ return true;
747
+ }
748
+ return false;
749
+ }
750
+
751
+ /**
752
+ * Determine if the browser is Firebird or not (last updated 1.7)
753
+ * @return boolean True if the browser is Firebird otherwise false
754
+ */
755
+ function checkBrowserFirebird() {
756
+ if( stripos($this->_agent,'Firebird') !== false ) {
757
+ $aversion = explode('/',stristr($this->_agent,'Firebird'));
758
+ $this->setVersion($aversion[1]);
759
+ $this->setBrowser($this->BROWSER_FIREBIRD);
760
+ return true;
761
+ }
762
+ return false;
763
+ }
764
+
765
+ /**
766
+ * Determine if the browser is Netscape Navigator 9+ or not (last updated 1.7)
767
+ * NOTE: (http://browser.netscape.com/ - Official support ended on March 1st, 2008)
768
+ * @return boolean True if the browser is Netscape Navigator 9+ otherwise false
769
+ */
770
+ function checkBrowserNetscapeNavigator9Plus() {
771
+ if( stripos($this->_agent,'Firefox') !== false && preg_match('/Navigator\/([^ ]*)/i',$this->_agent,$matches) ) {
772
+ $this->setVersion($matches[1]);
773
+ $this->setBrowser($this->BROWSER_NETSCAPE_NAVIGATOR);
774
+ return true;
775
+ }
776
+ else if( stripos($this->_agent,'Firefox') === false && preg_match('/Netscape6?\/([^ ]*)/i',$this->_agent,$matches) ) {
777
+ $this->setVersion($matches[1]);
778
+ $this->setBrowser($this->BROWSER_NETSCAPE_NAVIGATOR);
779
+ return true;
780
+ }
781
+ return false;
782
+ }
783
+
784
+ /**
785
+ * Determine if the browser is Shiretoko or not (https://wiki.mozilla.org/Projects/shiretoko) (last updated 1.7)
786
+ * @return boolean True if the browser is Shiretoko otherwise false
787
+ */
788
+ function checkBrowserShiretoko() {
789
+ if( stripos($this->_agent,'Mozilla') !== false && preg_match('/Shiretoko\/([^ ]*)/i',$this->_agent,$matches) ) {
790
+ $this->setVersion($matches[1]);
791
+ $this->setBrowser($this->BROWSER_SHIRETOKO);
792
+ return true;
793
+ }
794
+ return false;
795
+ }
796
+
797
+ /**
798
+ * Determine if the browser is Ice Cat or not (http://en.wikipedia.org/wiki/GNU_IceCat) (last updated 1.7)
799
+ * @return boolean True if the browser is Ice Cat otherwise false
800
+ */
801
+ function checkBrowserIceCat() {
802
+ if( stripos($this->_agent,'Mozilla') !== false && preg_match('/IceCat\/([^ ]*)/i',$this->_agent,$matches) ) {
803
+ $this->setVersion($matches[1]);
804
+ $this->setBrowser($this->BROWSER_ICECAT);
805
+ return true;
806
+ }
807
+ return false;
808
+ }
809
+
810
+ /**
811
+ * Determine if the browser is Nokia or not (last updated 1.7)
812
+ * @return boolean True if the browser is Nokia otherwise false
813
+ */
814
+ function checkBrowserNokia() {
815
+ if( preg_match("/Nokia([^\/]+)\/([^ SP]+)/i",$this->_agent,$matches) ) {
816
+ $this->setVersion($matches[2]);
817
+ if( stripos($this->_agent,'Series60') !== false || strpos($this->_agent,'S60') !== false ) {
818
+ $this->setBrowser($this->BROWSER_NOKIA_S60);
819
+ }
820
+ else {
821
+ $this->setBrowser( $this->BROWSER_NOKIA );
822
+ }
823
+ $this->setMobile(true);
824
+ return true;
825
+ }
826
+ return false;
827
+ }
828
+
829
+ /**
830
+ * Determine if the browser is Firefox or not (last updated 1.7)
831
+ * @return boolean True if the browser is Firefox otherwise false
832
+ */
833
+ function checkBrowserFirefox() {
834
+ if( stripos($this->_agent,'safari') === false ) {
835
+ if( preg_match("/Firefox[\/ \(]([^ ;\)]+)/i",$this->_agent,$matches) ) {
836
+ $this->setVersion($matches[1]);
837
+ $this->setBrowser($this->BROWSER_FIREFOX);
838
+ return true;
839
+ }
840
+ else if( preg_match("/Firefox$/i",$this->_agent,$matches) ) {
841
+ $this->setVersion("");
842
+ $this->setBrowser($this->BROWSER_FIREFOX);
843
+ return true;
844
+ }
845
+ }
846
+ return false;
847
+ }
848
+
849
+ /**
850
+ * Determine if the browser is Firefox or not (last updated 1.7)
851
+ * @return boolean True if the browser is Firefox otherwise false
852
+ */
853
+ function checkBrowserIceweasel() {
854
+ if( stripos($this->_agent,'Iceweasel') !== false ) {
855
+ $aresult = explode('/',stristr($this->_agent,'Iceweasel'));
856
+ $aversion = explode(' ',$aresult[1]);
857
+ $this->setVersion($aversion[0]);
858
+ $this->setBrowser($this->BROWSER_ICEWEASEL);
859
+ return true;
860
+ }
861
+ return false;
862
+ }
863
+ /**
864
+ * Determine if the browser is Mozilla or not (last updated 1.7)
865
+ * @return boolean True if the browser is Mozilla otherwise false
866
+ */
867
+ function checkBrowserMozilla() {
868
+ if( stripos($this->_agent,'mozilla') !== false && preg_match('/rv:[0-9].[0-9][a-b]?/i',$this->_agent) && stripos($this->_agent,'netscape') === false) {
869
+ $aversion = explode(' ',stristr($this->_agent,'rv:'));
870
+ preg_match('/rv:[0-9].[0-9][a-b]?/i',$this->_agent,$aversion);
871
+ $this->setVersion(str_replace('rv:','',$aversion[0]));
872
+ $this->setBrowser($this->BROWSER_MOZILLA);
873
+ return true;
874
+ }
875
+ else if( stripos($this->_agent,'mozilla') !== false && preg_match('/rv:[0-9]\.[0-9]/i',$this->_agent) && stripos($this->_agent,'netscape') === false ) {
876
+ $aversion = explode('',stristr($this->_agent,'rv:'));
877
+ $this->setVersion(str_replace('rv:','',$aversion[0]));
878
+ $this->setBrowser($this->BROWSER_MOZILLA);
879
+ return true;
880
+ }
881
+ else if( stripos($this->_agent,'mozilla') !== false && preg_match('/mozilla\/([^ ]*)/i',$this->_agent,$matches) && stripos($this->_agent,'netscape') === false ) {
882
+ $this->setVersion($matches[1]);
883
+ $this->setBrowser($this->BROWSER_MOZILLA);
884
+ return true;
885
+ }
886
+ return false;
887
+ }
888
+
889
+ /**
890
+ * Determine if the browser is Lynx or not (last updated 1.7)
891
+ * @return boolean True if the browser is Lynx otherwise false
892
+ */
893
+ function checkBrowserLynx() {
894
+ if( stripos($this->_agent,'lynx') !== false ) {
895
+ $aresult = explode('/',stristr($this->_agent,'Lynx'));
896
+ $aversion = explode(' ',(isset($aresult[1])?$aresult[1]:""));
897
+ $this->setVersion($aversion[0]);
898
+ $this->setBrowser($this->BROWSER_LYNX);
899
+ return true;
900
+ }
901
+ return false;
902
+ }
903
+
904
+ /**
905
+ * Determine if the browser is Amaya or not (last updated 1.7)
906
+ * @return boolean True if the browser is Amaya otherwise false
907
+ */
908
+ function checkBrowserAmaya() {
909
+ if( stripos($this->_agent,'amaya') !== false ) {
910
+ $aresult = explode('/',stristr($this->_agent,'Amaya'));
911
+ $aversion = explode(' ',$aresult[1]);
912
+ $this->setVersion($aversion[0]);
913
+ $this->setBrowser($this->BROWSER_AMAYA);
914
+ return true;
915
+ }
916
+ return false;
917
+ }
918
+
919
+ /**
920
+ * Determine if the browser is Safari or not (last updated 1.7)
921
+ * @return boolean True if the browser is Safari otherwise false
922
+ */
923
+ function checkBrowserSafari() {
924
+ if( stripos($this->_agent,'Safari') !== false && stripos($this->_agent,'iPhone') === false && stripos($this->_agent,'iPod') === false ) {
925
+ $aresult = explode('/',stristr($this->_agent,'Version'));
926
+ if( isset($aresult[1]) ) {
927
+ $aversion = explode(' ',$aresult[1]);
928
+ $this->setVersion($aversion[0]);
929
+ }
930
+ else {
931
+ $this->setVersion($this->VERSION_UNKNOWN);
932
+ }
933
+ $this->setBrowser($this->BROWSER_SAFARI);
934
+ return true;
935
+ }
936
+ return false;
937
+ }
938
+
939
+ /**
940
+ * Determine if the browser is iPhone or not (last updated 1.7)
941
+ * @return boolean True if the browser is iPhone otherwise false
942
+ */
943
+ function checkBrowseriPhone() {
944
+ if( stripos($this->_agent,'iPhone') !== false ) {
945
+ $aresult = explode('/',stristr($this->_agent,'Version'));
946
+ if( isset($aresult[1]) ) {
947
+ $aversion = explode(' ',$aresult[1]);
948
+ $this->setVersion($aversion[0]);
949
+ }
950
+ else {
951
+ $this->setVersion($this->VERSION_UNKNOWN);
952
+ }
953
+ $this->setMobile(true);
954
+ $this->setBrowser($this->BROWSER_IPHONE);
955
+ return true;
956
+ }
957
+ return false;
958
+ }
959
+
960
+ /**
961
+ * Determine if the browser is iPod or not (last updated 1.7)
962
+ * @return boolean True if the browser is iPod otherwise false
963
+ */
964
+ function checkBrowseriPad() {
965
+ if( stripos($this->_agent,'iPad') !== false ) {
966
+ $aresult = explode('/',stristr($this->_agent,'Version'));
967
+ if( isset($aresult[1]) ) {
968
+ $aversion = explode(' ',$aresult[1]);
969
+ $this->setVersion($aversion[0]);
970
+ }
971
+ else {
972
+ $this->setVersion($this->VERSION_UNKNOWN);
973
+ }
974
+ $this->setMobile(true);
975
+ $this->setBrowser($this->BROWSER_IPAD);
976
+ return true;
977
+ }
978
+ return false;
979
+ }
980
+
981
+ /**
982
+ * Determine if the browser is iPod or not (last updated 1.7)
983
+ * @return boolean True if the browser is iPod otherwise false
984
+ */
985
+ function checkBrowseriPod() {
986
+ if( stripos($this->_agent,'iPod') !== false ) {
987
+ $aresult = explode('/',stristr($this->_agent,'Version'));
988
+ if( isset($aresult[1]) ) {
989
+ $aversion = explode(' ',$aresult[1]);
990
+ $this->setVersion($aversion[0]);
991
+ }
992
+ else {
993
+ $this->setVersion($this->VERSION_UNKNOWN);
994
+ }
995
+ $this->setMobile(true);
996
+ $this->setBrowser($this->BROWSER_IPOD);
997
+ return true;
998
+ }
999
+ return false;
1000
+ }
1001
+
1002
+ /**
1003
+ * Determine if the browser is Android or not (last updated 1.7)
1004
+ * @return boolean True if the browser is Android otherwise false
1005
+ */
1006
+ function checkBrowserAndroid() {
1007
+ if( stripos($this->_agent,'Android') !== false ) {
1008
+ $aresult = explode(' ',stristr($this->_agent,'Android'));
1009
+ if( isset($aresult[1]) ) {
1010
+ $aversion = explode(' ',$aresult[1]);
1011
+ $this->setVersion($aversion[0]);
1012
+ }
1013
+ else {
1014
+ $this->setVersion($this->VERSION_UNKNOWN);
1015
+ }
1016
+ $this->setMobile(true);
1017
+ $this->setBrowser($this->BROWSER_ANDROID);
1018
+ return true;
1019
+ }
1020
+ return false;
1021
+ }
1022
+
1023
+ /**
1024
+ * Determine the user's platform (last updated 1.7)
1025
+ */
1026
+ function checkPlatform() {
1027
+ if( stripos($this->_agent, 'windows') !== false ) {
1028
+ $this->_platform = $this->PLATFORM_WINDOWS;
1029
+ }
1030
+ else if( stripos($this->_agent, 'iPad') !== false ) {
1031
+ $this->_platform = $this->PLATFORM_IPAD;
1032
+ }
1033
+ else if( stripos($this->_agent, 'iPod') !== false ) {
1034
+ $this->_platform = $this->PLATFORM_IPOD;
1035
+ }
1036
+ else if( stripos($this->_agent, 'iPhone') !== false ) {
1037
+ $this->_platform = $this->PLATFORM_IPHONE;
1038
+ }
1039
+ elseif( stripos($this->_agent, 'mac') !== false ) {
1040
+ $this->_platform = $this->PLATFORM_APPLE;
1041
+ }
1042
+ elseif( stripos($this->_agent, 'android') !== false ) {
1043
+ $this->_platform = $this->PLATFORM_ANDROID;
1044
+ }
1045
+ elseif( stripos($this->_agent, 'linux') !== false ) {
1046
+ $this->_platform = $this->PLATFORM_LINUX;
1047
+ }
1048
+ else if( stripos($this->_agent, 'Nokia') !== false ) {
1049
+ $this->_platform = $this->PLATFORM_NOKIA;
1050
+ }
1051
+ else if( stripos($this->_agent, 'BlackBerry') !== false ) {
1052
+ $this->_platform = $this->PLATFORM_BLACKBERRY;
1053
+ }
1054
+ elseif( stripos($this->_agent,'FreeBSD') !== false ) {
1055
+ $this->_platform = $this->PLATFORM_FREEBSD;
1056
+ }
1057
+ elseif( stripos($this->_agent,'OpenBSD') !== false ) {
1058
+ $this->_platform = $this->PLATFORM_OPENBSD;
1059
+ }
1060
+ elseif( stripos($this->_agent,'NetBSD') !== false ) {
1061
+ $this->_platform = $this->PLATFORM_NETBSD;
1062
+ }
1063
+ elseif( stripos($this->_agent, 'OpenSolaris') !== false ) {
1064
+ $this->_platform = $this->PLATFORM_OPENSOLARIS;
1065
+ }
1066
+ elseif( stripos($this->_agent, 'SunOS') !== false ) {
1067
+ $this->_platform = $this->PLATFORM_SUNOS;
1068
+ }
1069
+ elseif( stripos($this->_agent, 'OS\/2') !== false ) {
1070
+ $this->_platform = $this->PLATFORM_OS2;
1071
+ }
1072
+ elseif( stripos($this->_agent, 'BeOS') !== false ) {
1073
+ $this->_platform = $this->PLATFORM_BEOS;
1074
+ }
1075
+ elseif( stripos($this->_agent, 'win') !== false ) {
1076
+ $this->_platform = $this->PLATFORM_WINDOWS;
1077
+ }
1078
+
1079
+ }
1080
+ }
1081
+
1082
+ ?>
includes/libraries/php-markdown/markdown.php CHANGED
@@ -1,2932 +1,2932 @@
1
- <?php
2
- #
3
- # Markdown Extra - A text-to-HTML conversion tool for web writers
4
- #
5
- # PHP Markdown & Extra
6
- # Copyright (c) 2004-2009 Michel Fortin
7
- # <http://michelf.com/projects/php-markdown/>
8
- #
9
- # Original Markdown
10
- # Copyright (c) 2004-2006 John Gruber
11
- # <http://daringfireball.net/projects/markdown/>
12
- #
13
-
14
-
15
- define( 'MARKDOWN_VERSION', "1.0.1n" ); # Sat 10 Oct 2009
16
- define( 'MARKDOWNEXTRA_VERSION', "1.2.4" ); # Sat 10 Oct 2009
17
-
18
-
19
- #
20
- # Global default settings:
21
- #
22
-
23
- # Change to ">" for HTML output
24
- @define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />");
25
-
26
- # Define the width of a tab for code blocks.
27
- @define( 'MARKDOWN_TAB_WIDTH', 4 );
28
-
29
- # Optional title attribute for footnote links and backlinks.
30
- @define( 'MARKDOWN_FN_LINK_TITLE', "" );
31
- @define( 'MARKDOWN_FN_BACKLINK_TITLE', "" );
32
-
33
- # Optional class attribute for footnote links and backlinks.
34
- @define( 'MARKDOWN_FN_LINK_CLASS', "" );
35
- @define( 'MARKDOWN_FN_BACKLINK_CLASS', "" );
36
-
37
-
38
- #
39
- # WordPress settings:
40
- #
41
-
42
- # Change to false to remove Markdown from posts and/or comments.
43
- @define( 'MARKDOWN_WP_POSTS', true );
44
- @define( 'MARKDOWN_WP_COMMENTS', true );
45
-
46
-
47
-
48
- ### Standard Function Interface ###
49
-
50
- @define( 'MARKDOWN_PARSER_CLASS', 'MarkdownExtra_Parser' );
51
-
52
- function Markdown($text) {
53
- #
54
- # Initialize the parser and return the result of its transform method.
55
- #
56
- # Setup static parser variable.
57
- static $parser;
58
- if (!isset($parser)) {
59
- $parser_class = MARKDOWN_PARSER_CLASS;
60
- $parser = new $parser_class;
61
- }
62
-
63
- # Transform text using parser.
64
- return $parser->transform($text);
65
- }
66
-
67
-
68
- ### WordPress Plugin Interface ###
69
-
70
- /*
71
- Plugin Name: Markdown Extra
72
- Plugin URI: http://michelf.com/projects/php-markdown/
73
- Description: <a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.com/projects/php-markdown/">More...</a>
74
- Version: 1.2.4
75
- Author: Michel Fortin
76
- Author URI: http://michelf.com/
77
- */
78
-
79
- if (isset($wp_version)) {
80
- # More details about how it works here:
81
- # <http://michelf.com/weblog/2005/wordpress-text-flow-vs-markdown/>
82
-
83
- # Post content and excerpts
84
- # - Remove WordPress paragraph generator.
85
- # - Run Markdown on excerpt, then remove all tags.
86
- # - Add paragraph tag around the excerpt, but remove it for the excerpt rss.
87
- if (MARKDOWN_WP_POSTS) {
88
- remove_filter('the_content', 'wpautop');
89
- remove_filter('the_content_rss', 'wpautop');
90
- remove_filter('the_excerpt', 'wpautop');
91
- add_filter('the_content', 'mdwp_MarkdownPost', 6);
92
- add_filter('the_content_rss', 'mdwp_MarkdownPost', 6);
93
- add_filter('get_the_excerpt', 'mdwp_MarkdownPost', 6);
94
- add_filter('get_the_excerpt', 'trim', 7);
95
- add_filter('the_excerpt', 'mdwp_add_p');
96
- add_filter('the_excerpt_rss', 'mdwp_strip_p');
97
-
98
- remove_filter('content_save_pre', 'balanceTags', 50);
99
- remove_filter('excerpt_save_pre', 'balanceTags', 50);
100
- add_filter('the_content', 'balanceTags', 50);
101
- add_filter('get_the_excerpt', 'balanceTags', 9);
102
- }
103
-
104
- # Add a footnote id prefix to posts when inside a loop.
105
- function mdwp_MarkdownPost($text) {
106
- static $parser;
107
- if (!$parser) {
108
- $parser_class = MARKDOWN_PARSER_CLASS;
109
- $parser = new $parser_class;
110
- }
111
- if (is_single() || is_page() || is_feed()) {
112
- $parser->fn_id_prefix = "";
113
- } else {
114
- $parser->fn_id_prefix = get_the_ID() . ".";
115
- }
116
- return $parser->transform($text);
117
- }
118
-
119
- # Comments
120
- # - Remove WordPress paragraph generator.
121
- # - Remove WordPress auto-link generator.
122
- # - Scramble important tags before passing them to the kses filter.
123
- # - Run Markdown on excerpt then remove paragraph tags.
124
- if (MARKDOWN_WP_COMMENTS) {
125
- remove_filter('comment_text', 'wpautop', 30);
126
- remove_filter('comment_text', 'make_clickable');
127
- add_filter('pre_comment_content', 'Markdown', 6);
128
- add_filter('pre_comment_content', 'mdwp_hide_tags', 8);
129
- add_filter('pre_comment_content', 'mdwp_show_tags', 12);
130
- add_filter('get_comment_text', 'Markdown', 6);
131
- add_filter('get_comment_excerpt', 'Markdown', 6);
132
- add_filter('get_comment_excerpt', 'mdwp_strip_p', 7);
133
-
134
- global $mdwp_hidden_tags, $mdwp_placeholders;
135
- $mdwp_hidden_tags = explode(' ',
136
- '<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> </li>');
137
- $mdwp_placeholders = explode(' ', str_rot13(
138
- 'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '.
139
- 'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli'));
140
- }
141
-
142
- function mdwp_add_p($text) {
143
- if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) {
144
- $text = '<p>'.$text.'</p>';
145
- $text = preg_replace('{\n{2,}}', "</p>\n\n<p>", $text);
146
- }
147
- return $text;
148
- }
149
-
150
- function mdwp_strip_p($t) { return preg_replace('{</?p>}i', '', $t); }
151
-
152
- function mdwp_hide_tags($text) {
153
- global $mdwp_hidden_tags, $mdwp_placeholders;
154
- return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text);
155
- }
156
- function mdwp_show_tags($text) {
157
- global $mdwp_hidden_tags, $mdwp_placeholders;
158
- return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text);
159
- }
160
- }
161
-
162
-
163
- ### bBlog Plugin Info ###
164
-
165
- function identify_modifier_markdown() {
166
- return array(
167
- 'name' => 'markdown',
168
- 'type' => 'modifier',
169
- 'nicename' => 'PHP Markdown Extra',
170
- 'description' => 'A text-to-HTML conversion tool for web writers',
171
- 'authors' => 'Michel Fortin and John Gruber',
172
- 'licence' => 'GPL',
173
- 'version' => MARKDOWNEXTRA_VERSION,
174
- 'help' => '<a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.com/projects/php-markdown/">More...</a>',
175
- );
176
- }
177
-
178
-
179
- ### Smarty Modifier Interface ###
180
-
181
- function smarty_modifier_markdown($text) {
182
- return Markdown($text);
183
- }
184
-
185
-
186
- ### Textile Compatibility Mode ###
187
-
188
- # Rename this file to "classTextile.php" and it can replace Textile everywhere.
189
-
190
- if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
191
- # Try to include PHP SmartyPants. Should be in the same directory.
192
- @include_once 'smartypants.php';
193
- # Fake Textile class. It calls Markdown instead.
194
- class Textile {
195
- function TextileThis($text, $lite='', $encode='') {
196
- if ($lite == '' && $encode == '') $text = Markdown($text);
197
- if (function_exists('SmartyPants')) $text = SmartyPants($text);
198
- return $text;
199
- }
200
- # Fake restricted version: restrictions are not supported for now.
201
- function TextileRestricted($text, $lite='', $noimage='') {
202
- return $this->TextileThis($text, $lite);
203
- }
204
- # Workaround to ensure compatibility with TextPattern 4.0.3.
205
- function blockLite($text) { return $text; }
206
- }
207
- }
208
-
209
-
210
-
211
- #
212
- # Markdown Parser Class
213
- #
214
-
215
- class Markdown_Parser {
216
-
217
- # Regex to match balanced [brackets].
218
- # Needed to insert a maximum bracked depth while converting to PHP.
219
- var $nested_brackets_depth = 6;
220
- var $nested_brackets_re;
221
-
222
- var $nested_url_parenthesis_depth = 4;
223
- var $nested_url_parenthesis_re;
224
-
225
- # Table of hash values for escaped characters:
226
- var $escape_chars = '\`*_{}[]()>#+-.!';
227
- var $escape_chars_re;
228
-
229
- # Change to ">" for HTML output.
230
- var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
231
- var $tab_width = MARKDOWN_TAB_WIDTH;
232
-
233
- # Change to `true` to disallow markup or entities.
234
- var $no_markup = false;
235
- var $no_entities = false;
236
-
237
- # Predefined urls and titles for reference links and images.
238
- var $predef_urls = array();
239
- var $predef_titles = array();
240
-
241
-
242
- function Markdown_Parser() {
243
- #
244
- # Constructor function. Initialize appropriate member variables.
245
- #
246
- $this->_initDetab();
247
- $this->prepareItalicsAndBold();
248
-
249
- $this->nested_brackets_re =
250
- str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth).
251
- str_repeat('\])*', $this->nested_brackets_depth);
252
-
253
- $this->nested_url_parenthesis_re =
254
- str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth).
255
- str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
256
-
257
- $this->escape_chars_re = '['.preg_quote($this->escape_chars).']';
258
-
259
- # Sort document, block, and span gamut in ascendent priority order.
260
- asort($this->document_gamut);
261
- asort($this->block_gamut);
262
- asort($this->span_gamut);
263
- }
264
-
265
-
266
- # Internal hashes used during transformation.
267
- var $urls = array();
268
- var $titles = array();
269
- var $html_hashes = array();
270
-
271
- # Status flag to avoid invalid nesting.
272
- var $in_anchor = false;
273
-
274
-
275
- function setup() {
276
- #
277
- # Called before the transformation process starts to setup parser
278
- # states.
279
- #
280
- # Clear global hashes.
281
- $this->urls = $this->predef_urls;
282
- $this->titles = $this->predef_titles;
283
- $this->html_hashes = array();
284
-
285
- $in_anchor = false;
286
- }
287
-
288
- function teardown() {
289
- #
290
- # Called after the transformation process to clear any variable
291
- # which may be taking up memory unnecessarly.
292
- #
293
- $this->urls = array();
294
- $this->titles = array();
295
- $this->html_hashes = array();
296
- }
297
-
298
-
299
- function transform($text) {
300
- #
301
- # Main function. Performs some preprocessing on the input text
302
- # and pass it through the document gamut.
303
- #
304
- $this->setup();
305
-
306
- # Remove UTF-8 BOM and marker character in input, if present.
307
- $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
308
-
309
- # Standardize line endings:
310
- # DOS to Unix and Mac to Unix
311
- $text = preg_replace('{\r\n?}', "\n", $text);
312
-
313
- # Make sure $text ends with a couple of newlines:
314
- $text .= "\n\n";
315
-
316
- # Convert all tabs to spaces.
317
- $text = $this->detab($text);
318
-
319
- # Turn block-level HTML blocks into hash entries
320
- $text = $this->hashHTMLBlocks($text);
321
-
322
- # Strip any lines consisting only of spaces and tabs.
323
- # This makes subsequent regexen easier to write, because we can
324
- # match consecutive blank lines with /\n+/ instead of something
325
- # contorted like /[ ]*\n+/ .
326
- $text = preg_replace('/^[ ]+$/m', '', $text);
327
-
328
- # Run document gamut methods.
329
- foreach ($this->document_gamut as $method => $priority) {
330
- $text = $this->$method($text);
331
- }
332
-
333
- $this->teardown();
334
-
335
- return $text . "\n";
336
- }
337
-
338
- var $document_gamut = array(
339
- # Strip link definitions, store in hashes.
340
- "stripLinkDefinitions" => 20,
341
-
342
- "runBasicBlockGamut" => 30,
343
- );
344
-
345
-
346
- function stripLinkDefinitions($text) {
347
- #
348
- # Strips link definitions from text, stores the URLs and titles in
349
- # hash references.
350
- #
351
- $less_than_tab = $this->tab_width - 1;
352
-
353
- # Link defs are in the form: ^[id]: url "optional title"
354
- $text = preg_replace_callback('{
355
- ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
356
- [ ]*
357
- \n? # maybe *one* newline
358
- [ ]*
359
- (?:
360
- <(.+?)> # url = $2
361
- |
362
- (\S+?) # url = $3
363
- )
364
- [ ]*
365
- \n? # maybe one newline
366
- [ ]*
367
- (?:
368
- (?<=\s) # lookbehind for whitespace
369
- ["(]
370
- (.*?) # title = $4
371
- [")]
372
- [ ]*
373
- )? # title is optional
374
- (?:\n+|\Z)
375
- }xm',
376
- array(&$this, '_stripLinkDefinitions_callback'),
377
- $text);
378
- return $text;
379
- }
380
- function _stripLinkDefinitions_callback($matches) {
381
- $link_id = strtolower($matches[1]);
382
- $url = $matches[2] == '' ? $matches[3] : $matches[2];
383
- $this->urls[$link_id] = $url;
384
- $this->titles[$link_id] =& $matches[4];
385
- return ''; # String that will replace the block
386
- }
387
-
388
-
389
- function hashHTMLBlocks($text) {
390
- if ($this->no_markup) return $text;
391
-
392
- $less_than_tab = $this->tab_width - 1;
393
-
394
- # Hashify HTML blocks:
395
- # We only want to do this for block-level HTML tags, such as headers,
396
- # lists, and tables. That's because we still want to wrap <p>s around
397
- # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
398
- # phrase emphasis, and spans. The list of tags we're looking for is
399
- # hard-coded:
400
- #
401
- # * List "a" is made of tags which can be both inline or block-level.
402
- # These will be treated block-level when the start tag is alone on
403
- # its line, otherwise they're not matched here and will be taken as
404
- # inline later.
405
- # * List "b" is made of tags which are always block-level;
406
- #
407
- $block_tags_a_re = 'ins|del';
408
- $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
409
- 'script|noscript|form|fieldset|iframe|math';
410
-
411
- # Regular expression for the content of a block tag.
412
- $nested_tags_level = 4;
413
- $attr = '
414
- (?> # optional tag attributes
415
- \s # starts with whitespace
416
- (?>
417
- [^>"/]+ # text outside quotes
418
- |
419
- /+(?!>) # slash not followed by ">"
420
- |
421
- "[^"]*" # text inside double quotes (tolerate ">")
422
- |
423
- \'[^\']*\' # text inside single quotes (tolerate ">")
424
- )*
425
- )?
426
- ';
427
- $content =
428
- str_repeat('
429
- (?>
430
- [^<]+ # content without tag
431
- |
432
- <\2 # nested opening tag
433
- '.$attr.' # attributes
434
- (?>
435
- />
436
- |
437
- >', $nested_tags_level). # end of opening tag
438
- '.*?'. # last level nested tag content
439
- str_repeat('
440
- </\2\s*> # closing nested tag
441
- )
442
- |
443
- <(?!/\2\s*> # other tags with a different name
444
- )
445
- )*',
446
- $nested_tags_level);
447
- $content2 = str_replace('\2', '\3', $content);
448
-
449
- # First, look for nested blocks, e.g.:
450
- # <div>
451
- # <div>
452
- # tags for inner block must be indented.
453
- # </div>
454
- # </div>
455
- #
456
- # The outermost tags must start at the left margin for this to match, and
457
- # the inner nested divs must be indented.
458
- # We need to do this before the next, more liberal match, because the next
459
- # match will start at the first `<div>` and stop at the first `</div>`.
460
- $text = preg_replace_callback('{(?>
461
- (?>
462
- (?<=\n\n) # Starting after a blank line
463
- | # or
464
- \A\n? # the beginning of the doc
465
- )
466
- ( # save in $1
467
-
468
- # Match from `\n<tag>` to `</tag>\n`, handling nested tags
469
- # in between.
470
-
471
- [ ]{0,'.$less_than_tab.'}
472
- <('.$block_tags_b_re.')# start tag = $2
473
- '.$attr.'> # attributes followed by > and \n
474
- '.$content.' # content, support nesting
475
- </\2> # the matching end tag
476
- [ ]* # trailing spaces/tabs
477
- (?=\n+|\Z) # followed by a newline or end of document
478
-
479
- | # Special version for tags of group a.
480
-
481
- [ ]{0,'.$less_than_tab.'}
482
- <('.$block_tags_a_re.')# start tag = $3
483
- '.$attr.'>[ ]*\n # attributes followed by >
484
- '.$content2.' # content, support nesting
485
- </\3> # the matching end tag
486
- [ ]* # trailing spaces/tabs
487
- (?=\n+|\Z) # followed by a newline or end of document
488
-
489
- | # Special case just for <hr />. It was easier to make a special
490
- # case than to make the other regex more complicated.
491
-
492
- [ ]{0,'.$less_than_tab.'}
493
- <(hr) # start tag = $2
494
- '.$attr.' # attributes
495
- /?> # the matching end tag
496
- [ ]*
497
- (?=\n{2,}|\Z) # followed by a blank line or end of document
498
-
499
- | # Special case for standalone HTML comments:
500
-
501
- [ ]{0,'.$less_than_tab.'}
502
- (?s:
503
- <!-- .*? -->
504
- )
505
- [ ]*
506
- (?=\n{2,}|\Z) # followed by a blank line or end of document
507
-
508
- | # PHP and ASP-style processor instructions (<? and <%)
509
-
510
- [ ]{0,'.$less_than_tab.'}
511
- (?s:
512
- <([?%]) # $2
513
- .*?
514
- \2>
515
- )
516
- [ ]*
517
- (?=\n{2,}|\Z) # followed by a blank line or end of document
518
-
519
- )
520
- )}Sxmi',
521
- array(&$this, '_hashHTMLBlocks_callback'),
522
- $text);
523
-
524
- return $text;
525
- }
526
- function _hashHTMLBlocks_callback($matches) {
527
- $text = $matches[1];
528
- $key = $this->hashBlock($text);
529
- return "\n\n$key\n\n";
530
- }
531
-
532
-
533
- function hashPart($text, $boundary = 'X') {
534
- #
535
- # Called whenever a tag must be hashed when a function insert an atomic
536
- # element in the text stream. Passing $text to through this function gives
537
- # a unique text-token which will be reverted back when calling unhash.
538
- #
539
- # The $boundary argument specify what character should be used to surround
540
- # the token. By convension, "B" is used for block elements that needs not
541
- # to be wrapped into paragraph tags at the end, ":" is used for elements
542
- # that are word separators and "X" is used in the general case.
543
- #
544
- # Swap back any tag hash found in $text so we do not have to `unhash`
545
- # multiple times at the end.
546
- $text = $this->unhash($text);
547
-
548
- # Then hash the block.
549
- static $i = 0;
550
- $key = "$boundary\x1A" . ++$i . $boundary;
551
- $this->html_hashes[$key] = $text;
552
- return $key; # String that will replace the tag.
553
- }
554
-
555
-
556
- function hashBlock($text) {
557
- #
558
- # Shortcut function for hashPart with block-level boundaries.
559
- #
560
- return $this->hashPart($text, 'B');
561
- }
562
-
563
-
564
- var $block_gamut = array(
565
- #
566
- # These are all the transformations that form block-level
567
- # tags like paragraphs, headers, and list items.
568
- #
569
- "doHeaders" => 10,
570
- "doHorizontalRules" => 20,
571
-
572
- "doLists" => 40,
573
- "doCodeBlocks" => 50,
574
- "doBlockQuotes" => 60,
575
- );
576
-
577
- function runBlockGamut($text) {
578
- #
579
- # Run block gamut tranformations.
580
- #
581
- # We need to escape raw HTML in Markdown source before doing anything
582
- # else. This need to be done for each block, and not only at the
583
- # begining in the Markdown function since hashed blocks can be part of
584
- # list items and could have been indented. Indented blocks would have
585
- # been seen as a code block in a previous pass of hashHTMLBlocks.
586
- $text = $this->hashHTMLBlocks($text);
587
-
588
- return $this->runBasicBlockGamut($text);
589
- }
590
-
591
- function runBasicBlockGamut($text) {
592
- #
593
- # Run block gamut tranformations, without hashing HTML blocks. This is
594
- # useful when HTML blocks are known to be already hashed, like in the first
595
- # whole-document pass.
596
- #
597
- foreach ($this->block_gamut as $method => $priority) {
598
- $text = $this->$method($text);
599
- }
600
-
601
- # Finally form paragraph and restore hashed blocks.
602
- $text = $this->formParagraphs($text);
603
-
604
- return $text;
605
- }
606
-
607
-
608
- function doHorizontalRules($text) {
609
- # Do Horizontal Rules:
610
- return preg_replace(
611
- '{
612
- ^[ ]{0,3} # Leading space
613
- ([-*_]) # $1: First marker
614
- (?> # Repeated marker group
615
- [ ]{0,2} # Zero, one, or two spaces.
616
- \1 # Marker character
617
- ){2,} # Group repeated at least twice
618
- [ ]* # Tailing spaces
619
- $ # End of line.
620
- }mx',
621
- "\n".$this->hashBlock("<hr$this->empty_element_suffix")."\n",
622
- $text);
623
- }
624
-
625
-
626
- var $span_gamut = array(
627
- #
628
- # These are all the transformations that occur *within* block-level
629
- # tags like paragraphs, headers, and list items.
630
- #
631
- # Process character escapes, code spans, and inline HTML
632
- # in one shot.
633
- "parseSpan" => -30,
634
-
635
- # Process anchor and image tags. Images must come first,
636
- # because ![foo][f] looks like an anchor.
637
- "doImages" => 10,
638
- "doAnchors" => 20,
639
-
640
- # Make links out of things like `<http://example.com/>`
641
- # Must come after doAnchors, because you can use < and >
642
- # delimiters in inline links like [this](<url>).
643
- "doAutoLinks" => 30,
644
- "encodeAmpsAndAngles" => 40,
645
-
646
- "doItalicsAndBold" => 50,
647
- "doHardBreaks" => 60,
648
- );
649
-
650
- function runSpanGamut($text) {
651
- #
652
- # Run span gamut tranformations.
653
- #
654
- foreach ($this->span_gamut as $method => $priority) {
655
- $text = $this->$method($text);
656
- }
657
-
658
- return $text;
659
- }
660
-
661
-
662
- function doHardBreaks($text) {
663
- # Do hard breaks:
664
- return preg_replace_callback('/ {2,}\n/',
665
- array(&$this, '_doHardBreaks_callback'), $text);
666
- }
667
- function _doHardBreaks_callback($matches) {
668
- return $this->hashPart("<br$this->empty_element_suffix\n");
669
- }
670
-
671
-
672
- function doAnchors($text) {
673
- #
674
- # Turn Markdown link shortcuts into XHTML <a> tags.
675
- #
676
- if ($this->in_anchor) return $text;
677
- $this->in_anchor = true;
678
-
679
- #
680
- # First, handle reference-style links: [link text] [id]
681
- #
682
- $text = preg_replace_callback('{
683
- ( # wrap whole match in $1
684
- \[
685
- ('.$this->nested_brackets_re.') # link text = $2
686
- \]
687
-
688
- [ ]? # one optional space
689
- (?:\n[ ]*)? # one optional newline followed by spaces
690
-
691
- \[
692
- (.*?) # id = $3
693
- \]
694
- )
695
- }xs',
696
- array(&$this, '_doAnchors_reference_callback'), $text);
697
-
698
- #
699
- # Next, inline-style links: [link text](url "optional title")
700
- #
701
- $text = preg_replace_callback('{
702
- ( # wrap whole match in $1
703
- \[
704
- ('.$this->nested_brackets_re.') # link text = $2
705
- \]
706
- \( # literal paren
707
- [ \n]*
708
- (?:
709
- <(.+?)> # href = $3
710
- |
711
- ('.$this->nested_url_parenthesis_re.') # href = $4
712
- )
713
- [ \n]*
714
- ( # $5
715
- ([\'"]) # quote char = $6
716
- (.*?) # Title = $7
717
- \6 # matching quote
718
- [ \n]* # ignore any spaces/tabs between closing quote and )
719
- )? # title is optional
720
- \)
721
- )
722
- }xs',
723
- array(&$this, '_doAnchors_inline_callback'), $text);
724
-
725
- #
726
- # Last, handle reference-style shortcuts: [link text]
727
- # These must come last in case you've also got [link text][1]
728
- # or [link text](/foo)
729
- #
730
- $text = preg_replace_callback('{
731
- ( # wrap whole match in $1
732
- \[
733
- ([^\[\]]+) # link text = $2; can\'t contain [ or ]
734
- \]
735
- )
736
- }xs',
737
- array(&$this, '_doAnchors_reference_callback'), $text);
738
-
739
- $this->in_anchor = false;
740
- return $text;
741
- }
742
- function _doAnchors_reference_callback($matches) {
743
- $whole_match = $matches[1];
744
- $link_text = $matches[2];
745
- $link_id =& $matches[3];
746
-
747
- if ($link_id == "") {
748
- # for shortcut links like [this][] or [this].
749
- $link_id = $link_text;
750
- }
751
-
752
- # lower-case and turn embedded newlines into spaces
753
- $link_id = strtolower($link_id);
754
- $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
755
-
756
- if (isset($this->urls[$link_id])) {
757
- $url = $this->urls[$link_id];
758
- $url = $this->encodeAttribute($url);
759
-
760
- $result = "<a href=\"$url\"";
761
- if ( isset( $this->titles[$link_id] ) ) {
762
- $title = $this->titles[$link_id];
763
- $title = $this->encodeAttribute($title);
764
- $result .= " title=\"$title\"";
765
- }
766
-
767
- $link_text = $this->runSpanGamut($link_text);
768
- $result .= ">$link_text</a>";
769
- $result = $this->hashPart($result);
770
- }
771
- else {
772
- $result = $whole_match;
773
- }
774
- return $result;
775
- }
776
- function _doAnchors_inline_callback($matches) {
777
- $whole_match = $matches[1];
778
- $link_text = $this->runSpanGamut($matches[2]);
779
- $url = $matches[3] == '' ? $matches[4] : $matches[3];
780
- $title =& $matches[7];
781
-
782
- $url = $this->encodeAttribute($url);
783
-
784
- $result = "<a href=\"$url\"";
785
- if (isset($title)) {
786
- $title = $this->encodeAttribute($title);
787
- $result .= " title=\"$title\"";
788
- }
789
-
790
- $link_text = $this->runSpanGamut($link_text);
791
- $result .= ">$link_text</a>";
792
-
793
- return $this->hashPart($result);
794
- }
795
-
796
-
797
- function doImages($text) {
798
- #
799
- # Turn Markdown image shortcuts into <img> tags.
800
- #
801
- #
802
- # First, handle reference-style labeled images: ![alt text][id]
803
- #
804
- $text = preg_replace_callback('{
805
- ( # wrap whole match in $1
806
- !\[
807
- ('.$this->nested_brackets_re.') # alt text = $2
808
- \]
809
-
810
- [ ]? # one optional space
811
- (?:\n[ ]*)? # one optional newline followed by spaces
812
-
813
- \[
814
- (.*?) # id = $3
815
- \]
816
-
817
- )
818
- }xs',
819
- array(&$this, '_doImages_reference_callback'), $text);
820
-
821
- #
822
- # Next, handle inline images: ![alt text](url "optional title")
823
- # Don't forget: encode * and _
824
- #
825
- $text = preg_replace_callback('{
826
- ( # wrap whole match in $1
827
- !\[
828
- ('.$this->nested_brackets_re.') # alt text = $2
829
- \]
830
- \s? # One optional whitespace character
831
- \( # literal paren
832
- [ \n]*
833
- (?:
834
- <(\S*)> # src url = $3
835
- |
836
- ('.$this->nested_url_parenthesis_re.') # src url = $4
837
- )
838
- [ \n]*
839
- ( # $5
840
- ([\'"]) # quote char = $6
841
- (.*?) # title = $7
842
- \6 # matching quote
843
- [ \n]*
844
- )? # title is optional
845
- \)
846
- )
847
- }xs',
848
- array(&$this, '_doImages_inline_callback'), $text);
849
-
850
- return $text;
851
- }
852
- function _doImages_reference_callback($matches) {
853
- $whole_match = $matches[1];
854
- $alt_text = $matches[2];
855
- $link_id = strtolower($matches[3]);
856
-
857
- if ($link_id == "") {
858
- $link_id = strtolower($alt_text); # for shortcut links like ![this][].
859
- }
860
-
861
- $alt_text = $this->encodeAttribute($alt_text);
862
- if (isset($this->urls[$link_id])) {
863
- $url = $this->encodeAttribute($this->urls[$link_id]);
864
- $result = "<img src=\"$url\" alt=\"$alt_text\"";
865
- if (isset($this->titles[$link_id])) {
866
- $title = $this->titles[$link_id];
867
- $title = $this->encodeAttribute($title);
868
- $result .= " title=\"$title\"";
869
- }
870
- $result .= $this->empty_element_suffix;
871
- $result = $this->hashPart($result);
872
- }
873
- else {
874
- # If there's no such link ID, leave intact:
875
- $result = $whole_match;
876
- }
877
-
878
- return $result;
879
- }
880
- function _doImages_inline_callback($matches) {
881
- $whole_match = $matches[1];
882
- $alt_text = $matches[2];
883
- $url = $matches[3] == '' ? $matches[4] : $matches[3];
884
- $title =& $matches[7];
885
-
886
- $alt_text = $this->encodeAttribute($alt_text);
887
- $url = $this->encodeAttribute($url);
888
- $result = "<img src=\"$url\" alt=\"$alt_text\"";
889
- if (isset($title)) {
890
- $title = $this->encodeAttribute($title);
891
- $result .= " title=\"$title\""; # $title already quoted
892
- }
893
- $result .= $this->empty_element_suffix;
894
-
895
- return $this->hashPart($result);
896
- }
897
-
898
-
899
- function doHeaders($text) {
900
- # Setext-style headers:
901
- # Header 1
902
- # ========
903
- #
904
- # Header 2
905
- # --------
906
- #
907
- $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
908
- array(&$this, '_doHeaders_callback_setext'), $text);
909
-
910
- # atx-style headers:
911
- # # Header 1
912
- # ## Header 2
913
- # ## Header 2 with closing hashes ##
914
- # ...
915
- # ###### Header 6
916
- #
917
- $text = preg_replace_callback('{
918
- ^(\#{1,6}) # $1 = string of #\'s
919
- [ ]*
920
- (.+?) # $2 = Header text
921
- [ ]*
922
- \#* # optional closing #\'s (not counted)
923
- \n+
924
- }xm',
925
- array(&$this, '_doHeaders_callback_atx'), $text);
926
-
927
- return $text;
928
- }
929
- function _doHeaders_callback_setext($matches) {
930
- # Terrible hack to check we haven't found an empty list item.
931
- if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1]))
932
- return $matches[0];
933
-
934
- $level = $matches[2]{0} == '=' ? 1 : 2;
935
- $block = "<h$level>".$this->runSpanGamut($matches[1])."</h$level>";
936
- return "\n" . $this->hashBlock($block) . "\n\n";
937
- }
938
- function _doHeaders_callback_atx($matches) {
939
- $level = strlen($matches[1]);
940
- $block = "<h$level>".$this->runSpanGamut($matches[2])."</h$level>";
941
- return "\n" . $this->hashBlock($block) . "\n\n";
942
- }
943
-
944
-
945
- function doLists($text) {
946
- #
947
- # Form HTML ordered (numbered) and unordered (bulleted) lists.
948
- #
949
- $less_than_tab = $this->tab_width - 1;
950
-
951
- # Re-usable patterns to match list item bullets and number markers:
952
- $marker_ul_re = '[*+-]';
953
- $marker_ol_re = '\d+[.]';
954
- $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
955
-
956
- $markers_relist = array(
957
- $marker_ul_re => $marker_ol_re,
958
- $marker_ol_re => $marker_ul_re,
959
- );
960
-
961
- foreach ($markers_relist as $marker_re => $other_marker_re) {
962
- # Re-usable pattern to match any entirel ul or ol list:
963
- $whole_list_re = '
964
- ( # $1 = whole list
965
- ( # $2
966
- ([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces
967
- ('.$marker_re.') # $4 = first list item marker
968
- [ ]+
969
- )
970
- (?s:.+?)
971
- ( # $5
972
- \z
973
- |
974
- \n{2,}
975
- (?=\S)
976
- (?! # Negative lookahead for another list item marker
977
- [ ]*
978
- '.$marker_re.'[ ]+
979
- )
980
- |
981
- (?= # Lookahead for another kind of list
982
- \n
983
- \3 # Must have the same indentation
984
- '.$other_marker_re.'[ ]+
985
- )
986
- )
987
- )
988
- '; // mx
989
-
990
- # We use a different prefix before nested lists than top-level lists.
991
- # See extended comment in _ProcessListItems().
992
-
993
- if ($this->list_level) {
994
- $text = preg_replace_callback('{
995
- ^
996
- '.$whole_list_re.'
997
- }mx',
998
- array(&$this, '_doLists_callback'), $text);
999
- }
1000
- else {
1001
- $text = preg_replace_callback('{
1002
- (?:(?<=\n)\n|\A\n?) # Must eat the newline
1003
- '.$whole_list_re.'
1004
- }mx',
1005
- array(&$this, '_doLists_callback'), $text);
1006
- }
1007
- }
1008
-
1009
- return $text;
1010
- }
1011
- function _doLists_callback($matches) {
1012
- # Re-usable patterns to match list item bullets and number markers:
1013
- $marker_ul_re = '[*+-]';
1014
- $marker_ol_re = '\d+[.]';
1015
- $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
1016
-
1017
- $list = $matches[1];
1018
- $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol";
1019
-
1020
- $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re );
1021
-
1022
- $list .= "\n";
1023
- $result = $this->processListItems($list, $marker_any_re);
1024
-
1025
- $result = $this->hashBlock("<$list_type>\n" . $result . "</$list_type>");
1026
- return "\n". $result ."\n\n";
1027
- }
1028
-
1029
- var $list_level = 0;
1030
-
1031
- function processListItems($list_str, $marker_any_re) {
1032
- #
1033
- # Process the contents of a single ordered or unordered list, splitting it
1034
- # into individual list items.
1035
- #
1036
- # The $this->list_level global keeps track of when we're inside a list.
1037
- # Each time we enter a list, we increment it; when we leave a list,
1038
- # we decrement. If it's zero, we're not in a list anymore.
1039
- #
1040
- # We do this because when we're not inside a list, we want to treat
1041
- # something like this:
1042
- #
1043
- # I recommend upgrading to version
1044
- # 8. Oops, now this line is treated
1045
- # as a sub-list.
1046
- #
1047
- # As a single paragraph, despite the fact that the second line starts
1048
- # with a digit-period-space sequence.
1049
- #
1050
- # Whereas when we're inside a list (or sub-list), that line will be
1051
- # treated as the start of a sub-list. What a kludge, huh? This is
1052
- # an aspect of Markdown's syntax that's hard to parse perfectly
1053
- # without resorting to mind-reading. Perhaps the solution is to
1054
- # change the syntax rules such that sub-lists must start with a
1055
- # starting cardinal number; e.g. "1." or "a.".
1056
-
1057
- $this->list_level++;
1058
-
1059
- # trim trailing blank lines:
1060
- $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
1061
-
1062
- $list_str = preg_replace_callback('{
1063
- (\n)? # leading line = $1
1064
- (^[ ]*) # leading whitespace = $2
1065
- ('.$marker_any_re.' # list marker and space = $3
1066
- (?:[ ]+|(?=\n)) # space only required if item is not empty
1067
- )
1068
- ((?s:.*?)) # list item text = $4
1069
- (?:(\n+(?=\n))|\n) # tailing blank line = $5
1070
- (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
1071
- }xm',
1072
- array(&$this, '_processListItems_callback'), $list_str);
1073
-
1074
- $this->list_level--;
1075
- return $list_str;
1076
- }
1077
- function _processListItems_callback($matches) {
1078
- $item = $matches[4];
1079
- $leading_line =& $matches[1];
1080
- $leading_space =& $matches[2];
1081
- $marker_space = $matches[3];
1082
- $tailing_blank_line =& $matches[5];
1083
-
1084
- if ($leading_line || $tailing_blank_line ||
1085
- preg_match('/\n{2,}/', $item))
1086
- {
1087
- # Replace marker with the appropriate whitespace indentation
1088
- $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item;
1089
- $item = $this->runBlockGamut($this->outdent($item)."\n");
1090
- }
1091
- else {
1092
- # Recursion for sub-lists:
1093
- $item = $this->doLists($this->outdent($item));
1094
- $item = preg_replace('/\n+$/', '', $item);
1095
- $item = $this->runSpanGamut($item);
1096
- }
1097
-
1098
- return "<li>" . $item . "</li>\n";
1099
- }
1100
-
1101
-
1102
- function doCodeBlocks($text) {
1103
- #
1104
- # Process Markdown `<pre><code>` blocks.
1105
- #
1106
- $text = preg_replace_callback('{
1107
- (?:\n\n|\A\n?)
1108
- ( # $1 = the code block -- one or more lines, starting with a space/tab
1109
- (?>
1110
- [ ]{'.$this->tab_width.'} # Lines must start with a tab or a tab-width of spaces
1111
- .*\n+
1112
- )+
1113
- )
1114
- ((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
1115
- }xm',
1116
- array(&$this, '_doCodeBlocks_callback'), $text);
1117
-
1118
- return $text;
1119
- }
1120
- function _doCodeBlocks_callback($matches) {
1121
- $codeblock = $matches[1];
1122
-
1123
- $codeblock = $this->outdent($codeblock);
1124
- $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
1125
-
1126
- # trim leading newlines and trailing newlines
1127
- $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
1128
-
1129
- $codeblock = "<pre><code>$codeblock\n</code></pre>";
1130
- return "\n\n".$this->hashBlock($codeblock)."\n\n";
1131
- }
1132
-
1133
-
1134
- function makeCodeSpan($code) {
1135
- #
1136
- # Create a code span markup for $code. Called from handleSpanToken.
1137
- #
1138
- $code = htmlspecialchars(trim($code), ENT_NOQUOTES);
1139
- return $this->hashPart("<code>$code</code>");
1140
- }
1141
-
1142
-
1143
- var $em_relist = array(
1144
- '' => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![.,:;]\s)',
1145
- '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
1146
- '_' => '(?<=\S|^)(?<!_)_(?!_)',
1147
- );
1148
- var $strong_relist = array(
1149
- '' => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![.,:;]\s)',
1150
- '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
1151
- '__' => '(?<=\S|^)(?<!_)__(?!_)',
1152
- );
1153
- var $em_strong_relist = array(
1154
- '' => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![.,:;]\s)',
1155
- '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
1156
- '___' => '(?<=\S|^)(?<!_)___(?!_)',
1157
- );
1158
- var $em_strong_prepared_relist;
1159
-
1160
- function prepareItalicsAndBold() {
1161
- #
1162
- # Prepare regular expressions for searching emphasis tokens in any
1163
- # context.
1164
- #
1165
- foreach ($this->em_relist as $em => $em_re) {
1166
- foreach ($this->strong_relist as $strong => $strong_re) {
1167
- # Construct list of allowed token expressions.
1168
- $token_relist = array();
1169
- if (isset($this->em_strong_relist["$em$strong"])) {
1170
- $token_relist[] = $this->em_strong_relist["$em$strong"];
1171
- }
1172
- $token_relist[] = $em_re;
1173
- $token_relist[] = $strong_re;
1174
-
1175
- # Construct master expression from list.
1176
- $token_re = '{('. implode('|', $token_relist) .')}';
1177
- $this->em_strong_prepared_relist["$em$strong"] = $token_re;
1178
- }
1179
- }
1180
- }
1181
-
1182
- function doItalicsAndBold($text) {
1183
- $token_stack = array('');
1184
- $text_stack = array('');
1185
- $em = '';
1186
- $strong = '';
1187
- $tree_char_em = false;
1188
-
1189
- while (1) {
1190
- #
1191
- # Get prepared regular expression for seraching emphasis tokens
1192
- # in current context.
1193
- #
1194
- $token_re = $this->em_strong_prepared_relist["$em$strong"];
1195
-
1196
- #
1197
- # Each loop iteration search for the next emphasis token.
1198
- # Each token is then passed to handleSpanToken.
1199
- #
1200
- $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
1201
- $text_stack[0] .= $parts[0];
1202
- $token =& $parts[1];
1203
- $text =& $parts[2];
1204
-
1205
- if (empty($token)) {
1206
- # Reached end of text span: empty stack without emitting.
1207
- # any more emphasis.
1208
- while ($token_stack[0]) {
1209
- $text_stack[1] .= array_shift($token_stack);
1210
- $text_stack[0] .= array_shift($text_stack);
1211
- }
1212
- break;
1213
- }
1214
-
1215
- $token_len = strlen($token);
1216
- if ($tree_char_em) {
1217
- # Reached closing marker while inside a three-char emphasis.
1218
- if ($token_len == 3) {
1219
- # Three-char closing marker, close em and strong.
1220
- array_shift($token_stack);
1221
- $span = array_shift($text_stack);
1222
- $span = $this->runSpanGamut($span);
1223
- $span = "<strong><em>$span</em></strong>";
1224
- $text_stack[0] .= $this->hashPart($span);
1225
- $em = '';
1226
- $strong = '';
1227
- } else {
1228
- # Other closing marker: close one em or strong and
1229
- # change current token state to match the other
1230
- $token_stack[0] = str_repeat($token{0}, 3-$token_len);
1231
- $tag = $token_len == 2 ? "strong" : "em";
1232
- $span = $text_stack[0];
1233
- $span = $this->runSpanGamut($span);
1234
- $span = "<$tag>$span</$tag>";
1235
- $text_stack[0] = $this->hashPart($span);
1236
- $$tag = ''; # $$tag stands for $em or $strong
1237
- }
1238
- $tree_char_em = false;
1239
- } else if ($token_len == 3) {
1240
- if ($em) {
1241
- # Reached closing marker for both em and strong.
1242
- # Closing strong marker:
1243
- for ($i = 0; $i < 2; ++$i) {
1244
- $shifted_token = array_shift($token_stack);
1245
- $tag = strlen($shifted_token) == 2 ? "strong" : "em";
1246
- $span = array_shift($text_stack);
1247
- $span = $this->runSpanGamut($span);
1248
- $span = "<$tag>$span</$tag>";
1249
- $text_stack[0] .= $this->hashPart($span);
1250
- $$tag = ''; # $$tag stands for $em or $strong
1251
- }
1252
- } else {
1253
- # Reached opening three-char emphasis marker. Push on token
1254
- # stack; will be handled by the special condition above.
1255
- $em = $token{0};
1256
- $strong = "$em$em";
1257
- array_unshift($token_stack, $token);
1258
- array_unshift($text_stack, '');
1259
- $tree_char_em = true;
1260
- }
1261
- } else if ($token_len == 2) {
1262
- if ($strong) {
1263
- # Unwind any dangling emphasis marker:
1264
- if (strlen($token_stack[0]) == 1) {
1265
- $text_stack[1] .= array_shift($token_stack);
1266
- $text_stack[0] .= array_shift($text_stack);
1267
- }
1268
- # Closing strong marker:
1269
- array_shift($token_stack);
1270
- $span = array_shift($text_stack);
1271
- $span = $this->runSpanGamut($span);
1272
- $span = "<strong>$span</strong>";
1273
- $text_stack[0] .= $this->hashPart($span);
1274
- $strong = '';
1275
- } else {
1276
- array_unshift($token_stack, $token);
1277
- array_unshift($text_stack, '');
1278
- $strong = $token;
1279
- }
1280
- } else {
1281
- # Here $token_len == 1
1282
- if ($em) {
1283
- if (strlen($token_stack[0]) == 1) {
1284
- # Closing emphasis marker:
1285
- array_shift($token_stack);
1286
- $span = array_shift($text_stack);
1287
- $span = $this->runSpanGamut($span);
1288
- $span = "<em>$span</em>";
1289
- $text_stack[0] .= $this->hashPart($span);
1290
- $em = '';
1291
- } else {
1292
- $text_stack[0] .= $token;
1293
- }
1294
- } else {
1295
- array_unshift($token_stack, $token);
1296
- array_unshift($text_stack, '');
1297
- $em = $token;
1298
- }
1299
- }
1300
- }
1301
- return $text_stack[0];
1302
- }
1303
-
1304
-
1305
- function doBlockQuotes($text) {
1306
- $text = preg_replace_callback('/
1307
- ( # Wrap whole match in $1
1308
- (?>
1309
- ^[ ]*>[ ]? # ">" at the start of a line
1310
- .+\n # rest of the first line
1311
- (.+\n)* # subsequent consecutive lines
1312
- \n* # blanks
1313
- )+
1314
- )
1315
- /xm',
1316
- array(&$this, '_doBlockQuotes_callback'), $text);
1317
-
1318
- return $text;
1319
- }
1320
- function _doBlockQuotes_callback($matches) {
1321
- $bq = $matches[1];
1322
- # trim one level of quoting - trim whitespace-only lines
1323
- $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
1324
- $bq = $this->runBlockGamut($bq); # recurse
1325
-
1326
- $bq = preg_replace('/^/m', " ", $bq);
1327
- # These leading spaces cause problem with <pre> content,
1328
- # so we need to fix that:
1329
- $bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx',
1330
- array(&$this, '_doBlockQuotes_callback2'), $bq);
1331
-
1332
- return "\n". $this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n";
1333
- }
1334
- function _doBlockQuotes_callback2($matches) {
1335
- $pre = $matches[1];
1336
- $pre = preg_replace('/^ /m', '', $pre);
1337
- return $pre;
1338
- }
1339
-
1340
-
1341
- function formParagraphs($text) {
1342
- #
1343
- # Params:
1344
- # $text - string to process with html <p> tags
1345
- #
1346
- # Strip leading and trailing lines:
1347
- $text = preg_replace('/\A\n+|\n+\z/', '', $text);
1348
-
1349
- $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
1350
-
1351
- #
1352
- # Wrap <p> tags and unhashify HTML blocks
1353
- #
1354
- foreach ($grafs as $key => $value) {
1355
- if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
1356
- # Is a paragraph.
1357
- $value = $this->runSpanGamut($value);
1358
- $value = preg_replace('/^([ ]*)/', "<p>", $value);
1359
- $value .= "</p>";
1360
- $grafs[$key] = $this->unhash($value);
1361
- }
1362
- else {
1363
- # Is a block.
1364
- # Modify elements of @grafs in-place...
1365
- $graf = $value;
1366
- $block = $this->html_hashes[$graf];
1367
- $graf = $block;
1368
- // if (preg_match('{
1369
- // \A
1370
- // ( # $1 = <div> tag
1371
- // <div \s+
1372
- // [^>]*
1373
- // \b
1374
- // markdown\s*=\s* ([\'"]) # $2 = attr quote char
1375
- // 1
1376
- // \2
1377
- // [^>]*
1378
- // >
1379
- // )
1380
- // ( # $3 = contents
1381
- // .*
1382
- // )
1383
- // (</div>) # $4 = closing tag
1384
- // \z
1385
- // }xs', $block, $matches))
1386
- // {
1387
- // list(, $div_open, , $div_content, $div_close) = $matches;
1388
- //
1389
- // # We can't call Markdown(), because that resets the hash;
1390
- // # that initialization code should be pulled into its own sub, though.
1391
- // $div_content = $this->hashHTMLBlocks($div_content);
1392
- //
1393
- // # Run document gamut methods on the content.
1394
- // foreach ($this->document_gamut as $method => $priority) {
1395
- // $div_content = $this->$method($div_content);
1396
- // }
1397
- //
1398
- // $div_open = preg_replace(
1399
- // '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open);
1400
- //
1401
- // $graf = $div_open . "\n" . $div_content . "\n" . $div_close;
1402
- // }
1403
- $grafs[$key] = $graf;
1404
- }
1405
- }
1406
-
1407
- return implode("\n\n", $grafs);
1408
- }
1409
-
1410
-
1411
- function encodeAttribute($text) {
1412
- #
1413
- # Encode text for a double-quoted HTML attribute. This function
1414
- # is *not* suitable for attributes enclosed in single quotes.
1415
- #
1416
- $text = $this->encodeAmpsAndAngles($text);
1417
- $text = str_replace('"', '&quot;', $text);
1418
- return $text;
1419
- }
1420
-
1421
-
1422
- function encodeAmpsAndAngles($text) {
1423
- #
1424
- # Smart processing for ampersands and angle brackets that need to
1425
- # be encoded. Valid character entities are left alone unless the
1426
- # no-entities mode is set.
1427
- #
1428
- if ($this->no_entities) {
1429
- $text = str_replace('&', '&amp;', $text);
1430
- } else {
1431
- # Ampersand-encoding based entirely on Nat Irons's Amputator
1432
- # MT plugin: <http://bumppo.net/projects/amputator/>
1433
- $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/',
1434
- '&amp;', $text);;
1435
- }
1436
- # Encode remaining <'s
1437
- $text = str_replace('<', '&lt;', $text);
1438
-
1439
- return $text;
1440
- }
1441
-
1442
-
1443
- function doAutoLinks($text) {
1444
- $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i',
1445
- array(&$this, '_doAutoLinks_url_callback'), $text);
1446
-
1447
- # Email addresses: <address@domain.foo>
1448
- $text = preg_replace_callback('{
1449
- <
1450
- (?:mailto:)?
1451
- (
1452
- (?:
1453
- [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+
1454
- |
1455
- ".*?"
1456
- )
1457
- \@
1458
- (?:
1459
- [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+
1460
- |
1461
- \[[\d.a-fA-F:]+\] # IPv4 & IPv6
1462
- )
1463
- )
1464
- >
1465
- }xi',
1466
- array(&$this, '_doAutoLinks_email_callback'), $text);
1467
-
1468
- return $text;
1469
- }
1470
- function _doAutoLinks_url_callback($matches) {
1471
- $url = $this->encodeAttribute($matches[1]);
1472
- $link = "<a href=\"$url\">$url</a>";
1473
- return $this->hashPart($link);
1474
- }
1475
- function _doAutoLinks_email_callback($matches) {
1476
- $address = $matches[1];
1477
- $link = $this->encodeEmailAddress($address);
1478
- return $this->hashPart($link);
1479
- }
1480
-
1481
-
1482
- function encodeEmailAddress($addr) {
1483
- #
1484
- # Input: an email address, e.g. "foo@example.com"
1485
- #
1486
- # Output: the email address as a mailto link, with each character
1487
- # of the address encoded as either a decimal or hex entity, in
1488
- # the hopes of foiling most address harvesting spam bots. E.g.:
1489
- #
1490
- # <p><a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111;
1491
- # &#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111;
1492
- # &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;
1493
- # &#101;&#46;&#x63;&#111;&#x6d;</a></p>
1494
- #
1495
- # Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
1496
- # With some optimizations by Milian Wolff.
1497
- #
1498
- $addr = "mailto:" . $addr;
1499
- $chars = preg_split('/(?<!^)(?!$)/', $addr);
1500
- $seed = (int)abs(crc32($addr) / strlen($addr)); # Deterministic seed.
1501
-
1502
- foreach ($chars as $key => $char) {
1503
- $ord = ord($char);
1504
- # Ignore non-ascii chars.
1505
- if ($ord < 128) {
1506
- $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
1507
- # roughly 10% raw, 45% hex, 45% dec
1508
- # '@' *must* be encoded. I insist.
1509
- if ($r > 90 && $char != '@') /* do nothing */;
1510
- else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';';
1511
- else $chars[$key] = '&#'.$ord.';';
1512
- }
1513
- }
1514
-
1515
- $addr = implode('', $chars);
1516
- $text = implode('', array_slice($chars, 7)); # text without `mailto:`
1517
- $addr = "<a href=\"$addr\">$text</a>";
1518
-
1519
- return $addr;
1520
- }
1521
-
1522
-
1523
- function parseSpan($str) {
1524
- #
1525
- # Take the string $str and parse it into tokens, hashing embeded HTML,
1526
- # escaped characters and handling code spans.
1527
- #
1528
- $output = '';
1529
-
1530
- $span_re = '{
1531
- (
1532
- \\\\'.$this->escape_chars_re.'
1533
- |
1534
- (?<![`\\\\])
1535
- `+ # code span marker
1536
- '.( $this->no_markup ? '' : '
1537
- |
1538
- <!-- .*? --> # comment
1539
- |
1540
- <\?.*?\?> | <%.*?%> # processing instruction
1541
- |
1542
- <[/!$]?[-a-zA-Z0-9:_]+ # regular tags
1543
- (?>
1544
- \s
1545
- (?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
1546
- )?
1547
- >
1548
- ').'
1549
- )
1550
- }xs';
1551
-
1552
- while (1) {
1553
- #
1554
- # Each loop iteration seach for either the next tag, the next
1555
- # openning code span marker, or the next escaped character.
1556
- # Each token is then passed to handleSpanToken.
1557
- #
1558
- $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
1559
-
1560
- # Create token from text preceding tag.
1561
- if ($parts[0] != "") {
1562
- $output .= $parts[0];
1563
- }
1564
-
1565
- # Check if we reach the end.
1566
- if (isset($parts[1])) {
1567
- $output .= $this->handleSpanToken($parts[1], $parts[2]);
1568
- $str = $parts[2];
1569
- }
1570
- else {
1571
- break;
1572
- }
1573
- }
1574
-
1575
- return $output;
1576
- }
1577
-
1578
-
1579
- function handleSpanToken($token, &$str) {
1580
- #
1581
- # Handle $token provided by parseSpan by determining its nature and
1582
- # returning the corresponding value that should replace it.
1583
- #
1584
- switch ($token{0}) {
1585
- case "\\":
1586
- return $this->hashPart("&#". ord($token{1}). ";");
1587
- case "`":
1588
- # Search for end marker in remaining text.
1589
- if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm',
1590
- $str, $matches))
1591
- {
1592
- $str = $matches[2];
1593
- $codespan = $this->makeCodeSpan($matches[1]);
1594
- return $this->hashPart($codespan);
1595
- }
1596
- return $token; // return as text since no ending marker found.
1597
- default:
1598
- return $this->hashPart($token);
1599
- }
1600
- }
1601
-
1602
-
1603
- function outdent($text) {
1604
- #
1605
- # Remove one level of line-leading tabs or spaces
1606
- #
1607
- return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text);
1608
- }
1609
-
1610
-
1611
- # String length function for detab. `_initDetab` will create a function to
1612
- # hanlde UTF-8 if the default function does not exist.
1613
- var $utf8_strlen = 'mb_strlen';
1614
-
1615
- function detab($text) {
1616
- #
1617
- # Replace tabs with the appropriate amount of space.
1618
- #
1619
- # For each line we separate the line in blocks delemited by
1620
- # tab characters. Then we reconstruct every line by adding the
1621
- # appropriate number of space between each blocks.
1622
-
1623
- $text = preg_replace_callback('/^.*\t.*$/m',
1624
- array(&$this, '_detab_callback'), $text);
1625
-
1626
- return $text;
1627
- }
1628
- function _detab_callback($matches) {
1629
- $line = $matches[0];
1630
- $strlen = $this->utf8_strlen; # strlen function for UTF-8.
1631
-
1632
- # Split in blocks.
1633
- $blocks = explode("\t", $line);
1634
- # Add each blocks to the line.
1635
- $line = $blocks[0];
1636
- unset($blocks[0]); # Do not add first block twice.
1637
- foreach ($blocks as $block) {
1638
- # Calculate amount of space, insert spaces, insert block.
1639
- $amount = $this->tab_width -
1640
- $strlen($line, 'UTF-8') % $this->tab_width;
1641
- $line .= str_repeat(" ", $amount) . $block;
1642
- }
1643
- return $line;
1644
- }
1645
- function _initDetab() {
1646
- #
1647
- # Check for the availability of the function in the `utf8_strlen` property
1648
- # (initially `mb_strlen`). If the function is not available, create a
1649
- # function that will loosely count the number of UTF-8 characters with a
1650
- # regular expression.
1651
- #
1652
- if (function_exists($this->utf8_strlen)) return;
1653
- $this->utf8_strlen = create_function('$text', 'return preg_match_all(
1654
- "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/",
1655
- $text, $m);');
1656
- }
1657
-
1658
-
1659
- function unhash($text) {
1660
- #
1661
- # Swap back in all the tags hashed by _HashHTMLBlocks.
1662
- #
1663
- return preg_replace_callback('/(.)\x1A[0-9]+\1/',
1664
- array(&$this, '_unhash_callback'), $text);
1665
- }
1666
- function _unhash_callback($matches) {
1667
- return $this->html_hashes[$matches[0]];
1668
- }
1669
-
1670
- }
1671
-
1672
-
1673
- #
1674
- # Markdown Extra Parser Class
1675
- #
1676
-
1677
- class MarkdownExtra_Parser extends Markdown_Parser {
1678
-
1679
- # Prefix for footnote ids.
1680
- var $fn_id_prefix = "";
1681
-
1682
- # Optional title attribute for footnote links and backlinks.
1683
- var $fn_link_title = MARKDOWN_FN_LINK_TITLE;
1684
- var $fn_backlink_title = MARKDOWN_FN_BACKLINK_TITLE;
1685
-
1686
- # Optional class attribute for footnote links and backlinks.
1687
- var $fn_link_class = MARKDOWN_FN_LINK_CLASS;
1688
- var $fn_backlink_class = MARKDOWN_FN_BACKLINK_CLASS;
1689
-
1690
- # Predefined abbreviations.
1691
- var $predef_abbr = array();
1692
-
1693
-
1694
- function MarkdownExtra_Parser() {
1695
- #
1696
- # Constructor function. Initialize the parser object.
1697
- #
1698
- # Add extra escapable characters before parent constructor
1699
- # initialize the table.
1700
- $this->escape_chars .= ':|';
1701
-
1702
- # Insert extra document, block, and span transformations.
1703
- # Parent constructor will do the sorting.
1704
- $this->document_gamut += array(
1705
- "doFencedCodeBlocks" => 5,
1706
- "stripFootnotes" => 15,
1707
- "stripAbbreviations" => 25,
1708
- "appendFootnotes" => 50,
1709
- );
1710
- $this->block_gamut += array(
1711
- "doFencedCodeBlocks" => 5,
1712
- "doTables" => 15,
1713
- "doDefLists" => 45,
1714
- );
1715
- $this->span_gamut += array(
1716
- "doFootnotes" => 5,
1717
- "doAbbreviations" => 70,
1718
- );
1719
-
1720
- parent::Markdown_Parser();
1721
- }
1722
-
1723
-
1724
- # Extra variables used during extra transformations.
1725
- var $footnotes = array();
1726
- var $footnotes_ordered = array();
1727
- var $abbr_desciptions = array();
1728
- var $abbr_word_re = '';
1729
-
1730
- # Give the current footnote number.
1731
- var $footnote_counter = 1;
1732
-
1733
-
1734
- function setup() {
1735
- #
1736
- # Setting up Extra-specific variables.
1737
- #
1738
- parent::setup();
1739
-
1740
- $this->footnotes = array();
1741
- $this->footnotes_ordered = array();
1742
- $this->abbr_desciptions = array();
1743
- $this->abbr_word_re = '';
1744
- $this->footnote_counter = 1;
1745
-
1746
- foreach ($this->predef_abbr as $abbr_word => $abbr_desc) {
1747
- if ($this->abbr_word_re)
1748
- $this->abbr_word_re .= '|';
1749
- $this->abbr_word_re .= preg_quote($abbr_word);
1750
- $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
1751
- }
1752
- }
1753
-
1754
- function teardown() {
1755
- #
1756
- # Clearing Extra-specific variables.
1757
- #
1758
- $this->footnotes = array();
1759
- $this->footnotes_ordered = array();
1760
- $this->abbr_desciptions = array();
1761
- $this->abbr_word_re = '';
1762
-
1763
- parent::teardown();
1764
- }
1765
-
1766
-
1767
- ### HTML Block Parser ###
1768
-
1769
- # Tags that are always treated as block tags:
1770
- var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend';
1771
-
1772
- # Tags treated as block tags only if the opening tag is alone on it's line:
1773
- var $context_block_tags_re = 'script|noscript|math|ins|del';
1774
-
1775
- # Tags where markdown="1" default to span mode:
1776
- var $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address';
1777
-
1778
- # Tags which must not have their contents modified, no matter where
1779
- # they appear:
1780
- var $clean_tags_re = 'script|math';
1781
-
1782
- # Tags that do not need to be closed.
1783
- var $auto_close_tags_re = 'hr|img';
1784
-
1785
-
1786
- function hashHTMLBlocks($text) {
1787
- #
1788
- # Hashify HTML Blocks and "clean tags".
1789
- #
1790
- # We only want to do this for block-level HTML tags, such as headers,
1791
- # lists, and tables. That's because we still want to wrap <p>s around
1792
- # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
1793
- # phrase emphasis, and spans. The list of tags we're looking for is
1794
- # hard-coded.
1795
- #
1796
- # This works by calling _HashHTMLBlocks_InMarkdown, which then calls
1797
- # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1"
1798
- # attribute is found whitin a tag, _HashHTMLBlocks_InHTML calls back
1799
- # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag.
1800
- # These two functions are calling each other. It's recursive!
1801
- #
1802
- #
1803
- # Call the HTML-in-Markdown hasher.
1804
- #
1805
- list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text);
1806
-
1807
- return $text;
1808
- }
1809
- function _hashHTMLBlocks_inMarkdown($text, $indent = 0,
1810
- $enclosing_tag_re = '', $span = false)
1811
- {
1812
- #
1813
- # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags.
1814
- #
1815
- # * $indent is the number of space to be ignored when checking for code
1816
- # blocks. This is important because if we don't take the indent into
1817
- # account, something like this (which looks right) won't work as expected:
1818
- #
1819
- # <div>
1820
- # <div markdown="1">
1821
- # Hello World. <-- Is this a Markdown code block or text?
1822
- # </div> <-- Is this a Markdown code block or a real tag?
1823
- # <div>
1824
- #
1825
- # If you don't like this, just don't indent the tag on which
1826
- # you apply the markdown="1" attribute.
1827
- #
1828
- # * If $enclosing_tag_re is not empty, stops at the first unmatched closing
1829
- # tag with that name. Nested tags supported.
1830
- #
1831
- # * If $span is true, text inside must treated as span. So any double
1832
- # newline will be replaced by a single newline so that it does not create
1833
- # paragraphs.
1834
- #
1835
- # Returns an array of that form: ( processed text , remaining text )
1836
- #
1837
- if ($text === '') return array('', '');
1838
-
1839
- # Regex to check for the presense of newlines around a block tag.
1840
- $newline_before_re = '/(?:^\n?|\n\n)*$/';
1841
- $newline_after_re =
1842
- '{
1843
- ^ # Start of text following the tag.
1844
- (?>[ ]*<!--.*?-->)? # Optional comment.
1845
- [ ]*\n # Must be followed by newline.
1846
- }xs';
1847
-
1848
- # Regex to match any tag.
1849
- $block_tag_re =
1850
- '{
1851
- ( # $2: Capture hole tag.
1852
- </? # Any opening or closing tag.
1853
- (?> # Tag name.
1854
- '.$this->block_tags_re.' |
1855
- '.$this->context_block_tags_re.' |
1856
- '.$this->clean_tags_re.' |
1857
- (?!\s)'.$enclosing_tag_re.'
1858
- )
1859
- (?:
1860
- (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
1861
- (?>
1862
- ".*?" | # Double quotes (can contain `>`)
1863
- \'.*?\' | # Single quotes (can contain `>`)
1864
- .+? # Anything but quotes and `>`.
1865
- )*?
1866
- )?
1867
- > # End of tag.
1868
- |
1869
- <!-- .*? --> # HTML Comment
1870
- |
1871
- <\?.*?\?> | <%.*?%> # Processing instruction
1872
- |
1873
- <!\[CDATA\[.*?\]\]> # CData Block
1874
- |
1875
- # Code span marker
1876
- `+
1877
- '. ( !$span ? ' # If not in span.
1878
- |
1879
- # Indented code block
1880
- (?: ^[ ]*\n | ^ | \n[ ]*\n )
1881
- [ ]{'.($indent+4).'}[^\n]* \n
1882
- (?>
1883
- (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n
1884
- )*
1885
- |
1886
- # Fenced code block marker
1887
- (?> ^ | \n )
1888
- [ ]{'.($indent).'}~~~+[ ]*\n
1889
- ' : '' ). ' # End (if not is span).
1890
- )
1891
- }xs';
1892
-
1893
-
1894
- $depth = 0; # Current depth inside the tag tree.
1895
- $parsed = ""; # Parsed text that will be returned.
1896
-
1897
- #
1898
- # Loop through every tag until we find the closing tag of the parent
1899
- # or loop until reaching the end of text if no parent tag specified.
1900
- #
1901
- do {
1902
- #
1903
- # Split the text using the first $tag_match pattern found.
1904
- # Text before pattern will be first in the array, text after
1905
- # pattern will be at the end, and between will be any catches made
1906
- # by the pattern.
1907
- #
1908
- $parts = preg_split($block_tag_re, $text, 2,
1909
- PREG_SPLIT_DELIM_CAPTURE);
1910
-
1911
- # If in Markdown span mode, add a empty-string span-level hash
1912
- # after each newline to prevent triggering any block element.
1913
- if ($span) {
1914
- $void = $this->hashPart("", ':');
1915
- $newline = "$void\n";
1916
- $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void;
1917
- }
1918
-
1919
- $parsed .= $parts[0]; # Text before current tag.
1920
-
1921
- # If end of $text has been reached. Stop loop.
1922
- if (count($parts) < 3) {
1923
- $text = "";
1924
- break;
1925
- }
1926
-
1927
- $tag = $parts[1]; # Tag to handle.
1928
- $text = $parts[2]; # Remaining text after current tag.
1929
- $tag_re = preg_quote($tag); # For use in a regular expression.
1930
-
1931
- #
1932
- # Check for: Code span marker
1933
- #
1934
- if ($tag{0} == "`") {
1935
- # Find corresponding end marker.
1936
- $tag_re = preg_quote($tag);
1937
- if (preg_match('{^(?>.+?|\n(?!\n))*?(?<!`)'.$tag_re.'(?!`)}',
1938
- $text, $matches))
1939
- {
1940
- # End marker found: pass text unchanged until marker.
1941
- $parsed .= $tag . $matches[0];
1942
- $text = substr($text, strlen($matches[0]));
1943
- }
1944
- else {
1945
- # Unmatched marker: just skip it.
1946
- $parsed .= $tag;
1947
- }
1948
- }
1949
- #
1950
- # Check for: Indented code block.
1951
- #
1952
- else if ($tag{0} == "\n" || $tag{0} == " ") {
1953
- # Indented code block: pass it unchanged, will be handled
1954
- # later.
1955
- $parsed .= $tag;
1956
- }
1957
- #
1958
- # Check for: Fenced code block marker.
1959
- #
1960
- else if ($tag{0} == "~") {
1961
- # Fenced code block marker: find matching end marker.
1962
- $tag_re = preg_quote(trim($tag));
1963
- if (preg_match('{^(?>.*\n)+?'.$tag_re.' *\n}', $text,
1964
- $matches))
1965
- {
1966
- # End marker found: pass text unchanged until marker.
1967
- $parsed .= $tag . $matches[0];
1968
- $text = substr($text, strlen($matches[0]));
1969
- }
1970
- else {
1971
- # No end marker: just skip it.
1972
- $parsed .= $tag;
1973
- }
1974
- }
1975
- #
1976
- # Check for: Opening Block level tag or
1977
- # Opening Context Block tag (like ins and del)
1978
- # used as a block tag (tag is alone on it's line).
1979
- #
1980
- else if (preg_match('{^<(?:'.$this->block_tags_re.')\b}', $tag) ||
1981
- ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) &&
1982
- preg_match($newline_before_re, $parsed) &&
1983
- preg_match($newline_after_re, $text) )
1984
- )
1985
- {
1986
- # Need to parse tag and following text using the HTML parser.
1987
- list($block_text, $text) =
1988
- $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true);
1989
-
1990
- # Make sure it stays outside of any paragraph by adding newlines.
1991
- $parsed .= "\n\n$block_text\n\n";
1992
- }
1993
- #
1994
- # Check for: Clean tag (like script, math)
1995
- # HTML Comments, processing instructions.
1996
- #
1997
- else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) ||
1998
- $tag{1} == '!' || $tag{1} == '?')
1999
- {
2000
- # Need to parse tag and following text using the HTML parser.
2001
- # (don't check for markdown attribute)
2002
- list($block_text, $text) =
2003
- $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false);
2004
-
2005
- $parsed .= $block_text;
2006
- }
2007
- #
2008
- # Check for: Tag with same name as enclosing tag.
2009
- #
2010
- else if ($enclosing_tag_re !== '' &&
2011
- # Same name as enclosing tag.
2012
- preg_match('{^</?(?:'.$enclosing_tag_re.')\b}', $tag))
2013
- {
2014
- #
2015
- # Increase/decrease nested tag count.
2016
- #
2017
- if ($tag{1} == '/') $depth--;
2018
- else if ($tag{strlen($tag)-2} != '/') $depth++;
2019
-
2020
- if ($depth < 0) {
2021
- #
2022
- # Going out of parent element. Clean up and break so we
2023
- # return to the calling function.
2024
- #
2025
- $text = $tag . $text;
2026
- break;
2027
- }
2028
-
2029
- $parsed .= $tag;
2030
- }
2031
- else {
2032
- $parsed .= $tag;
2033
- }
2034
- } while ($depth >= 0);
2035
-
2036
- return array($parsed, $text);
2037
- }
2038
- function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) {
2039
- #
2040
- # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags.
2041
- #
2042
- # * Calls $hash_method to convert any blocks.
2043
- # * Stops when the first opening tag closes.
2044
- # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed.
2045
- # (it is not inside clean tags)
2046
- #
2047
- # Returns an array of that form: ( processed text , remaining text )
2048
- #
2049
- if ($text === '') return array('', '');
2050
-
2051
- # Regex to match `markdown` attribute inside of a tag.
2052
- $markdown_attr_re = '
2053
- {
2054
- \s* # Eat whitespace before the `markdown` attribute
2055
- markdown
2056
- \s*=\s*
2057
- (?>
2058
- (["\']) # $1: quote delimiter
2059
- (.*?) # $2: attribute value
2060
- \1 # matching delimiter
2061
- |
2062
- ([^\s>]*) # $3: unquoted attribute value
2063
- )
2064
- () # $4: make $3 always defined (avoid warnings)
2065
- }xs';
2066
-
2067
- # Regex to match any tag.
2068
- $tag_re = '{
2069
- ( # $2: Capture hole tag.
2070
- </? # Any opening or closing tag.
2071
- [\w:$]+ # Tag name.
2072
- (?:
2073
- (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
2074
- (?>
2075
- ".*?" | # Double quotes (can contain `>`)
2076
- \'.*?\' | # Single quotes (can contain `>`)
2077
- .+? # Anything but quotes and `>`.
2078
- )*?
2079
- )?
2080
- > # End of tag.
2081
- |
2082
- <!-- .*? --> # HTML Comment
2083
- |
2084
- <\?.*?\?> | <%.*?%> # Processing instruction
2085
- |
2086
- <!\[CDATA\[.*?\]\]> # CData Block
2087
- )
2088
- }xs';
2089
-
2090
- $original_text = $text; # Save original text in case of faliure.
2091
-
2092
- $depth = 0; # Current depth inside the tag tree.
2093
- $block_text = ""; # Temporary text holder for current text.
2094
- $parsed = ""; # Parsed text that will be returned.
2095
-
2096
- #
2097
- # Get the name of the starting tag.
2098
- # (This pattern makes $base_tag_name_re safe without quoting.)
2099
- #
2100
- if (preg_match('/^<([\w:$]*)\b/', $text, $matches))
2101
- $base_tag_name_re = $matches[1];
2102
-
2103
- #
2104
- # Loop through every tag until we find the corresponding closing tag.
2105
- #
2106
- do {
2107
- #
2108
- # Split the text using the first $tag_match pattern found.
2109
- # Text before pattern will be first in the array, text after
2110
- # pattern will be at the end, and between will be any catches made
2111
- # by the pattern.
2112
- #
2113
- $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
2114
-
2115
- if (count($parts) < 3) {
2116
- #
2117
- # End of $text reached with unbalenced tag(s).
2118
- # In that case, we return original text unchanged and pass the
2119
- # first character as filtered to prevent an infinite loop in the
2120
- # parent function.
2121
- #
2122
- return array($original_text{0}, substr($original_text, 1));
2123
- }
2124
-
2125
- $block_text .= $parts[0]; # Text before current tag.
2126
- $tag = $parts[1]; # Tag to handle.
2127
- $text = $parts[2]; # Remaining text after current tag.
2128
-
2129
- #
2130
- # Check for: Auto-close tag (like <hr/>)
2131
- # Comments and Processing Instructions.
2132
- #
2133
- if (preg_match('{^</?(?:'.$this->auto_close_tags_re.')\b}', $tag) ||
2134
- $tag{1} == '!' || $tag{1} == '?')
2135
- {
2136
- # Just add the tag to the block as if it was text.
2137
- $block_text .= $tag;
2138
- }
2139
- else {
2140
- #
2141
- # Increase/decrease nested tag count. Only do so if
2142
- # the tag's name match base tag's.
2143
- #
2144
- if (preg_match('{^</?'.$base_tag_name_re.'\b}', $tag)) {
2145
- if ($tag{1} == '/') $depth--;
2146
- else if ($tag{strlen($tag)-2} != '/') $depth++;
2147
- }
2148
-
2149
- #
2150
- # Check for `markdown="1"` attribute and handle it.
2151
- #
2152
- if ($md_attr &&
2153
- preg_match($markdown_attr_re, $tag, $attr_m) &&
2154
- preg_match('/^1|block|span$/', $attr_m[2] . $attr_m[3]))
2155
- {
2156
- # Remove `markdown` attribute from opening tag.
2157
- $tag = preg_replace($markdown_attr_re, '', $tag);
2158
-
2159
- # Check if text inside this tag must be parsed in span mode.
2160
- $this->mode = $attr_m[2] . $attr_m[3];
2161
- $span_mode = $this->mode == 'span' || $this->mode != 'block' &&
2162
- preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag);
2163
-
2164
- # Calculate indent before tag.
2165
- if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) {
2166
- $strlen = $this->utf8_strlen;
2167
- $indent = $strlen($matches[1], 'UTF-8');
2168
- } else {
2169
- $indent = 0;
2170
- }
2171
-
2172
- # End preceding block with this tag.
2173
- $block_text .= $tag;
2174
- $parsed .= $this->$hash_method($block_text);
2175
-
2176
- # Get enclosing tag name for the ParseMarkdown function.
2177
- # (This pattern makes $tag_name_re safe without quoting.)
2178
- preg_match('/^<([\w:$]*)\b/', $tag, $matches);
2179
- $tag_name_re = $matches[1];
2180
-
2181
- # Parse the content using the HTML-in-Markdown parser.
2182
- list ($block_text, $text)
2183
- = $this->_hashHTMLBlocks_inMarkdown($text, $indent,
2184
- $tag_name_re, $span_mode);
2185
-
2186
- # Outdent markdown text.
2187
- if ($indent > 0) {
2188
- $block_text = preg_replace("/^[ ]{1,$indent}/m", "",
2189
- $block_text);
2190
- }
2191
-
2192
- # Append tag content to parsed text.
2193
- if (!$span_mode) $parsed .= "\n\n$block_text\n\n";
2194
- else $parsed .= "$block_text";
2195
-
2196
- # Start over a new block.
2197
- $block_text = "";
2198
- }
2199
- else $block_text .= $tag;
2200
- }
2201
-
2202
- } while ($depth > 0);
2203
-
2204
- #
2205
- # Hash last block text that wasn't processed inside the loop.
2206
- #
2207
- $parsed .= $this->$hash_method($block_text);
2208
-
2209
- return array($parsed, $text);
2210
- }
2211
-
2212
-
2213
- function hashClean($text) {
2214
- #
2215
- # Called whenever a tag must be hashed when a function insert a "clean" tag
2216
- # in $text, it pass through this function and is automaticaly escaped,
2217
- # blocking invalid nested overlap.
2218
- #
2219
- return $this->hashPart($text, 'C');
2220
- }
2221
-
2222
-
2223
- function doHeaders($text) {
2224
- #
2225
- # Redefined to add id attribute support.
2226
- #
2227
- # Setext-style headers:
2228
- # Header 1 {#header1}
2229
- # ========
2230
- #
2231
- # Header 2 {#header2}
2232
- # --------
2233
- #
2234
- $text = preg_replace_callback(
2235
- '{
2236
- (^.+?) # $1: Header text
2237
- (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # $2: Id attribute
2238
- [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer
2239
- }mx',
2240
- array(&$this, '_doHeaders_callback_setext'), $text);
2241
-
2242
- # atx-style headers:
2243
- # # Header 1 {#header1}
2244
- # ## Header 2 {#header2}
2245
- # ## Header 2 with closing hashes ## {#header3}
2246
- # ...
2247
- # ###### Header 6 {#header2}
2248
- #
2249
- $text = preg_replace_callback('{
2250
- ^(\#{1,6}) # $1 = string of #\'s
2251
- [ ]*
2252
- (.+?) # $2 = Header text
2253
- [ ]*
2254
- \#* # optional closing #\'s (not counted)
2255
- (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # id attribute
2256
- [ ]*
2257
- \n+
2258
- }xm',
2259
- array(&$this, '_doHeaders_callback_atx'), $text);
2260
-
2261
- return $text;
2262
- }
2263
- function _doHeaders_attr($attr) {
2264
- if (empty($attr)) return "";
2265
- return " id=\"$attr\"";
2266
- }
2267
- function _doHeaders_callback_setext($matches) {
2268
- if ($matches[3] == '-' && preg_match('{^- }', $matches[1]))
2269
- return $matches[0];
2270
- $level = $matches[3]{0} == '=' ? 1 : 2;
2271
- $attr = $this->_doHeaders_attr($id =& $matches[2]);
2272
- $block = "<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>";
2273
- return "\n" . $this->hashBlock($block) . "\n\n";
2274
- }
2275
- function _doHeaders_callback_atx($matches) {
2276
- $level = strlen($matches[1]);
2277
- $attr = $this->_doHeaders_attr($id =& $matches[3]);
2278
- $block = "<h$level$attr>".$this->runSpanGamut($matches[2])."</h$level>";
2279
- return "\n" . $this->hashBlock($block) . "\n\n";
2280
- }
2281
-
2282
-
2283
- function doTables($text) {
2284
- #
2285
- # Form HTML tables.
2286
- #
2287
- $less_than_tab = $this->tab_width - 1;
2288
- #
2289
- # Find tables with leading pipe.
2290
- #
2291
- # | Header 1 | Header 2
2292
- # | -------- | --------
2293
- # | Cell 1 | Cell 2
2294
- # | Cell 3 | Cell 4
2295
- #
2296
- $text = preg_replace_callback('
2297
- {
2298
- ^ # Start of a line
2299
- [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2300
- [|] # Optional leading pipe (present)
2301
- (.+) \n # $1: Header row (at least one pipe)
2302
-
2303
- [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2304
- [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline
2305
-
2306
- ( # $3: Cells
2307
- (?>
2308
- [ ]* # Allowed whitespace.
2309
- [|] .* \n # Row content.
2310
- )*
2311
- )
2312
- (?=\n|\Z) # Stop at final double newline.
2313
- }xm',
2314
- array(&$this, '_doTable_leadingPipe_callback'), $text);
2315
-
2316
- #
2317
- # Find tables without leading pipe.
2318
- #
2319
- # Header 1 | Header 2
2320
- # -------- | --------
2321
- # Cell 1 | Cell 2
2322
- # Cell 3 | Cell 4
2323
- #
2324
- $text = preg_replace_callback('
2325
- {
2326
- ^ # Start of a line
2327
- [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2328
- (\S.*[|].*) \n # $1: Header row (at least one pipe)
2329
-
2330
- [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2331
- ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline
2332
-
2333
- ( # $3: Cells
2334
- (?>
2335
- .* [|] .* \n # Row content
2336
- )*
2337
- )
2338
- (?=\n|\Z) # Stop at final double newline.
2339
- }xm',
2340
- array(&$this, '_DoTable_callback'), $text);
2341
-
2342
- return $text;
2343
- }
2344
- function _doTable_leadingPipe_callback($matches) {
2345
- $head = $matches[1];
2346
- $underline = $matches[2];
2347
- $content = $matches[3];
2348
-
2349
- # Remove leading pipe for each row.
2350
- $content = preg_replace('/^ *[|]/m', '', $content);
2351
-
2352
- return $this->_doTable_callback(array($matches[0], $head, $underline, $content));
2353
- }
2354
- function _doTable_callback($matches) {
2355
- $head = $matches[1];
2356
- $underline = $matches[2];
2357
- $content = $matches[3];
2358
-
2359
- # Remove any tailing pipes for each line.
2360
- $head = preg_replace('/[|] *$/m', '', $head);
2361
- $underline = preg_replace('/[|] *$/m', '', $underline);
2362
- $content = preg_replace('/[|] *$/m', '', $content);
2363
-
2364
- # Reading alignement from header underline.
2365
- $separators = preg_split('/ *[|] */', $underline);
2366
- foreach ($separators as $n => $s) {
2367
- if (preg_match('/^ *-+: *$/', $s)) $attr[$n] = ' align="right"';
2368
- else if (preg_match('/^ *:-+: *$/', $s))$attr[$n] = ' align="center"';
2369
- else if (preg_match('/^ *:-+ *$/', $s)) $attr[$n] = ' align="left"';
2370
- else $attr[$n] = '';
2371
- }
2372
-
2373
- # Parsing span elements, including code spans, character escapes,
2374
- # and inline HTML tags, so that pipes inside those gets ignored.
2375
- $head = $this->parseSpan($head);
2376
- $headers = preg_split('/ *[|] */', $head);
2377
- $col_count = count($headers);
2378
-
2379
- # Write column headers.
2380
- $text = "<table>\n";
2381
- $text .= "<thead>\n";
2382
- $text .= "<tr>\n";
2383
- foreach ($headers as $n => $header)
2384
- $text .= " <th$attr[$n]>".$this->runSpanGamut(trim($header))."</th>\n";
2385
- $text .= "</tr>\n";
2386
- $text .= "</thead>\n";
2387
-
2388
- # Split content by row.
2389
- $rows = explode("\n", trim($content, "\n"));
2390
-
2391
- $text .= "<tbody>\n";
2392
- foreach ($rows as $row) {
2393
- # Parsing span elements, including code spans, character escapes,
2394
- # and inline HTML tags, so that pipes inside those gets ignored.
2395
- $row = $this->parseSpan($row);
2396
-
2397
- # Split row by cell.
2398
- $row_cells = preg_split('/ *[|] */', $row, $col_count);
2399
- $row_cells = array_pad($row_cells, $col_count, '');
2400
-
2401
- $text .= "<tr>\n";
2402
- foreach ($row_cells as $n => $cell)
2403
- $text .= " <td$attr[$n]>".$this->runSpanGamut(trim($cell))."</td>\n";
2404
- $text .= "</tr>\n";
2405
- }
2406
- $text .= "</tbody>\n";
2407
- $text .= "</table>";
2408
-
2409
- return $this->hashBlock($text) . "\n";
2410
- }
2411
-
2412
-
2413
- function doDefLists($text) {
2414
- #
2415
- # Form HTML definition lists.
2416
- #
2417
- $less_than_tab = $this->tab_width - 1;
2418
-
2419
- # Re-usable pattern to match any entire dl list:
2420
- $whole_list_re = '(?>
2421
- ( # $1 = whole list
2422
- ( # $2
2423
- [ ]{0,'.$less_than_tab.'}
2424
- ((?>.*\S.*\n)+) # $3 = defined term
2425
- \n?
2426
- [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2427
- )
2428
- (?s:.+?)
2429
- ( # $4
2430
- \z
2431
- |
2432
- \n{2,}
2433
- (?=\S)
2434
- (?! # Negative lookahead for another term
2435
- [ ]{0,'.$less_than_tab.'}
2436
- (?: \S.*\n )+? # defined term
2437
- \n?
2438
- [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2439
- )
2440
- (?! # Negative lookahead for another definition
2441
- [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2442
- )
2443
- )
2444
- )
2445
- )'; // mx
2446
-
2447
- $text = preg_replace_callback('{
2448
- (?>\A\n?|(?<=\n\n))
2449
- '.$whole_list_re.'
2450
- }mx',
2451
- array(&$this, '_doDefLists_callback'), $text);
2452
-
2453
- return $text;
2454
- }
2455
- function _doDefLists_callback($matches) {
2456
- # Re-usable patterns to match list item bullets and number markers:
2457
- $list = $matches[1];
2458
-
2459
- # Turn double returns into triple returns, so that we can make a
2460
- # paragraph for the last item in a list, if necessary:
2461
- $result = trim($this->processDefListItems($list));
2462
- $result = "<dl>\n" . $result . "\n</dl>";
2463
- return $this->hashBlock($result) . "\n\n";
2464
- }
2465
-
2466
-
2467
- function processDefListItems($list_str) {
2468
- #
2469
- # Process the contents of a single definition list, splitting it
2470
- # into individual term and definition list items.
2471
- #
2472
- $less_than_tab = $this->tab_width - 1;
2473
-
2474
- # trim trailing blank lines:
2475
- $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
2476
-
2477
- # Process definition terms.
2478
- $list_str = preg_replace_callback('{
2479
- (?>\A\n?|\n\n+) # leading line
2480
- ( # definition terms = $1
2481
- [ ]{0,'.$less_than_tab.'} # leading whitespace
2482
- (?![:][ ]|[ ]) # negative lookahead for a definition
2483
- # mark (colon) or more whitespace.
2484
- (?> \S.* \n)+? # actual term (not whitespace).
2485
- )
2486
- (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed
2487
- # with a definition mark.
2488
- }xm',
2489
- array(&$this, '_processDefListItems_callback_dt'), $list_str);
2490
-
2491
- # Process actual definitions.
2492
- $list_str = preg_replace_callback('{
2493
- \n(\n+)? # leading line = $1
2494
- ( # marker space = $2
2495
- [ ]{0,'.$less_than_tab.'} # whitespace before colon
2496
- [:][ ]+ # definition mark (colon)
2497
- )
2498
- ((?s:.+?)) # definition text = $3
2499
- (?= \n+ # stop at next definition mark,
2500
- (?: # next term or end of text
2501
- [ ]{0,'.$less_than_tab.'} [:][ ] |
2502
- <dt> | \z
2503
- )
2504
- )
2505
- }xm',
2506
- array(&$this, '_processDefListItems_callback_dd'), $list_str);
2507
-
2508
- return $list_str;
2509
- }
2510
- function _processDefListItems_callback_dt($matches) {
2511
- $terms = explode("\n", trim($matches[1]));
2512
- $text = '';
2513
- foreach ($terms as $term) {
2514
- $term = $this->runSpanGamut(trim($term));
2515
- $text .= "\n<dt>" . $term . "</dt>";
2516
- }
2517
- return $text . "\n";
2518
- }
2519
- function _processDefListItems_callback_dd($matches) {
2520
- $leading_line = $matches[1];
2521
- $marker_space = $matches[2];
2522
- $def = $matches[3];
2523
-
2524
- if ($leading_line || preg_match('/\n{2,}/', $def)) {
2525
- # Replace marker with the appropriate whitespace indentation
2526
- $def = str_repeat(' ', strlen($marker_space)) . $def;
2527
- $def = $this->runBlockGamut($this->outdent($def . "\n\n"));
2528
- $def = "\n". $def ."\n";
2529
- }
2530
- else {
2531
- $def = rtrim($def);
2532
- $def = $this->runSpanGamut($this->outdent($def));
2533
- }
2534
-
2535
- return "\n<dd>" . $def . "</dd>\n";
2536
- }
2537
-
2538
-
2539
- function doFencedCodeBlocks($text) {
2540
- #
2541
- # Adding the fenced code block syntax to regular Markdown:
2542
- #
2543
- # ~~~
2544
- # Code block
2545
- # ~~~
2546
- #
2547
- $less_than_tab = $this->tab_width;
2548
-
2549
- $text = preg_replace_callback('{
2550
- (?:\n|\A)
2551
- # 1: Opening marker
2552
- (
2553
- ~{3,} # Marker: three tilde or more.
2554
- )
2555
- [ ]* \n # Whitespace and newline following marker.
2556
-
2557
- # 2: Content
2558
- (
2559
- (?>
2560
- (?!\1 [ ]* \n) # Not a closing marker.
2561
- .*\n+
2562
- )+
2563
- )
2564
-
2565
- # Closing marker.
2566
- \1 [ ]* \n
2567
- }xm',
2568
- array(&$this, '_doFencedCodeBlocks_callback'), $text);
2569
-
2570
- return $text;
2571
- }
2572
- function _doFencedCodeBlocks_callback($matches) {
2573
- $codeblock = $matches[2];
2574
- $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
2575
- $codeblock = preg_replace_callback('/^\n+/',
2576
- array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock);
2577
- $codeblock = "<pre><code>$codeblock</code></pre>";
2578
- return "\n\n".$this->hashBlock($codeblock)."\n\n";
2579
- }
2580
- function _doFencedCodeBlocks_newlines($matches) {
2581
- return str_repeat("<br$this->empty_element_suffix",
2582
- strlen($matches[0]));
2583
- }
2584
-
2585
-
2586
- #
2587
- # Redefining emphasis markers so that emphasis by underscore does not
2588
- # work in the middle of a word.
2589
- #
2590
- var $em_relist = array(
2591
- '' => '(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?=\S|$)(?![.,:;]\s)',
2592
- '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
2593
- '_' => '(?<=\S|^)(?<!_)_(?![a-zA-Z0-9_])',
2594
- );
2595
- var $strong_relist = array(
2596
- '' => '(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?=\S|$)(?![.,:;]\s)',
2597
- '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
2598
- '__' => '(?<=\S|^)(?<!_)__(?![a-zA-Z0-9_])',
2599
- );
2600
- var $em_strong_relist = array(
2601
- '' => '(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?=\S|$)(?![.,:;]\s)',
2602
- '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
2603
- '___' => '(?<=\S|^)(?<!_)___(?![a-zA-Z0-9_])',
2604
- );
2605
-
2606
-
2607
- function formParagraphs($text) {
2608
- #
2609
- # Params:
2610
- # $text - string to process with html <p> tags
2611
- #
2612
- # Strip leading and trailing lines:
2613
- $text = preg_replace('/\A\n+|\n+\z/', '', $text);
2614
-
2615
- $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
2616
-
2617
- #
2618
- # Wrap <p> tags and unhashify HTML blocks
2619
- #
2620
- foreach ($grafs as $key => $value) {
2621
- $value = trim($this->runSpanGamut($value));
2622
-
2623
- # Check if this should be enclosed in a paragraph.
2624
- # Clean tag hashes & block tag hashes are left alone.
2625
- $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value);
2626
-
2627
- if ($is_p) {
2628
- $value = "<p>$value</p>";
2629
- }
2630
- $grafs[$key] = $value;
2631
- }
2632
-
2633
- # Join grafs in one text, then unhash HTML tags.
2634
- $text = implode("\n\n", $grafs);
2635
-
2636
- # Finish by removing any tag hashes still present in $text.
2637
- $text = $this->unhash($text);
2638
-
2639
- return $text;
2640
- }
2641
-
2642
-
2643
- ### Footnotes
2644
-
2645
- function stripFootnotes($text) {
2646
- #
2647
- # Strips link definitions from text, stores the URLs and titles in
2648
- # hash references.
2649
- #
2650
- $less_than_tab = $this->tab_width - 1;
2651
-
2652
- # Link defs are in the form: [^id]: url "optional title"
2653
- $text = preg_replace_callback('{
2654
- ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1
2655
- [ ]*
2656
- \n? # maybe *one* newline
2657
- ( # text = $2 (no blank lines allowed)
2658
- (?:
2659
- .+ # actual text
2660
- |
2661
- \n # newlines but
2662
- (?!\[\^.+?\]:\s)# negative lookahead for footnote marker.
2663
- (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed
2664
- # by non-indented content
2665
- )*
2666
- )
2667
- }xm',
2668
- array(&$this, '_stripFootnotes_callback'),
2669
- $text);
2670
- return $text;
2671
- }
2672
- function _stripFootnotes_callback($matches) {
2673
- $note_id = $this->fn_id_prefix . $matches[1];
2674
- $this->footnotes[$note_id] = $this->outdent($matches[2]);
2675
- return ''; # String that will replace the block
2676
- }
2677
-
2678
-
2679
- function doFootnotes($text) {
2680
- #
2681
- # Replace footnote references in $text [^id] with a special text-token
2682
- # which will be replaced by the actual footnote marker in appendFootnotes.
2683
- #
2684
- if (!$this->in_anchor) {
2685
- $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text);
2686
- }
2687
- return $text;
2688
- }
2689
-
2690
-
2691
- function appendFootnotes($text) {
2692
- #
2693
- # Append footnote list to text.
2694
- #
2695
- $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
2696
- array(&$this, '_appendFootnotes_callback'), $text);
2697
-
2698
- if (!empty($this->footnotes_ordered)) {
2699
- $text .= "\n\n";
2700
- $text .= "<div class=\"footnotes\">\n";
2701
- $text .= "<hr". $this->empty_element_suffix ."\n";
2702
- $text .= "<ol>\n\n";
2703
-
2704
- $attr = " rev=\"footnote\"";
2705
- if ($this->fn_backlink_class != "") {
2706
- $class = $this->fn_backlink_class;
2707
- $class = $this->encodeAttribute($class);
2708
- $attr .= " class=\"$class\"";
2709
- }
2710
- if ($this->fn_backlink_title != "") {
2711
- $title = $this->fn_backlink_title;
2712
- $title = $this->encodeAttribute($title);
2713
- $attr .= " title=\"$title\"";
2714
- }
2715
- $num = 0;
2716
-
2717
- while (!empty($this->footnotes_ordered)) {
2718
- $footnote = reset($this->footnotes_ordered);
2719
- $note_id = key($this->footnotes_ordered);
2720
- unset($this->footnotes_ordered[$note_id]);
2721
-
2722
- $footnote .= "\n"; # Need to append newline before parsing.
2723
- $footnote = $this->runBlockGamut("$footnote\n");
2724
- $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
2725
- array(&$this, '_appendFootnotes_callback'), $footnote);
2726
-
2727
- $attr = str_replace("%%", ++$num, $attr);
2728
- $note_id = $this->encodeAttribute($note_id);
2729
-
2730
- # Add backlink to last paragraph; create new paragraph if needed.
2731
- $backlink = "<a href=\"#fnref:$note_id\"$attr>&#8617;</a>";
2732
- if (preg_match('{</p>$}', $footnote)) {
2733
- $footnote = substr($footnote, 0, -4) . "&#160;$backlink</p>";
2734
- } else {
2735
- $footnote .= "\n\n<p>$backlink</p>";
2736
- }
2737
-
2738
- $text .= "<li id=\"fn:$note_id\">\n";
2739
- $text .= $footnote . "\n";
2740
- $text .= "</li>\n\n";
2741
- }
2742
-
2743
- $text .= "</ol>\n";
2744
- $text .= "</div>";
2745
- }
2746
- return $text;
2747
- }
2748
- function _appendFootnotes_callback($matches) {
2749
- $node_id = $this->fn_id_prefix . $matches[1];
2750
-
2751
- # Create footnote marker only if it has a corresponding footnote *and*
2752
- # the footnote hasn't been used by another marker.
2753
- if (isset($this->footnotes[$node_id])) {
2754
- # Transfert footnote content to the ordered list.
2755
- $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id];
2756
- unset($this->footnotes[$node_id]);
2757
-
2758
- $num = $this->footnote_counter++;
2759
- $attr = " rel=\"footnote\"";
2760
- if ($this->fn_link_class != "") {
2761
- $class = $this->fn_link_class;
2762
- $class = $this->encodeAttribute($class);
2763
- $attr .= " class=\"$class\"";
2764
- }
2765
- if ($this->fn_link_title != "") {
2766
- $title = $this->fn_link_title;
2767
- $title = $this->encodeAttribute($title);
2768
- $attr .= " title=\"$title\"";
2769
- }
2770
-
2771
- $attr = str_replace("%%", $num, $attr);
2772
- $node_id = $this->encodeAttribute($node_id);
2773
-
2774
- return
2775
- "<sup id=\"fnref:$node_id\">".
2776
- "<a href=\"#fn:$node_id\"$attr>$num</a>".
2777
- "</sup>";
2778
- }
2779
-
2780
- return "[^".$matches[1]."]";
2781
- }
2782
-
2783
-
2784
- ### Abbreviations ###
2785
-
2786
- function stripAbbreviations($text) {
2787
- #
2788
- # Strips abbreviations from text, stores titles in hash references.
2789
- #
2790
- $less_than_tab = $this->tab_width - 1;
2791
-
2792
- # Link defs are in the form: [id]*: url "optional title"
2793
- $text = preg_replace_callback('{
2794
- ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1
2795
- (.*) # text = $2 (no blank lines allowed)
2796
- }xm',
2797
- array(&$this, '_stripAbbreviations_callback'),
2798
- $text);
2799
- return $text;
2800
- }
2801
- function _stripAbbreviations_callback($matches) {
2802
- $abbr_word = $matches[1];
2803
- $abbr_desc = $matches[2];
2804
- if ($this->abbr_word_re)
2805
- $this->abbr_word_re .= '|';
2806
- $this->abbr_word_re .= preg_quote($abbr_word);
2807
- $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
2808
- return ''; # String that will replace the block
2809
- }
2810
-
2811
-
2812
- function doAbbreviations($text) {
2813
- #
2814
- # Find defined abbreviations in text and wrap them in <abbr> elements.
2815
- #
2816
- if ($this->abbr_word_re) {
2817
- // cannot use the /x modifier because abbr_word_re may
2818
- // contain significant spaces:
2819
- $text = preg_replace_callback('{'.
2820
- '(?<![\w\x1A])'.
2821
- '(?:'.$this->abbr_word_re.')'.
2822
- '(?![\w\x1A])'.
2823
- '}',
2824
- array(&$this, '_doAbbreviations_callback'), $text);
2825
- }
2826
- return $text;
2827
- }
2828
- function _doAbbreviations_callback($matches) {
2829
- $abbr = $matches[0];
2830
- if (isset($this->abbr_desciptions[$abbr])) {
2831
- $desc = $this->abbr_desciptions[$abbr];
2832
- if (empty($desc)) {
2833
- return $this->hashPart("<abbr>$abbr</abbr>");
2834
- } else {
2835
- $desc = $this->encodeAttribute($desc);
2836
- return $this->hashPart("<abbr title=\"$desc\">$abbr</abbr>");
2837
- }
2838
- } else {
2839
- return $matches[0];
2840
- }
2841
- }
2842
-
2843
- }
2844
-
2845
-
2846
- /*
2847
-
2848
- PHP Markdown Extra
2849
- ==================
2850
-
2851
- Description
2852
- -----------
2853
-
2854
- This is a PHP port of the original Markdown formatter written in Perl
2855
- by John Gruber. This special "Extra" version of PHP Markdown features
2856
- further enhancements to the syntax for making additional constructs
2857
- such as tables and definition list.
2858
-
2859
- Markdown is a text-to-HTML filter; it translates an easy-to-read /
2860
- easy-to-write structured text format into HTML. Markdown's text format
2861
- is most similar to that of plain text email, and supports features such
2862
- as headers, *emphasis*, code blocks, blockquotes, and links.
2863
-
2864
- Markdown's syntax is designed not as a generic markup language, but
2865
- specifically to serve as a front-end to (X)HTML. You can use span-level
2866
- HTML tags anywhere in a Markdown document, and you can use block level
2867
- HTML tags (like <div> and <table> as well).
2868
-
2869
- For more information about Markdown's syntax, see:
2870
-
2871
- <http://daringfireball.net/projects/markdown/>
2872
-
2873
-
2874
- Bugs
2875
- ----
2876
-
2877
- To file bug reports please send email to:
2878
-
2879
- <michel.fortin@michelf.com>
2880
-
2881
- Please include with your report: (1) the example input; (2) the output you
2882
- expected; (3) the output Markdown actually produced.
2883
-
2884
-
2885
- Version History
2886
- ---------------
2887
-
2888
- See the readme file for detailed release notes for this version.
2889
-
2890
-
2891
- Copyright and License
2892
- ---------------------
2893
-
2894
- PHP Markdown & Extra
2895
- Copyright (c) 2004-2009 Michel Fortin
2896
- <http://michelf.com/>
2897
- All rights reserved.
2898
-
2899
- Based on Markdown
2900
- Copyright (c) 2003-2006 John Gruber
2901
- <http://daringfireball.net/>
2902
- All rights reserved.
2903
-
2904
- Redistribution and use in source and binary forms, with or without
2905
- modification, are permitted provided that the following conditions are
2906
- met:
2907
-
2908
- * Redistributions of source code must retain the above copyright notice,
2909
- this list of conditions and the following disclaimer.
2910
-
2911
- * Redistributions in binary form must reproduce the above copyright
2912
- notice, this list of conditions and the following disclaimer in the
2913
- documentation and/or other materials provided with the distribution.
2914
-
2915
- * Neither the name "Markdown" nor the names of its contributors may
2916
- be used to endorse or promote products derived from this software
2917
- without specific prior written permission.
2918
-
2919
- This software is provided by the copyright holders and contributors "as
2920
- is" and any express or implied warranties, including, but not limited
2921
- to, the implied warranties of merchantability and fitness for a
2922
- particular purpose are disclaimed. In no event shall the copyright owner
2923
- or contributors be liable for any direct, indirect, incidental, special,
2924
- exemplary, or consequential damages (including, but not limited to,
2925
- procurement of substitute goods or services; loss of use, data, or
2926
- profits; or business interruption) however caused and on any theory of
2927
- liability, whether in contract, strict liability, or tort (including
2928
- negligence or otherwise) arising in any way out of the use of this
2929
- software, even if advised of the possibility of such damage.
2930
-
2931
- */
2932
  ?>
1
+ <?php
2
+ #
3
+ # Markdown Extra - A text-to-HTML conversion tool for web writers
4
+ #
5
+ # PHP Markdown & Extra
6
+ # Copyright (c) 2004-2009 Michel Fortin
7
+ # <http://michelf.com/projects/php-markdown/>
8
+ #
9
+ # Original Markdown
10
+ # Copyright (c) 2004-2006 John Gruber
11
+ # <http://daringfireball.net/projects/markdown/>
12
+ #
13
+
14
+
15
+ define( 'MARKDOWN_VERSION', "1.0.1n" ); # Sat 10 Oct 2009
16
+ define( 'MARKDOWNEXTRA_VERSION', "1.2.4" ); # Sat 10 Oct 2009
17
+
18
+
19
+ #
20
+ # Global default settings:
21
+ #
22
+
23
+ # Change to ">" for HTML output
24
+ @define( 'MARKDOWN_EMPTY_ELEMENT_SUFFIX', " />");
25
+
26
+ # Define the width of a tab for code blocks.
27
+ @define( 'MARKDOWN_TAB_WIDTH', 4 );
28
+
29
+ # Optional title attribute for footnote links and backlinks.
30
+ @define( 'MARKDOWN_FN_LINK_TITLE', "" );
31
+ @define( 'MARKDOWN_FN_BACKLINK_TITLE', "" );
32
+
33
+ # Optional class attribute for footnote links and backlinks.
34
+ @define( 'MARKDOWN_FN_LINK_CLASS', "" );
35
+ @define( 'MARKDOWN_FN_BACKLINK_CLASS', "" );
36
+
37
+
38
+ #
39
+ # WordPress settings:
40
+ #
41
+
42
+ # Change to false to remove Markdown from posts and/or comments.
43
+ @define( 'MARKDOWN_WP_POSTS', true );
44
+ @define( 'MARKDOWN_WP_COMMENTS', true );
45
+
46
+
47
+
48
+ ### Standard Function Interface ###
49
+
50
+ @define( 'MARKDOWN_PARSER_CLASS', 'MarkdownExtra_Parser' );
51
+
52
+ function Markdown($text) {
53
+ #
54
+ # Initialize the parser and return the result of its transform method.
55
+ #
56
+ # Setup static parser variable.
57
+ static $parser;
58
+ if (!isset($parser)) {
59
+ $parser_class = MARKDOWN_PARSER_CLASS;
60
+ $parser = new $parser_class;
61
+ }
62
+
63
+ # Transform text using parser.
64
+ return $parser->transform($text);
65
+ }
66
+
67
+
68
+ ### WordPress Plugin Interface ###
69
+
70
+ /*
71
+ Plugin Name: Markdown Extra
72
+ Plugin URI: http://michelf.com/projects/php-markdown/
73
+ Description: <a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.com/projects/php-markdown/">More...</a>
74
+ Version: 1.2.4
75
+ Author: Michel Fortin
76
+ Author URI: http://michelf.com/
77
+ */
78
+
79
+ if (isset($wp_version)) {
80
+ # More details about how it works here:
81
+ # <http://michelf.com/weblog/2005/wordpress-text-flow-vs-markdown/>
82
+
83
+ # Post content and excerpts
84
+ # - Remove WordPress paragraph generator.
85
+ # - Run Markdown on excerpt, then remove all tags.
86
+ # - Add paragraph tag around the excerpt, but remove it for the excerpt rss.
87
+ if (MARKDOWN_WP_POSTS) {
88
+ remove_filter('the_content', 'wpautop');
89
+ remove_filter('the_content_rss', 'wpautop');
90
+ remove_filter('the_excerpt', 'wpautop');
91
+ add_filter('the_content', 'mdwp_MarkdownPost', 6);
92
+ add_filter('the_content_rss', 'mdwp_MarkdownPost', 6);
93
+ add_filter('get_the_excerpt', 'mdwp_MarkdownPost', 6);
94
+ add_filter('get_the_excerpt', 'trim', 7);
95
+ add_filter('the_excerpt', 'mdwp_add_p');
96
+ add_filter('the_excerpt_rss', 'mdwp_strip_p');
97
+
98
+ remove_filter('content_save_pre', 'balanceTags', 50);
99
+ remove_filter('excerpt_save_pre', 'balanceTags', 50);
100
+ add_filter('the_content', 'balanceTags', 50);
101
+ add_filter('get_the_excerpt', 'balanceTags', 9);
102
+ }
103
+
104
+ # Add a footnote id prefix to posts when inside a loop.
105
+ function mdwp_MarkdownPost($text) {
106
+ static $parser;
107
+ if (!$parser) {
108
+ $parser_class = MARKDOWN_PARSER_CLASS;
109
+ $parser = new $parser_class;
110
+ }
111
+ if (is_single() || is_page() || is_feed()) {
112
+ $parser->fn_id_prefix = "";
113
+ } else {
114
+ $parser->fn_id_prefix = get_the_ID() . ".";
115
+ }
116
+ return $parser->transform($text);
117
+ }
118
+
119
+ # Comments
120
+ # - Remove WordPress paragraph generator.
121
+ # - Remove WordPress auto-link generator.
122
+ # - Scramble important tags before passing them to the kses filter.
123
+ # - Run Markdown on excerpt then remove paragraph tags.
124
+ if (MARKDOWN_WP_COMMENTS) {
125
+ remove_filter('comment_text', 'wpautop', 30);
126
+ remove_filter('comment_text', 'make_clickable');
127
+ add_filter('pre_comment_content', 'Markdown', 6);
128
+ add_filter('pre_comment_content', 'mdwp_hide_tags', 8);
129
+ add_filter('pre_comment_content', 'mdwp_show_tags', 12);
130
+ add_filter('get_comment_text', 'Markdown', 6);
131
+ add_filter('get_comment_excerpt', 'Markdown', 6);
132
+ add_filter('get_comment_excerpt', 'mdwp_strip_p', 7);
133
+
134
+ global $mdwp_hidden_tags, $mdwp_placeholders;
135
+ $mdwp_hidden_tags = explode(' ',
136
+ '<p> </p> <pre> </pre> <ol> </ol> <ul> </ul> <li> </li>');
137
+ $mdwp_placeholders = explode(' ', str_rot13(
138
+ 'pEj07ZbbBZ U1kqgh4w4p pre2zmeN6K QTi31t9pre ol0MP1jzJR '.
139
+ 'ML5IjmbRol ulANi1NsGY J7zRLJqPul liA8ctl16T K9nhooUHli'));
140
+ }
141
+
142
+ function mdwp_add_p($text) {
143
+ if (!preg_match('{^$|^<(p|ul|ol|dl|pre|blockquote)>}i', $text)) {
144
+ $text = '<p>'.$text.'</p>';
145
+ $text = preg_replace('{\n{2,}}', "</p>\n\n<p>", $text);
146
+ }
147
+ return $text;
148
+ }
149
+
150
+ function mdwp_strip_p($t) { return preg_replace('{</?p>}i', '', $t); }
151
+
152
+ function mdwp_hide_tags($text) {
153
+ global $mdwp_hidden_tags, $mdwp_placeholders;
154
+ return str_replace($mdwp_hidden_tags, $mdwp_placeholders, $text);
155
+ }
156
+ function mdwp_show_tags($text) {
157
+ global $mdwp_hidden_tags, $mdwp_placeholders;
158
+ return str_replace($mdwp_placeholders, $mdwp_hidden_tags, $text);
159
+ }
160
+ }
161
+
162
+
163
+ ### bBlog Plugin Info ###
164
+
165
+ function identify_modifier_markdown() {
166
+ return array(
167
+ 'name' => 'markdown',
168
+ 'type' => 'modifier',
169
+ 'nicename' => 'PHP Markdown Extra',
170
+ 'description' => 'A text-to-HTML conversion tool for web writers',
171
+ 'authors' => 'Michel Fortin and John Gruber',
172
+ 'licence' => 'GPL',
173
+ 'version' => MARKDOWNEXTRA_VERSION,
174
+ 'help' => '<a href="http://daringfireball.net/projects/markdown/syntax">Markdown syntax</a> allows you to write using an easy-to-read, easy-to-write plain text format. Based on the original Perl version by <a href="http://daringfireball.net/">John Gruber</a>. <a href="http://michelf.com/projects/php-markdown/">More...</a>',
175
+ );
176
+ }
177
+
178
+
179
+ ### Smarty Modifier Interface ###
180
+
181
+ function smarty_modifier_markdown($text) {
182
+ return Markdown($text);
183
+ }
184
+
185
+
186
+ ### Textile Compatibility Mode ###
187
+
188
+ # Rename this file to "classTextile.php" and it can replace Textile everywhere.
189
+
190
+ if (strcasecmp(substr(__FILE__, -16), "classTextile.php") == 0) {
191
+ # Try to include PHP SmartyPants. Should be in the same directory.
192
+ @include_once 'smartypants.php';
193
+ # Fake Textile class. It calls Markdown instead.
194
+ class Textile {
195
+ function TextileThis($text, $lite='', $encode='') {
196
+ if ($lite == '' && $encode == '') $text = Markdown($text);
197
+ if (function_exists('SmartyPants')) $text = SmartyPants($text);
198
+ return $text;
199
+ }
200
+ # Fake restricted version: restrictions are not supported for now.
201
+ function TextileRestricted($text, $lite='', $noimage='') {
202
+ return $this->TextileThis($text, $lite);
203
+ }
204
+ # Workaround to ensure compatibility with TextPattern 4.0.3.
205
+ function blockLite($text) { return $text; }
206
+ }
207
+ }
208
+
209
+
210
+
211
+ #
212
+ # Markdown Parser Class
213
+ #
214
+
215
+ class Markdown_Parser {
216
+
217
+ # Regex to match balanced [brackets].
218
+ # Needed to insert a maximum bracked depth while converting to PHP.
219
+ var $nested_brackets_depth = 6;
220
+ var $nested_brackets_re;
221
+
222
+ var $nested_url_parenthesis_depth = 4;
223
+ var $nested_url_parenthesis_re;
224
+
225
+ # Table of hash values for escaped characters:
226
+ var $escape_chars = '\`*_{}[]()>#+-.!';
227
+ var $escape_chars_re;
228
+
229
+ # Change to ">" for HTML output.
230
+ var $empty_element_suffix = MARKDOWN_EMPTY_ELEMENT_SUFFIX;
231
+ var $tab_width = MARKDOWN_TAB_WIDTH;
232
+
233
+ # Change to `true` to disallow markup or entities.
234
+ var $no_markup = false;
235
+ var $no_entities = false;
236
+
237
+ # Predefined urls and titles for reference links and images.
238
+ var $predef_urls = array();
239
+ var $predef_titles = array();
240
+
241
+
242
+ function Markdown_Parser() {
243
+ #
244
+ # Constructor function. Initialize appropriate member variables.
245
+ #
246
+ $this->_initDetab();
247
+ $this->prepareItalicsAndBold();
248
+
249
+ $this->nested_brackets_re =
250
+ str_repeat('(?>[^\[\]]+|\[', $this->nested_brackets_depth).
251
+ str_repeat('\])*', $this->nested_brackets_depth);
252
+
253
+ $this->nested_url_parenthesis_re =
254
+ str_repeat('(?>[^()\s]+|\(', $this->nested_url_parenthesis_depth).
255
+ str_repeat('(?>\)))*', $this->nested_url_parenthesis_depth);
256
+
257
+ $this->escape_chars_re = '['.preg_quote($this->escape_chars).']';
258
+
259
+ # Sort document, block, and span gamut in ascendent priority order.
260
+ asort($this->document_gamut);
261
+ asort($this->block_gamut);
262
+ asort($this->span_gamut);
263
+ }
264
+
265
+
266
+ # Internal hashes used during transformation.
267
+ var $urls = array();
268
+ var $titles = array();
269
+ var $html_hashes = array();
270
+
271
+ # Status flag to avoid invalid nesting.
272
+ var $in_anchor = false;
273
+
274
+
275
+ function setup() {
276
+ #
277
+ # Called before the transformation process starts to setup parser
278
+ # states.
279
+ #
280
+ # Clear global hashes.
281
+ $this->urls = $this->predef_urls;
282
+ $this->titles = $this->predef_titles;
283
+ $this->html_hashes = array();
284
+
285
+ $in_anchor = false;
286
+ }
287
+
288
+ function teardown() {
289
+ #
290
+ # Called after the transformation process to clear any variable
291
+ # which may be taking up memory unnecessarly.
292
+ #
293
+ $this->urls = array();
294
+ $this->titles = array();
295
+ $this->html_hashes = array();
296
+ }
297
+
298
+
299
+ function transform($text) {
300
+ #
301
+ # Main function. Performs some preprocessing on the input text
302
+ # and pass it through the document gamut.
303
+ #
304
+ $this->setup();
305
+
306
+ # Remove UTF-8 BOM and marker character in input, if present.
307
+ $text = preg_replace('{^\xEF\xBB\xBF|\x1A}', '', $text);
308
+
309
+ # Standardize line endings:
310
+ # DOS to Unix and Mac to Unix
311
+ $text = preg_replace('{\r\n?}', "\n", $text);
312
+
313
+ # Make sure $text ends with a couple of newlines:
314
+ $text .= "\n\n";
315
+
316
+ # Convert all tabs to spaces.
317
+ $text = $this->detab($text);
318
+
319
+ # Turn block-level HTML blocks into hash entries
320
+ $text = $this->hashHTMLBlocks($text);
321
+
322
+ # Strip any lines consisting only of spaces and tabs.
323
+ # This makes subsequent regexen easier to write, because we can
324
+ # match consecutive blank lines with /\n+/ instead of something
325
+ # contorted like /[ ]*\n+/ .
326
+ $text = preg_replace('/^[ ]+$/m', '', $text);
327
+
328
+ # Run document gamut methods.
329
+ foreach ($this->document_gamut as $method => $priority) {
330
+ $text = $this->$method($text);
331
+ }
332
+
333
+ $this->teardown();
334
+
335
+ return $text . "\n";
336
+ }
337
+
338
+ var $document_gamut = array(
339
+ # Strip link definitions, store in hashes.
340
+ "stripLinkDefinitions" => 20,
341
+
342
+ "runBasicBlockGamut" => 30,
343
+ );
344
+
345
+
346
+ function stripLinkDefinitions($text) {
347
+ #
348
+ # Strips link definitions from text, stores the URLs and titles in
349
+ # hash references.
350
+ #
351
+ $less_than_tab = $this->tab_width - 1;
352
+
353
+ # Link defs are in the form: ^[id]: url "optional title"
354
+ $text = preg_replace_callback('{
355
+ ^[ ]{0,'.$less_than_tab.'}\[(.+)\][ ]?: # id = $1
356
+ [ ]*
357
+ \n? # maybe *one* newline
358
+ [ ]*
359
+ (?:
360
+ <(.+?)> # url = $2
361
+ |
362
+ (\S+?) # url = $3
363
+ )
364
+ [ ]*
365
+ \n? # maybe one newline
366
+ [ ]*
367
+ (?:
368
+ (?<=\s) # lookbehind for whitespace
369
+ ["(]
370
+ (.*?) # title = $4
371
+ [")]
372
+ [ ]*
373
+ )? # title is optional
374
+ (?:\n+|\Z)
375
+ }xm',
376
+ array(&$this, '_stripLinkDefinitions_callback'),
377
+ $text);
378
+ return $text;
379
+ }
380
+ function _stripLinkDefinitions_callback($matches) {
381
+ $link_id = strtolower($matches[1]);
382
+ $url = $matches[2] == '' ? $matches[3] : $matches[2];
383
+ $this->urls[$link_id] = $url;
384
+ $this->titles[$link_id] =& $matches[4];
385
+ return ''; # String that will replace the block
386
+ }
387
+
388
+
389
+ function hashHTMLBlocks($text) {
390
+ if ($this->no_markup) return $text;
391
+
392
+ $less_than_tab = $this->tab_width - 1;
393
+
394
+ # Hashify HTML blocks:
395
+ # We only want to do this for block-level HTML tags, such as headers,
396
+ # lists, and tables. That's because we still want to wrap <p>s around
397
+ # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
398
+ # phrase emphasis, and spans. The list of tags we're looking for is
399
+ # hard-coded:
400
+ #
401
+ # * List "a" is made of tags which can be both inline or block-level.
402
+ # These will be treated block-level when the start tag is alone on
403
+ # its line, otherwise they're not matched here and will be taken as
404
+ # inline later.
405
+ # * List "b" is made of tags which are always block-level;
406
+ #
407
+ $block_tags_a_re = 'ins|del';
408
+ $block_tags_b_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|'.
409
+ 'script|noscript|form|fieldset|iframe|math';
410
+
411
+ # Regular expression for the content of a block tag.
412
+ $nested_tags_level = 4;
413
+ $attr = '
414
+ (?> # optional tag attributes
415
+ \s # starts with whitespace
416
+ (?>
417
+ [^>"/]+ # text outside quotes
418
+ |
419
+ /+(?!>) # slash not followed by ">"
420
+ |
421
+ "[^"]*" # text inside double quotes (tolerate ">")
422
+ |
423
+ \'[^\']*\' # text inside single quotes (tolerate ">")
424
+ )*
425
+ )?
426
+ ';
427
+ $content =
428
+ str_repeat('
429
+ (?>
430
+ [^<]+ # content without tag
431
+ |
432
+ <\2 # nested opening tag
433
+ '.$attr.' # attributes
434
+ (?>
435
+ />
436
+ |
437
+ >', $nested_tags_level). # end of opening tag
438
+ '.*?'. # last level nested tag content
439
+ str_repeat('
440
+ </\2\s*> # closing nested tag
441
+ )
442
+ |
443
+ <(?!/\2\s*> # other tags with a different name
444
+ )
445
+ )*',
446
+ $nested_tags_level);
447
+ $content2 = str_replace('\2', '\3', $content);
448
+
449
+ # First, look for nested blocks, e.g.:
450
+ # <div>
451
+ # <div>
452
+ # tags for inner block must be indented.
453
+ # </div>
454
+ # </div>
455
+ #
456
+ # The outermost tags must start at the left margin for this to match, and
457
+ # the inner nested divs must be indented.
458
+ # We need to do this before the next, more liberal match, because the next
459
+ # match will start at the first `<div>` and stop at the first `</div>`.
460
+ $text = preg_replace_callback('{(?>
461
+ (?>
462
+ (?<=\n\n) # Starting after a blank line
463
+ | # or
464
+ \A\n? # the beginning of the doc
465
+ )
466
+ ( # save in $1
467
+
468
+ # Match from `\n<tag>` to `</tag>\n`, handling nested tags
469
+ # in between.
470
+
471
+ [ ]{0,'.$less_than_tab.'}
472
+ <('.$block_tags_b_re.')# start tag = $2
473
+ '.$attr.'> # attributes followed by > and \n
474
+ '.$content.' # content, support nesting
475
+ </\2> # the matching end tag
476
+ [ ]* # trailing spaces/tabs
477
+ (?=\n+|\Z) # followed by a newline or end of document
478
+
479
+ | # Special version for tags of group a.
480
+
481
+ [ ]{0,'.$less_than_tab.'}
482
+ <('.$block_tags_a_re.')# start tag = $3
483
+ '.$attr.'>[ ]*\n # attributes followed by >
484
+ '.$content2.' # content, support nesting
485
+ </\3> # the matching end tag
486
+ [ ]* # trailing spaces/tabs
487
+ (?=\n+|\Z) # followed by a newline or end of document
488
+
489
+ | # Special case just for <hr />. It was easier to make a special
490
+ # case than to make the other regex more complicated.
491
+
492
+ [ ]{0,'.$less_than_tab.'}
493
+ <(hr) # start tag = $2
494
+ '.$attr.' # attributes
495
+ /?> # the matching end tag
496
+ [ ]*
497
+ (?=\n{2,}|\Z) # followed by a blank line or end of document
498
+
499
+ | # Special case for standalone HTML comments:
500
+
501
+ [ ]{0,'.$less_than_tab.'}
502
+ (?s:
503
+ <!-- .*? -->
504
+ )
505
+ [ ]*
506
+ (?=\n{2,}|\Z) # followed by a blank line or end of document
507
+
508
+ | # PHP and ASP-style processor instructions (<? and <%)
509
+
510
+ [ ]{0,'.$less_than_tab.'}
511
+ (?s:
512
+ <([?%]) # $2
513
+ .*?
514
+ \2>
515
+ )
516
+ [ ]*
517
+ (?=\n{2,}|\Z) # followed by a blank line or end of document
518
+
519
+ )
520
+ )}Sxmi',
521
+ array(&$this, '_hashHTMLBlocks_callback'),
522
+ $text);
523
+
524
+ return $text;
525
+ }
526
+ function _hashHTMLBlocks_callback($matches) {
527
+ $text = $matches[1];
528
+ $key = $this->hashBlock($text);
529
+ return "\n\n$key\n\n";
530
+ }
531
+
532
+
533
+ function hashPart($text, $boundary = 'X') {
534
+ #
535
+ # Called whenever a tag must be hashed when a function insert an atomic
536
+ # element in the text stream. Passing $text to through this function gives
537
+ # a unique text-token which will be reverted back when calling unhash.
538
+ #
539
+ # The $boundary argument specify what character should be used to surround
540
+ # the token. By convension, "B" is used for block elements that needs not
541
+ # to be wrapped into paragraph tags at the end, ":" is used for elements
542
+ # that are word separators and "X" is used in the general case.
543
+ #
544
+ # Swap back any tag hash found in $text so we do not have to `unhash`
545
+ # multiple times at the end.
546
+ $text = $this->unhash($text);
547
+
548
+ # Then hash the block.
549
+ static $i = 0;
550
+ $key = "$boundary\x1A" . ++$i . $boundary;
551
+ $this->html_hashes[$key] = $text;
552
+ return $key; # String that will replace the tag.
553
+ }
554
+
555
+
556
+ function hashBlock($text) {
557
+ #
558
+ # Shortcut function for hashPart with block-level boundaries.
559
+ #
560
+ return $this->hashPart($text, 'B');
561
+ }
562
+
563
+
564
+ var $block_gamut = array(
565
+ #
566
+ # These are all the transformations that form block-level
567
+ # tags like paragraphs, headers, and list items.
568
+ #
569
+ "doHeaders" => 10,
570
+ "doHorizontalRules" => 20,
571
+
572
+ "doLists" => 40,
573
+ "doCodeBlocks" => 50,
574
+ "doBlockQuotes" => 60,
575
+ );
576
+
577
+ function runBlockGamut($text) {
578
+ #
579
+ # Run block gamut tranformations.
580
+ #
581
+ # We need to escape raw HTML in Markdown source before doing anything
582
+ # else. This need to be done for each block, and not only at the
583
+ # begining in the Markdown function since hashed blocks can be part of
584
+ # list items and could have been indented. Indented blocks would have
585
+ # been seen as a code block in a previous pass of hashHTMLBlocks.
586
+ $text = $this->hashHTMLBlocks($text);
587
+
588
+ return $this->runBasicBlockGamut($text);
589
+ }
590
+
591
+ function runBasicBlockGamut($text) {
592
+ #
593
+ # Run block gamut tranformations, without hashing HTML blocks. This is
594
+ # useful when HTML blocks are known to be already hashed, like in the first
595
+ # whole-document pass.
596
+ #
597
+ foreach ($this->block_gamut as $method => $priority) {
598
+ $text = $this->$method($text);
599
+ }
600
+
601
+ # Finally form paragraph and restore hashed blocks.
602
+ $text = $this->formParagraphs($text);
603
+
604
+ return $text;
605
+ }
606
+
607
+
608
+ function doHorizontalRules($text) {
609
+ # Do Horizontal Rules:
610
+ return preg_replace(
611
+ '{
612
+ ^[ ]{0,3} # Leading space
613
+ ([-*_]) # $1: First marker
614
+ (?> # Repeated marker group
615
+ [ ]{0,2} # Zero, one, or two spaces.
616
+ \1 # Marker character
617
+ ){2,} # Group repeated at least twice
618
+ [ ]* # Tailing spaces
619
+ $ # End of line.
620
+ }mx',
621
+ "\n".$this->hashBlock("<hr$this->empty_element_suffix")."\n",
622
+ $text);
623
+ }
624
+
625
+
626
+ var $span_gamut = array(
627
+ #
628
+ # These are all the transformations that occur *within* block-level
629
+ # tags like paragraphs, headers, and list items.
630
+ #
631
+ # Process character escapes, code spans, and inline HTML
632
+ # in one shot.
633
+ "parseSpan" => -30,
634
+
635
+ # Process anchor and image tags. Images must come first,
636
+ # because ![foo][f] looks like an anchor.
637
+ "doImages" => 10,
638
+ "doAnchors" => 20,
639
+
640
+ # Make links out of things like `<http://example.com/>`
641
+ # Must come after doAnchors, because you can use < and >
642
+ # delimiters in inline links like [this](<url>).
643
+ "doAutoLinks" => 30,
644
+ "encodeAmpsAndAngles" => 40,
645
+
646
+ "doItalicsAndBold" => 50,
647
+ "doHardBreaks" => 60,
648
+ );
649
+
650
+ function runSpanGamut($text) {
651
+ #
652
+ # Run span gamut tranformations.
653
+ #
654
+ foreach ($this->span_gamut as $method => $priority) {
655
+ $text = $this->$method($text);
656
+ }
657
+
658
+ return $text;
659
+ }
660
+
661
+
662
+ function doHardBreaks($text) {
663
+ # Do hard breaks:
664
+ return preg_replace_callback('/ {2,}\n/',
665
+ array(&$this, '_doHardBreaks_callback'), $text);
666
+ }
667
+ function _doHardBreaks_callback($matches) {
668
+ return $this->hashPart("<br$this->empty_element_suffix\n");
669
+ }
670
+
671
+
672
+ function doAnchors($text) {
673
+ #
674
+ # Turn Markdown link shortcuts into XHTML <a> tags.
675
+ #
676
+ if ($this->in_anchor) return $text;
677
+ $this->in_anchor = true;
678
+
679
+ #
680
+ # First, handle reference-style links: [link text] [id]
681
+ #
682
+ $text = preg_replace_callback('{
683
+ ( # wrap whole match in $1
684
+ \[
685
+ ('.$this->nested_brackets_re.') # link text = $2
686
+ \]
687
+
688
+ [ ]? # one optional space
689
+ (?:\n[ ]*)? # one optional newline followed by spaces
690
+
691
+ \[
692
+ (.*?) # id = $3
693
+ \]
694
+ )
695
+ }xs',
696
+ array(&$this, '_doAnchors_reference_callback'), $text);
697
+
698
+ #
699
+ # Next, inline-style links: [link text](url "optional title")
700
+ #
701
+ $text = preg_replace_callback('{
702
+ ( # wrap whole match in $1
703
+ \[
704
+ ('.$this->nested_brackets_re.') # link text = $2
705
+ \]
706
+ \( # literal paren
707
+ [ \n]*
708
+ (?:
709
+ <(.+?)> # href = $3
710
+ |
711
+ ('.$this->nested_url_parenthesis_re.') # href = $4
712
+ )
713
+ [ \n]*
714
+ ( # $5
715
+ ([\'"]) # quote char = $6
716
+ (.*?) # Title = $7
717
+ \6 # matching quote
718
+ [ \n]* # ignore any spaces/tabs between closing quote and )
719
+ )? # title is optional
720
+ \)
721
+ )
722
+ }xs',
723
+ array(&$this, '_doAnchors_inline_callback'), $text);
724
+
725
+ #
726
+ # Last, handle reference-style shortcuts: [link text]
727
+ # These must come last in case you've also got [link text][1]
728
+ # or [link text](/foo)
729
+ #
730
+ $text = preg_replace_callback('{
731
+ ( # wrap whole match in $1
732
+ \[
733
+ ([^\[\]]+) # link text = $2; can\'t contain [ or ]
734
+ \]
735
+ )
736
+ }xs',
737
+ array(&$this, '_doAnchors_reference_callback'), $text);
738
+
739
+ $this->in_anchor = false;
740
+ return $text;
741
+ }
742
+ function _doAnchors_reference_callback($matches) {
743
+ $whole_match = $matches[1];
744
+ $link_text = $matches[2];
745
+ $link_id =& $matches[3];
746
+
747
+ if ($link_id == "") {
748
+ # for shortcut links like [this][] or [this].
749
+ $link_id = $link_text;
750
+ }
751
+
752
+ # lower-case and turn embedded newlines into spaces
753
+ $link_id = strtolower($link_id);
754
+ $link_id = preg_replace('{[ ]?\n}', ' ', $link_id);
755
+
756
+ if (isset($this->urls[$link_id])) {
757
+ $url = $this->urls[$link_id];
758
+ $url = $this->encodeAttribute($url);
759
+
760
+ $result = "<a href=\"$url\"";
761
+ if ( isset( $this->titles[$link_id] ) ) {
762
+ $title = $this->titles[$link_id];
763
+ $title = $this->encodeAttribute($title);
764
+ $result .= " title=\"$title\"";
765
+ }
766
+
767
+ $link_text = $this->runSpanGamut($link_text);
768
+ $result .= ">$link_text</a>";
769
+ $result = $this->hashPart($result);
770
+ }
771
+ else {
772
+ $result = $whole_match;
773
+ }
774
+ return $result;
775
+ }
776
+ function _doAnchors_inline_callback($matches) {
777
+ $whole_match = $matches[1];
778
+ $link_text = $this->runSpanGamut($matches[2]);
779
+ $url = $matches[3] == '' ? $matches[4] : $matches[3];
780
+ $title =& $matches[7];
781
+
782
+ $url = $this->encodeAttribute($url);
783
+
784
+ $result = "<a href=\"$url\"";
785
+ if (isset($title)) {
786
+ $title = $this->encodeAttribute($title);
787
+ $result .= " title=\"$title\"";
788
+ }
789
+
790
+ $link_text = $this->runSpanGamut($link_text);
791
+ $result .= ">$link_text</a>";
792
+
793
+ return $this->hashPart($result);
794
+ }
795
+
796
+
797
+ function doImages($text) {
798
+ #
799
+ # Turn Markdown image shortcuts into <img> tags.
800
+ #
801
+ #
802
+ # First, handle reference-style labeled images: ![alt text][id]
803
+ #
804
+ $text = preg_replace_callback('{
805
+ ( # wrap whole match in $1
806
+ !\[
807
+ ('.$this->nested_brackets_re.') # alt text = $2
808
+ \]
809
+
810
+ [ ]? # one optional space
811
+ (?:\n[ ]*)? # one optional newline followed by spaces
812
+
813
+ \[
814
+ (.*?) # id = $3
815
+ \]
816
+
817
+ )
818
+ }xs',
819
+ array(&$this, '_doImages_reference_callback'), $text);
820
+
821
+ #
822
+ # Next, handle inline images: ![alt text](url "optional title")
823
+ # Don't forget: encode * and _
824
+ #
825
+ $text = preg_replace_callback('{
826
+ ( # wrap whole match in $1
827
+ !\[
828
+ ('.$this->nested_brackets_re.') # alt text = $2
829
+ \]
830
+ \s? # One optional whitespace character
831
+ \( # literal paren
832
+ [ \n]*
833
+ (?:
834
+ <(\S*)> # src url = $3
835
+ |
836
+ ('.$this->nested_url_parenthesis_re.') # src url = $4
837
+ )
838
+ [ \n]*
839
+ ( # $5
840
+ ([\'"]) # quote char = $6
841
+ (.*?) # title = $7
842
+ \6 # matching quote
843
+ [ \n]*
844
+ )? # title is optional
845
+ \)
846
+ )
847
+ }xs',
848
+ array(&$this, '_doImages_inline_callback'), $text);
849
+
850
+ return $text;
851
+ }
852
+ function _doImages_reference_callback($matches) {
853
+ $whole_match = $matches[1];
854
+ $alt_text = $matches[2];
855
+ $link_id = strtolower($matches[3]);
856
+
857
+ if ($link_id == "") {
858
+ $link_id = strtolower($alt_text); # for shortcut links like ![this][].
859
+ }
860
+
861
+ $alt_text = $this->encodeAttribute($alt_text);
862
+ if (isset($this->urls[$link_id])) {
863
+ $url = $this->encodeAttribute($this->urls[$link_id]);
864
+ $result = "<img src=\"$url\" alt=\"$alt_text\"";
865
+ if (isset($this->titles[$link_id])) {
866
+ $title = $this->titles[$link_id];
867
+ $title = $this->encodeAttribute($title);
868
+ $result .= " title=\"$title\"";
869
+ }
870
+ $result .= $this->empty_element_suffix;
871
+ $result = $this->hashPart($result);
872
+ }
873
+ else {
874
+ # If there's no such link ID, leave intact:
875
+ $result = $whole_match;
876
+ }
877
+
878
+ return $result;
879
+ }
880
+ function _doImages_inline_callback($matches) {
881
+ $whole_match = $matches[1];
882
+ $alt_text = $matches[2];
883
+ $url = $matches[3] == '' ? $matches[4] : $matches[3];
884
+ $title =& $matches[7];
885
+
886
+ $alt_text = $this->encodeAttribute($alt_text);
887
+ $url = $this->encodeAttribute($url);
888
+ $result = "<img src=\"$url\" alt=\"$alt_text\"";
889
+ if (isset($title)) {
890
+ $title = $this->encodeAttribute($title);
891
+ $result .= " title=\"$title\""; # $title already quoted
892
+ }
893
+ $result .= $this->empty_element_suffix;
894
+
895
+ return $this->hashPart($result);
896
+ }
897
+
898
+
899
+ function doHeaders($text) {
900
+ # Setext-style headers:
901
+ # Header 1
902
+ # ========
903
+ #
904
+ # Header 2
905
+ # --------
906
+ #
907
+ $text = preg_replace_callback('{ ^(.+?)[ ]*\n(=+|-+)[ ]*\n+ }mx',
908
+ array(&$this, '_doHeaders_callback_setext'), $text);
909
+
910
+ # atx-style headers:
911
+ # # Header 1
912
+ # ## Header 2
913
+ # ## Header 2 with closing hashes ##
914
+ # ...
915
+ # ###### Header 6
916
+ #
917
+ $text = preg_replace_callback('{
918
+ ^(\#{1,6}) # $1 = string of #\'s
919
+ [ ]*
920
+ (.+?) # $2 = Header text
921
+ [ ]*
922
+ \#* # optional closing #\'s (not counted)
923
+ \n+
924
+ }xm',
925
+ array(&$this, '_doHeaders_callback_atx'), $text);
926
+
927
+ return $text;
928
+ }
929
+ function _doHeaders_callback_setext($matches) {
930
+ # Terrible hack to check we haven't found an empty list item.
931
+ if ($matches[2] == '-' && preg_match('{^-(?: |$)}', $matches[1]))
932
+ return $matches[0];
933
+
934
+ $level = $matches[2]{0} == '=' ? 1 : 2;
935
+ $block = "<h$level>".$this->runSpanGamut($matches[1])."</h$level>";
936
+ return "\n" . $this->hashBlock($block) . "\n\n";
937
+ }
938
+ function _doHeaders_callback_atx($matches) {
939
+ $level = strlen($matches[1]);
940
+ $block = "<h$level>".$this->runSpanGamut($matches[2])."</h$level>";
941
+ return "\n" . $this->hashBlock($block) . "\n\n";
942
+ }
943
+
944
+
945
+ function doLists($text) {
946
+ #
947
+ # Form HTML ordered (numbered) and unordered (bulleted) lists.
948
+ #
949
+ $less_than_tab = $this->tab_width - 1;
950
+
951
+ # Re-usable patterns to match list item bullets and number markers:
952
+ $marker_ul_re = '[*+-]';
953
+ $marker_ol_re = '\d+[.]';
954
+ $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
955
+
956
+ $markers_relist = array(
957
+ $marker_ul_re => $marker_ol_re,
958
+ $marker_ol_re => $marker_ul_re,
959
+ );
960
+
961
+ foreach ($markers_relist as $marker_re => $other_marker_re) {
962
+ # Re-usable pattern to match any entirel ul or ol list:
963
+ $whole_list_re = '
964
+ ( # $1 = whole list
965
+ ( # $2
966
+ ([ ]{0,'.$less_than_tab.'}) # $3 = number of spaces
967
+ ('.$marker_re.') # $4 = first list item marker
968
+ [ ]+
969
+ )
970
+ (?s:.+?)
971
+ ( # $5
972
+ \z
973
+ |
974
+ \n{2,}
975
+ (?=\S)
976
+ (?! # Negative lookahead for another list item marker
977
+ [ ]*
978
+ '.$marker_re.'[ ]+
979
+ )
980
+ |
981
+ (?= # Lookahead for another kind of list
982
+ \n
983
+ \3 # Must have the same indentation
984
+ '.$other_marker_re.'[ ]+
985
+ )
986
+ )
987
+ )
988
+ '; // mx
989
+
990
+ # We use a different prefix before nested lists than top-level lists.
991
+ # See extended comment in _ProcessListItems().
992
+
993
+ if ($this->list_level) {
994
+ $text = preg_replace_callback('{
995
+ ^
996
+ '.$whole_list_re.'
997
+ }mx',
998
+ array(&$this, '_doLists_callback'), $text);
999
+ }
1000
+ else {
1001
+ $text = preg_replace_callback('{
1002
+ (?:(?<=\n)\n|\A\n?) # Must eat the newline
1003
+ '.$whole_list_re.'
1004
+ }mx',
1005
+ array(&$this, '_doLists_callback'), $text);
1006
+ }
1007
+ }
1008
+
1009
+ return $text;
1010
+ }
1011
+ function _doLists_callback($matches) {
1012
+ # Re-usable patterns to match list item bullets and number markers:
1013
+ $marker_ul_re = '[*+-]';
1014
+ $marker_ol_re = '\d+[.]';
1015
+ $marker_any_re = "(?:$marker_ul_re|$marker_ol_re)";
1016
+
1017
+ $list = $matches[1];
1018
+ $list_type = preg_match("/$marker_ul_re/", $matches[4]) ? "ul" : "ol";
1019
+
1020
+ $marker_any_re = ( $list_type == "ul" ? $marker_ul_re : $marker_ol_re );
1021
+
1022
+ $list .= "\n";
1023
+ $result = $this->processListItems($list, $marker_any_re);
1024
+
1025
+ $result = $this->hashBlock("<$list_type>\n" . $result . "</$list_type>");
1026
+ return "\n". $result ."\n\n";
1027
+ }
1028
+
1029
+ var $list_level = 0;
1030
+
1031
+ function processListItems($list_str, $marker_any_re) {
1032
+ #
1033
+ # Process the contents of a single ordered or unordered list, splitting it
1034
+ # into individual list items.
1035
+ #
1036
+ # The $this->list_level global keeps track of when we're inside a list.
1037
+ # Each time we enter a list, we increment it; when we leave a list,
1038
+ # we decrement. If it's zero, we're not in a list anymore.
1039
+ #
1040
+ # We do this because when we're not inside a list, we want to treat
1041
+ # something like this:
1042
+ #
1043
+ # I recommend upgrading to version
1044
+ # 8. Oops, now this line is treated
1045
+ # as a sub-list.
1046
+ #
1047
+ # As a single paragraph, despite the fact that the second line starts
1048
+ # with a digit-period-space sequence.
1049
+ #
1050
+ # Whereas when we're inside a list (or sub-list), that line will be
1051
+ # treated as the start of a sub-list. What a kludge, huh? This is
1052
+ # an aspect of Markdown's syntax that's hard to parse perfectly
1053
+ # without resorting to mind-reading. Perhaps the solution is to
1054
+ # change the syntax rules such that sub-lists must start with a
1055
+ # starting cardinal number; e.g. "1." or "a.".
1056
+
1057
+ $this->list_level++;
1058
+
1059
+ # trim trailing blank lines:
1060
+ $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
1061
+
1062
+ $list_str = preg_replace_callback('{
1063
+ (\n)? # leading line = $1
1064
+ (^[ ]*) # leading whitespace = $2
1065
+ ('.$marker_any_re.' # list marker and space = $3
1066
+ (?:[ ]+|(?=\n)) # space only required if item is not empty
1067
+ )
1068
+ ((?s:.*?)) # list item text = $4
1069
+ (?:(\n+(?=\n))|\n) # tailing blank line = $5
1070
+ (?= \n* (\z | \2 ('.$marker_any_re.') (?:[ ]+|(?=\n))))
1071
+ }xm',
1072
+ array(&$this, '_processListItems_callback'), $list_str);
1073
+
1074
+ $this->list_level--;
1075
+ return $list_str;
1076
+ }
1077
+ function _processListItems_callback($matches) {
1078
+ $item = $matches[4];
1079
+ $leading_line =& $matches[1];
1080
+ $leading_space =& $matches[2];
1081
+ $marker_space = $matches[3];
1082
+ $tailing_blank_line =& $matches[5];
1083
+
1084
+ if ($leading_line || $tailing_blank_line ||
1085
+ preg_match('/\n{2,}/', $item))
1086
+ {
1087
+ # Replace marker with the appropriate whitespace indentation
1088
+ $item = $leading_space . str_repeat(' ', strlen($marker_space)) . $item;
1089
+ $item = $this->runBlockGamut($this->outdent($item)."\n");
1090
+ }
1091
+ else {
1092
+ # Recursion for sub-lists:
1093
+ $item = $this->doLists($this->outdent($item));
1094
+ $item = preg_replace('/\n+$/', '', $item);
1095
+ $item = $this->runSpanGamut($item);
1096
+ }
1097
+
1098
+ return "<li>" . $item . "</li>\n";
1099
+ }
1100
+
1101
+
1102
+ function doCodeBlocks($text) {
1103
+ #
1104
+ # Process Markdown `<pre><code>` blocks.
1105
+ #
1106
+ $text = preg_replace_callback('{
1107
+ (?:\n\n|\A\n?)
1108
+ ( # $1 = the code block -- one or more lines, starting with a space/tab
1109
+ (?>
1110
+ [ ]{'.$this->tab_width.'} # Lines must start with a tab or a tab-width of spaces
1111
+ .*\n+
1112
+ )+
1113
+ )
1114
+ ((?=^[ ]{0,'.$this->tab_width.'}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
1115
+ }xm',
1116
+ array(&$this, '_doCodeBlocks_callback'), $text);
1117
+
1118
+ return $text;
1119
+ }
1120
+ function _doCodeBlocks_callback($matches) {
1121
+ $codeblock = $matches[1];
1122
+
1123
+ $codeblock = $this->outdent($codeblock);
1124
+ $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
1125
+
1126
+ # trim leading newlines and trailing newlines
1127
+ $codeblock = preg_replace('/\A\n+|\n+\z/', '', $codeblock);
1128
+
1129
+ $codeblock = "<pre><code>$codeblock\n</code></pre>";
1130
+ return "\n\n".$this->hashBlock($codeblock)."\n\n";
1131
+ }
1132
+
1133
+
1134
+ function makeCodeSpan($code) {
1135
+ #
1136
+ # Create a code span markup for $code. Called from handleSpanToken.
1137
+ #
1138
+ $code = htmlspecialchars(trim($code), ENT_NOQUOTES);
1139
+ return $this->hashPart("<code>$code</code>");
1140
+ }
1141
+
1142
+
1143
+ var $em_relist = array(
1144
+ '' => '(?:(?<!\*)\*(?!\*)|(?<!_)_(?!_))(?=\S|$)(?![.,:;]\s)',
1145
+ '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
1146
+ '_' => '(?<=\S|^)(?<!_)_(?!_)',
1147
+ );
1148
+ var $strong_relist = array(
1149
+ '' => '(?:(?<!\*)\*\*(?!\*)|(?<!_)__(?!_))(?=\S|$)(?![.,:;]\s)',
1150
+ '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
1151
+ '__' => '(?<=\S|^)(?<!_)__(?!_)',
1152
+ );
1153
+ var $em_strong_relist = array(
1154
+ '' => '(?:(?<!\*)\*\*\*(?!\*)|(?<!_)___(?!_))(?=\S|$)(?![.,:;]\s)',
1155
+ '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
1156
+ '___' => '(?<=\S|^)(?<!_)___(?!_)',
1157
+ );
1158
+ var $em_strong_prepared_relist;
1159
+
1160
+ function prepareItalicsAndBold() {
1161
+ #
1162
+ # Prepare regular expressions for searching emphasis tokens in any
1163
+ # context.
1164
+ #
1165
+ foreach ($this->em_relist as $em => $em_re) {
1166
+ foreach ($this->strong_relist as $strong => $strong_re) {
1167
+ # Construct list of allowed token expressions.
1168
+ $token_relist = array();
1169
+ if (isset($this->em_strong_relist["$em$strong"])) {
1170
+ $token_relist[] = $this->em_strong_relist["$em$strong"];
1171
+ }
1172
+ $token_relist[] = $em_re;
1173
+ $token_relist[] = $strong_re;
1174
+
1175
+ # Construct master expression from list.
1176
+ $token_re = '{('. implode('|', $token_relist) .')}';
1177
+ $this->em_strong_prepared_relist["$em$strong"] = $token_re;
1178
+ }
1179
+ }
1180
+ }
1181
+
1182
+ function doItalicsAndBold($text) {
1183
+ $token_stack = array('');
1184
+ $text_stack = array('');
1185
+ $em = '';
1186
+ $strong = '';
1187
+ $tree_char_em = false;
1188
+
1189
+ while (1) {
1190
+ #
1191
+ # Get prepared regular expression for seraching emphasis tokens
1192
+ # in current context.
1193
+ #
1194
+ $token_re = $this->em_strong_prepared_relist["$em$strong"];
1195
+
1196
+ #
1197
+ # Each loop iteration search for the next emphasis token.
1198
+ # Each token is then passed to handleSpanToken.
1199
+ #
1200
+ $parts = preg_split($token_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
1201
+ $text_stack[0] .= $parts[0];
1202
+ $token =& $parts[1];
1203
+ $text =& $parts[2];
1204
+
1205
+ if (empty($token)) {
1206
+ # Reached end of text span: empty stack without emitting.
1207
+ # any more emphasis.
1208
+ while ($token_stack[0]) {
1209
+ $text_stack[1] .= array_shift($token_stack);
1210
+ $text_stack[0] .= array_shift($text_stack);
1211
+ }
1212
+ break;
1213
+ }
1214
+
1215
+ $token_len = strlen($token);
1216
+ if ($tree_char_em) {
1217
+ # Reached closing marker while inside a three-char emphasis.
1218
+ if ($token_len == 3) {
1219
+ # Three-char closing marker, close em and strong.
1220
+ array_shift($token_stack);
1221
+ $span = array_shift($text_stack);
1222
+ $span = $this->runSpanGamut($span);
1223
+ $span = "<strong><em>$span</em></strong>";
1224
+ $text_stack[0] .= $this->hashPart($span);
1225
+ $em = '';
1226
+ $strong = '';
1227
+ } else {
1228
+ # Other closing marker: close one em or strong and
1229
+ # change current token state to match the other
1230
+ $token_stack[0] = str_repeat($token{0}, 3-$token_len);
1231
+ $tag = $token_len == 2 ? "strong" : "em";
1232
+ $span = $text_stack[0];
1233
+ $span = $this->runSpanGamut($span);
1234
+ $span = "<$tag>$span</$tag>";
1235
+ $text_stack[0] = $this->hashPart($span);
1236
+ $$tag = ''; # $$tag stands for $em or $strong
1237
+ }
1238
+ $tree_char_em = false;
1239
+ } else if ($token_len == 3) {
1240
+ if ($em) {
1241
+ # Reached closing marker for both em and strong.
1242
+ # Closing strong marker:
1243
+ for ($i = 0; $i < 2; ++$i) {
1244
+ $shifted_token = array_shift($token_stack);
1245
+ $tag = strlen($shifted_token) == 2 ? "strong" : "em";
1246
+ $span = array_shift($text_stack);
1247
+ $span = $this->runSpanGamut($span);
1248
+ $span = "<$tag>$span</$tag>";
1249
+ $text_stack[0] .= $this->hashPart($span);
1250
+ $$tag = ''; # $$tag stands for $em or $strong
1251
+ }
1252
+ } else {
1253
+ # Reached opening three-char emphasis marker. Push on token
1254
+ # stack; will be handled by the special condition above.
1255
+ $em = $token{0};
1256
+ $strong = "$em$em";
1257
+ array_unshift($token_stack, $token);
1258
+ array_unshift($text_stack, '');
1259
+ $tree_char_em = true;
1260
+ }
1261
+ } else if ($token_len == 2) {
1262
+ if ($strong) {
1263
+ # Unwind any dangling emphasis marker:
1264
+ if (strlen($token_stack[0]) == 1) {
1265
+ $text_stack[1] .= array_shift($token_stack);
1266
+ $text_stack[0] .= array_shift($text_stack);
1267
+ }
1268
+ # Closing strong marker:
1269
+ array_shift($token_stack);
1270
+ $span = array_shift($text_stack);
1271
+ $span = $this->runSpanGamut($span);
1272
+ $span = "<strong>$span</strong>";
1273
+ $text_stack[0] .= $this->hashPart($span);
1274
+ $strong = '';
1275
+ } else {
1276
+ array_unshift($token_stack, $token);
1277
+ array_unshift($text_stack, '');
1278
+ $strong = $token;
1279
+ }
1280
+ } else {
1281
+ # Here $token_len == 1
1282
+ if ($em) {
1283
+ if (strlen($token_stack[0]) == 1) {
1284
+ # Closing emphasis marker:
1285
+ array_shift($token_stack);
1286
+ $span = array_shift($text_stack);
1287
+ $span = $this->runSpanGamut($span);
1288
+ $span = "<em>$span</em>";
1289
+ $text_stack[0] .= $this->hashPart($span);
1290
+ $em = '';
1291
+ } else {
1292
+ $text_stack[0] .= $token;
1293
+ }
1294
+ } else {
1295
+ array_unshift($token_stack, $token);
1296
+ array_unshift($text_stack, '');
1297
+ $em = $token;
1298
+ }
1299
+ }
1300
+ }
1301
+ return $text_stack[0];
1302
+ }
1303
+
1304
+
1305
+ function doBlockQuotes($text) {
1306
+ $text = preg_replace_callback('/
1307
+ ( # Wrap whole match in $1
1308
+ (?>
1309
+ ^[ ]*>[ ]? # ">" at the start of a line
1310
+ .+\n # rest of the first line
1311
+ (.+\n)* # subsequent consecutive lines
1312
+ \n* # blanks
1313
+ )+
1314
+ )
1315
+ /xm',
1316
+ array(&$this, '_doBlockQuotes_callback'), $text);
1317
+
1318
+ return $text;
1319
+ }
1320
+ function _doBlockQuotes_callback($matches) {
1321
+ $bq = $matches[1];
1322
+ # trim one level of quoting - trim whitespace-only lines
1323
+ $bq = preg_replace('/^[ ]*>[ ]?|^[ ]+$/m', '', $bq);
1324
+ $bq = $this->runBlockGamut($bq); # recurse
1325
+
1326
+ $bq = preg_replace('/^/m', " ", $bq);
1327
+ # These leading spaces cause problem with <pre> content,
1328
+ # so we need to fix that:
1329
+ $bq = preg_replace_callback('{(\s*<pre>.+?</pre>)}sx',
1330
+ array(&$this, '_doBlockQuotes_callback2'), $bq);
1331
+
1332
+ return "\n". $this->hashBlock("<blockquote>\n$bq\n</blockquote>")."\n\n";
1333
+ }
1334
+ function _doBlockQuotes_callback2($matches) {
1335
+ $pre = $matches[1];
1336
+ $pre = preg_replace('/^ /m', '', $pre);
1337
+ return $pre;
1338
+ }
1339
+
1340
+
1341
+ function formParagraphs($text) {
1342
+ #
1343
+ # Params:
1344
+ # $text - string to process with html <p> tags
1345
+ #
1346
+ # Strip leading and trailing lines:
1347
+ $text = preg_replace('/\A\n+|\n+\z/', '', $text);
1348
+
1349
+ $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
1350
+
1351
+ #
1352
+ # Wrap <p> tags and unhashify HTML blocks
1353
+ #
1354
+ foreach ($grafs as $key => $value) {
1355
+ if (!preg_match('/^B\x1A[0-9]+B$/', $value)) {
1356
+ # Is a paragraph.
1357
+ $value = $this->runSpanGamut($value);
1358
+ $value = preg_replace('/^([ ]*)/', "<p>", $value);
1359
+ $value .= "</p>";
1360
+ $grafs[$key] = $this->unhash($value);
1361
+ }
1362
+ else {
1363
+ # Is a block.
1364
+ # Modify elements of @grafs in-place...
1365
+ $graf = $value;
1366
+ $block = $this->html_hashes[$graf];
1367
+ $graf = $block;
1368
+ // if (preg_match('{
1369
+ // \A
1370
+ // ( # $1 = <div> tag
1371
+ // <div \s+
1372
+ // [^>]*
1373
+ // \b
1374
+ // markdown\s*=\s* ([\'"]) # $2 = attr quote char
1375
+ // 1
1376
+ // \2
1377
+ // [^>]*
1378
+ // >
1379
+ // )
1380
+ // ( # $3 = contents
1381
+ // .*
1382
+ // )
1383
+ // (</div>) # $4 = closing tag
1384
+ // \z
1385
+ // }xs', $block, $matches))
1386
+ // {
1387
+ // list(, $div_open, , $div_content, $div_close) = $matches;
1388
+ //
1389
+ // # We can't call Markdown(), because that resets the hash;
1390
+ // # that initialization code should be pulled into its own sub, though.
1391
+ // $div_content = $this->hashHTMLBlocks($div_content);
1392
+ //
1393
+ // # Run document gamut methods on the content.
1394
+ // foreach ($this->document_gamut as $method => $priority) {
1395
+ // $div_content = $this->$method($div_content);
1396
+ // }
1397
+ //
1398
+ // $div_open = preg_replace(
1399
+ // '{\smarkdown\s*=\s*([\'"]).+?\1}', '', $div_open);
1400
+ //
1401
+ // $graf = $div_open . "\n" . $div_content . "\n" . $div_close;
1402
+ // }
1403
+ $grafs[$key] = $graf;
1404
+ }
1405
+ }
1406
+
1407
+ return implode("\n\n", $grafs);
1408
+ }
1409
+
1410
+
1411
+ function encodeAttribute($text) {
1412
+ #
1413
+ # Encode text for a double-quoted HTML attribute. This function
1414
+ # is *not* suitable for attributes enclosed in single quotes.
1415
+ #
1416
+ $text = $this->encodeAmpsAndAngles($text);
1417
+ $text = str_replace('"', '&quot;', $text);
1418
+ return $text;
1419
+ }
1420
+
1421
+
1422
+ function encodeAmpsAndAngles($text) {
1423
+ #
1424
+ # Smart processing for ampersands and angle brackets that need to
1425
+ # be encoded. Valid character entities are left alone unless the
1426
+ # no-entities mode is set.
1427
+ #
1428
+ if ($this->no_entities) {
1429
+ $text = str_replace('&', '&amp;', $text);
1430
+ } else {
1431
+ # Ampersand-encoding based entirely on Nat Irons's Amputator
1432
+ # MT plugin: <http://bumppo.net/projects/amputator/>
1433
+ $text = preg_replace('/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/',
1434
+ '&amp;', $text);;
1435
+ }
1436
+ # Encode remaining <'s
1437
+ $text = str_replace('<', '&lt;', $text);
1438
+
1439
+ return $text;
1440
+ }
1441
+
1442
+
1443
+ function doAutoLinks($text) {
1444
+ $text = preg_replace_callback('{<((https?|ftp|dict):[^\'">\s]+)>}i',
1445
+ array(&$this, '_doAutoLinks_url_callback'), $text);
1446
+
1447
+ # Email addresses: <address@domain.foo>
1448
+ $text = preg_replace_callback('{
1449
+ <
1450
+ (?:mailto:)?
1451
+ (
1452
+ (?:
1453
+ [-!#$%&\'*+/=?^_`.{|}~\w\x80-\xFF]+
1454
+ |
1455
+ ".*?"
1456
+ )
1457
+ \@
1458
+ (?:
1459
+ [-a-z0-9\x80-\xFF]+(\.[-a-z0-9\x80-\xFF]+)*\.[a-z]+
1460
+ |
1461
+ \[[\d.a-fA-F:]+\] # IPv4 & IPv6
1462
+ )
1463
+ )
1464
+ >
1465
+ }xi',
1466
+ array(&$this, '_doAutoLinks_email_callback'), $text);
1467
+
1468
+ return $text;
1469
+ }
1470
+ function _doAutoLinks_url_callback($matches) {
1471
+ $url = $this->encodeAttribute($matches[1]);
1472
+ $link = "<a href=\"$url\">$url</a>";
1473
+ return $this->hashPart($link);
1474
+ }
1475
+ function _doAutoLinks_email_callback($matches) {
1476
+ $address = $matches[1];
1477
+ $link = $this->encodeEmailAddress($address);
1478
+ return $this->hashPart($link);
1479
+ }
1480
+
1481
+
1482
+ function encodeEmailAddress($addr) {
1483
+ #
1484
+ # Input: an email address, e.g. "foo@example.com"
1485
+ #
1486
+ # Output: the email address as a mailto link, with each character
1487
+ # of the address encoded as either a decimal or hex entity, in
1488
+ # the hopes of foiling most address harvesting spam bots. E.g.:
1489
+ #
1490
+ # <p><a href="&#109;&#x61;&#105;&#x6c;&#116;&#x6f;&#58;&#x66;o&#111;
1491
+ # &#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;&#101;&#46;&#x63;&#111;
1492
+ # &#x6d;">&#x66;o&#111;&#x40;&#101;&#x78;&#97;&#x6d;&#112;&#x6c;
1493
+ # &#101;&#46;&#x63;&#111;&#x6d;</a></p>
1494
+ #
1495
+ # Based by a filter by Matthew Wickline, posted to BBEdit-Talk.
1496
+ # With some optimizations by Milian Wolff.
1497
+ #
1498
+ $addr = "mailto:" . $addr;
1499
+ $chars = preg_split('/(?<!^)(?!$)/', $addr);
1500
+ $seed = (int)abs(crc32($addr) / strlen($addr)); # Deterministic seed.
1501
+
1502
+ foreach ($chars as $key => $char) {
1503
+ $ord = ord($char);
1504
+ # Ignore non-ascii chars.
1505
+ if ($ord < 128) {
1506
+ $r = ($seed * (1 + $key)) % 100; # Pseudo-random function.
1507
+ # roughly 10% raw, 45% hex, 45% dec
1508
+ # '@' *must* be encoded. I insist.
1509
+ if ($r > 90 && $char != '@') /* do nothing */;
1510
+ else if ($r < 45) $chars[$key] = '&#x'.dechex($ord).';';
1511
+ else $chars[$key] = '&#'.$ord.';';
1512
+ }
1513
+ }
1514
+
1515
+ $addr = implode('', $chars);
1516
+ $text = implode('', array_slice($chars, 7)); # text without `mailto:`
1517
+ $addr = "<a href=\"$addr\">$text</a>";
1518
+
1519
+ return $addr;
1520
+ }
1521
+
1522
+
1523
+ function parseSpan($str) {
1524
+ #
1525
+ # Take the string $str and parse it into tokens, hashing embeded HTML,
1526
+ # escaped characters and handling code spans.
1527
+ #
1528
+ $output = '';
1529
+
1530
+ $span_re = '{
1531
+ (
1532
+ \\\\'.$this->escape_chars_re.'
1533
+ |
1534
+ (?<![`\\\\])
1535
+ `+ # code span marker
1536
+ '.( $this->no_markup ? '' : '
1537
+ |
1538
+ <!-- .*? --> # comment
1539
+ |
1540
+ <\?.*?\?> | <%.*?%> # processing instruction
1541
+ |
1542
+ <[/!$]?[-a-zA-Z0-9:_]+ # regular tags
1543
+ (?>
1544
+ \s
1545
+ (?>[^"\'>]+|"[^"]*"|\'[^\']*\')*
1546
+ )?
1547
+ >
1548
+ ').'
1549
+ )
1550
+ }xs';
1551
+
1552
+ while (1) {
1553
+ #
1554
+ # Each loop iteration seach for either the next tag, the next
1555
+ # openning code span marker, or the next escaped character.
1556
+ # Each token is then passed to handleSpanToken.
1557
+ #
1558
+ $parts = preg_split($span_re, $str, 2, PREG_SPLIT_DELIM_CAPTURE);
1559
+
1560
+ # Create token from text preceding tag.
1561
+ if ($parts[0] != "") {
1562
+ $output .= $parts[0];
1563
+ }
1564
+
1565
+ # Check if we reach the end.
1566
+ if (isset($parts[1])) {
1567
+ $output .= $this->handleSpanToken($parts[1], $parts[2]);
1568
+ $str = $parts[2];
1569
+ }
1570
+ else {
1571
+ break;
1572
+ }
1573
+ }
1574
+
1575
+ return $output;
1576
+ }
1577
+
1578
+
1579
+ function handleSpanToken($token, &$str) {
1580
+ #
1581
+ # Handle $token provided by parseSpan by determining its nature and
1582
+ # returning the corresponding value that should replace it.
1583
+ #
1584
+ switch ($token{0}) {
1585
+ case "\\":
1586
+ return $this->hashPart("&#". ord($token{1}). ";");
1587
+ case "`":
1588
+ # Search for end marker in remaining text.
1589
+ if (preg_match('/^(.*?[^`])'.preg_quote($token).'(?!`)(.*)$/sm',
1590
+ $str, $matches))
1591
+ {
1592
+ $str = $matches[2];
1593
+ $codespan = $this->makeCodeSpan($matches[1]);
1594
+ return $this->hashPart($codespan);
1595
+ }
1596
+ return $token; // return as text since no ending marker found.
1597
+ default:
1598
+ return $this->hashPart($token);
1599
+ }
1600
+ }
1601
+
1602
+
1603
+ function outdent($text) {
1604
+ #
1605
+ # Remove one level of line-leading tabs or spaces
1606
+ #
1607
+ return preg_replace('/^(\t|[ ]{1,'.$this->tab_width.'})/m', '', $text);
1608
+ }
1609
+
1610
+
1611
+ # String length function for detab. `_initDetab` will create a function to
1612
+ # hanlde UTF-8 if the default function does not exist.
1613
+ var $utf8_strlen = 'mb_strlen';
1614
+
1615
+ function detab($text) {
1616
+ #
1617
+ # Replace tabs with the appropriate amount of space.
1618
+ #
1619
+ # For each line we separate the line in blocks delemited by
1620
+ # tab characters. Then we reconstruct every line by adding the
1621
+ # appropriate number of space between each blocks.
1622
+
1623
+ $text = preg_replace_callback('/^.*\t.*$/m',
1624
+ array(&$this, '_detab_callback'), $text);
1625
+
1626
+ return $text;
1627
+ }
1628
+ function _detab_callback($matches) {
1629
+ $line = $matches[0];
1630
+ $strlen = $this->utf8_strlen; # strlen function for UTF-8.
1631
+
1632
+ # Split in blocks.
1633
+ $blocks = explode("\t", $line);
1634
+ # Add each blocks to the line.
1635
+ $line = $blocks[0];
1636
+ unset($blocks[0]); # Do not add first block twice.
1637
+ foreach ($blocks as $block) {
1638
+ # Calculate amount of space, insert spaces, insert block.
1639
+ $amount = $this->tab_width -
1640
+ $strlen($line, 'UTF-8') % $this->tab_width;
1641
+ $line .= str_repeat(" ", $amount) . $block;
1642
+ }
1643
+ return $line;
1644
+ }
1645
+ function _initDetab() {
1646
+ #
1647
+ # Check for the availability of the function in the `utf8_strlen` property
1648
+ # (initially `mb_strlen`). If the function is not available, create a
1649
+ # function that will loosely count the number of UTF-8 characters with a
1650
+ # regular expression.
1651
+ #
1652
+ if (function_exists($this->utf8_strlen)) return;
1653
+ $this->utf8_strlen = create_function('$text', 'return preg_match_all(
1654
+ "/[\\\\x00-\\\\xBF]|[\\\\xC0-\\\\xFF][\\\\x80-\\\\xBF]*/",
1655
+ $text, $m);');
1656
+ }
1657
+
1658
+
1659
+ function unhash($text) {
1660
+ #
1661
+ # Swap back in all the tags hashed by _HashHTMLBlocks.
1662
+ #
1663
+ return preg_replace_callback('/(.)\x1A[0-9]+\1/',
1664
+ array(&$this, '_unhash_callback'), $text);
1665
+ }
1666
+ function _unhash_callback($matches) {
1667
+ return $this->html_hashes[$matches[0]];
1668
+ }
1669
+
1670
+ }
1671
+
1672
+
1673
+ #
1674
+ # Markdown Extra Parser Class
1675
+ #
1676
+
1677
+ class MarkdownExtra_Parser extends Markdown_Parser {
1678
+
1679
+ # Prefix for footnote ids.
1680
+ var $fn_id_prefix = "";
1681
+
1682
+ # Optional title attribute for footnote links and backlinks.
1683
+ var $fn_link_title = MARKDOWN_FN_LINK_TITLE;
1684
+ var $fn_backlink_title = MARKDOWN_FN_BACKLINK_TITLE;
1685
+
1686
+ # Optional class attribute for footnote links and backlinks.
1687
+ var $fn_link_class = MARKDOWN_FN_LINK_CLASS;
1688
+ var $fn_backlink_class = MARKDOWN_FN_BACKLINK_CLASS;
1689
+
1690
+ # Predefined abbreviations.
1691
+ var $predef_abbr = array();
1692
+
1693
+
1694
+ function MarkdownExtra_Parser() {
1695
+ #
1696
+ # Constructor function. Initialize the parser object.
1697
+ #
1698
+ # Add extra escapable characters before parent constructor
1699
+ # initialize the table.
1700
+ $this->escape_chars .= ':|';
1701
+
1702
+ # Insert extra document, block, and span transformations.
1703
+ # Parent constructor will do the sorting.
1704
+ $this->document_gamut += array(
1705
+ "doFencedCodeBlocks" => 5,
1706
+ "stripFootnotes" => 15,
1707
+ "stripAbbreviations" => 25,
1708
+ "appendFootnotes" => 50,
1709
+ );
1710
+ $this->block_gamut += array(
1711
+ "doFencedCodeBlocks" => 5,
1712
+ "doTables" => 15,
1713
+ "doDefLists" => 45,
1714
+ );
1715
+ $this->span_gamut += array(
1716
+ "doFootnotes" => 5,
1717
+ "doAbbreviations" => 70,
1718
+ );
1719
+
1720
+ parent::Markdown_Parser();
1721
+ }
1722
+
1723
+
1724
+ # Extra variables used during extra transformations.
1725
+ var $footnotes = array();
1726
+ var $footnotes_ordered = array();
1727
+ var $abbr_desciptions = array();
1728
+ var $abbr_word_re = '';
1729
+
1730
+ # Give the current footnote number.
1731
+ var $footnote_counter = 1;
1732
+
1733
+
1734
+ function setup() {
1735
+ #
1736
+ # Setting up Extra-specific variables.
1737
+ #
1738
+ parent::setup();
1739
+
1740
+ $this->footnotes = array();
1741
+ $this->footnotes_ordered = array();
1742
+ $this->abbr_desciptions = array();
1743
+ $this->abbr_word_re = '';
1744
+ $this->footnote_counter = 1;
1745
+
1746
+ foreach ($this->predef_abbr as $abbr_word => $abbr_desc) {
1747
+ if ($this->abbr_word_re)
1748
+ $this->abbr_word_re .= '|';
1749
+ $this->abbr_word_re .= preg_quote($abbr_word);
1750
+ $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
1751
+ }
1752
+ }
1753
+
1754
+ function teardown() {
1755
+ #
1756
+ # Clearing Extra-specific variables.
1757
+ #
1758
+ $this->footnotes = array();
1759
+ $this->footnotes_ordered = array();
1760
+ $this->abbr_desciptions = array();
1761
+ $this->abbr_word_re = '';
1762
+
1763
+ parent::teardown();
1764
+ }
1765
+
1766
+
1767
+ ### HTML Block Parser ###
1768
+
1769
+ # Tags that are always treated as block tags:
1770
+ var $block_tags_re = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|address|form|fieldset|iframe|hr|legend';
1771
+
1772
+ # Tags treated as block tags only if the opening tag is alone on it's line:
1773
+ var $context_block_tags_re = 'script|noscript|math|ins|del';
1774
+
1775
+ # Tags where markdown="1" default to span mode:
1776
+ var $contain_span_tags_re = 'p|h[1-6]|li|dd|dt|td|th|legend|address';
1777
+
1778
+ # Tags which must not have their contents modified, no matter where
1779
+ # they appear:
1780
+ var $clean_tags_re = 'script|math';
1781
+
1782
+ # Tags that do not need to be closed.
1783
+ var $auto_close_tags_re = 'hr|img';
1784
+
1785
+
1786
+ function hashHTMLBlocks($text) {
1787
+ #
1788
+ # Hashify HTML Blocks and "clean tags".
1789
+ #
1790
+ # We only want to do this for block-level HTML tags, such as headers,
1791
+ # lists, and tables. That's because we still want to wrap <p>s around
1792
+ # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
1793
+ # phrase emphasis, and spans. The list of tags we're looking for is
1794
+ # hard-coded.
1795
+ #
1796
+ # This works by calling _HashHTMLBlocks_InMarkdown, which then calls
1797
+ # _HashHTMLBlocks_InHTML when it encounter block tags. When the markdown="1"
1798
+ # attribute is found whitin a tag, _HashHTMLBlocks_InHTML calls back
1799
+ # _HashHTMLBlocks_InMarkdown to handle the Markdown syntax within the tag.
1800
+ # These two functions are calling each other. It's recursive!
1801
+ #
1802
+ #
1803
+ # Call the HTML-in-Markdown hasher.
1804
+ #
1805
+ list($text, ) = $this->_hashHTMLBlocks_inMarkdown($text);
1806
+
1807
+ return $text;
1808
+ }
1809
+ function _hashHTMLBlocks_inMarkdown($text, $indent = 0,
1810
+ $enclosing_tag_re = '', $span = false)
1811
+ {
1812
+ #
1813
+ # Parse markdown text, calling _HashHTMLBlocks_InHTML for block tags.
1814
+ #
1815
+ # * $indent is the number of space to be ignored when checking for code
1816
+ # blocks. This is important because if we don't take the indent into
1817
+ # account, something like this (which looks right) won't work as expected:
1818
+ #
1819
+ # <div>
1820
+ # <div markdown="1">
1821
+ # Hello World. <-- Is this a Markdown code block or text?
1822
+ # </div> <-- Is this a Markdown code block or a real tag?
1823
+ # <div>
1824
+ #
1825
+ # If you don't like this, just don't indent the tag on which
1826
+ # you apply the markdown="1" attribute.
1827
+ #
1828
+ # * If $enclosing_tag_re is not empty, stops at the first unmatched closing
1829
+ # tag with that name. Nested tags supported.
1830
+ #
1831
+ # * If $span is true, text inside must treated as span. So any double
1832
+ # newline will be replaced by a single newline so that it does not create
1833
+ # paragraphs.
1834
+ #
1835
+ # Returns an array of that form: ( processed text , remaining text )
1836
+ #
1837
+ if ($text === '') return array('', '');
1838
+
1839
+ # Regex to check for the presense of newlines around a block tag.
1840
+ $newline_before_re = '/(?:^\n?|\n\n)*$/';
1841
+ $newline_after_re =
1842
+ '{
1843
+ ^ # Start of text following the tag.
1844
+ (?>[ ]*<!--.*?-->)? # Optional comment.
1845
+ [ ]*\n # Must be followed by newline.
1846
+ }xs';
1847
+
1848
+ # Regex to match any tag.
1849
+ $block_tag_re =
1850
+ '{
1851
+ ( # $2: Capture hole tag.
1852
+ </? # Any opening or closing tag.
1853
+ (?> # Tag name.
1854
+ '.$this->block_tags_re.' |
1855
+ '.$this->context_block_tags_re.' |
1856
+ '.$this->clean_tags_re.' |
1857
+ (?!\s)'.$enclosing_tag_re.'
1858
+ )
1859
+ (?:
1860
+ (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
1861
+ (?>
1862
+ ".*?" | # Double quotes (can contain `>`)
1863
+ \'.*?\' | # Single quotes (can contain `>`)
1864
+ .+? # Anything but quotes and `>`.
1865
+ )*?
1866
+ )?
1867
+ > # End of tag.
1868
+ |
1869
+ <!-- .*? --> # HTML Comment
1870
+ |
1871
+ <\?.*?\?> | <%.*?%> # Processing instruction
1872
+ |
1873
+ <!\[CDATA\[.*?\]\]> # CData Block
1874
+ |
1875
+ # Code span marker
1876
+ `+
1877
+ '. ( !$span ? ' # If not in span.
1878
+ |
1879
+ # Indented code block
1880
+ (?: ^[ ]*\n | ^ | \n[ ]*\n )
1881
+ [ ]{'.($indent+4).'}[^\n]* \n
1882
+ (?>
1883
+ (?: [ ]{'.($indent+4).'}[^\n]* | [ ]* ) \n
1884
+ )*
1885
+ |
1886
+ # Fenced code block marker
1887
+ (?> ^ | \n )
1888
+ [ ]{'.($indent).'}~~~+[ ]*\n
1889
+ ' : '' ). ' # End (if not is span).
1890
+ )
1891
+ }xs';
1892
+
1893
+
1894
+ $depth = 0; # Current depth inside the tag tree.
1895
+ $parsed = ""; # Parsed text that will be returned.
1896
+
1897
+ #
1898
+ # Loop through every tag until we find the closing tag of the parent
1899
+ # or loop until reaching the end of text if no parent tag specified.
1900
+ #
1901
+ do {
1902
+ #
1903
+ # Split the text using the first $tag_match pattern found.
1904
+ # Text before pattern will be first in the array, text after
1905
+ # pattern will be at the end, and between will be any catches made
1906
+ # by the pattern.
1907
+ #
1908
+ $parts = preg_split($block_tag_re, $text, 2,
1909
+ PREG_SPLIT_DELIM_CAPTURE);
1910
+
1911
+ # If in Markdown span mode, add a empty-string span-level hash
1912
+ # after each newline to prevent triggering any block element.
1913
+ if ($span) {
1914
+ $void = $this->hashPart("", ':');
1915
+ $newline = "$void\n";
1916
+ $parts[0] = $void . str_replace("\n", $newline, $parts[0]) . $void;
1917
+ }
1918
+
1919
+ $parsed .= $parts[0]; # Text before current tag.
1920
+
1921
+ # If end of $text has been reached. Stop loop.
1922
+ if (count($parts) < 3) {
1923
+ $text = "";
1924
+ break;
1925
+ }
1926
+
1927
+ $tag = $parts[1]; # Tag to handle.
1928
+ $text = $parts[2]; # Remaining text after current tag.
1929
+ $tag_re = preg_quote($tag); # For use in a regular expression.
1930
+
1931
+ #
1932
+ # Check for: Code span marker
1933
+ #
1934
+ if ($tag{0} == "`") {
1935
+ # Find corresponding end marker.
1936
+ $tag_re = preg_quote($tag);
1937
+ if (preg_match('{^(?>.+?|\n(?!\n))*?(?<!`)'.$tag_re.'(?!`)}',
1938
+ $text, $matches))
1939
+ {
1940
+ # End marker found: pass text unchanged until marker.
1941
+ $parsed .= $tag . $matches[0];
1942
+ $text = substr($text, strlen($matches[0]));
1943
+ }
1944
+ else {
1945
+ # Unmatched marker: just skip it.
1946
+ $parsed .= $tag;
1947
+ }
1948
+ }
1949
+ #
1950
+ # Check for: Indented code block.
1951
+ #
1952
+ else if ($tag{0} == "\n" || $tag{0} == " ") {
1953
+ # Indented code block: pass it unchanged, will be handled
1954
+ # later.
1955
+ $parsed .= $tag;
1956
+ }
1957
+ #
1958
+ # Check for: Fenced code block marker.
1959
+ #
1960
+ else if ($tag{0} == "~") {
1961
+ # Fenced code block marker: find matching end marker.
1962
+ $tag_re = preg_quote(trim($tag));
1963
+ if (preg_match('{^(?>.*\n)+?'.$tag_re.' *\n}', $text,
1964
+ $matches))
1965
+ {
1966
+ # End marker found: pass text unchanged until marker.
1967
+ $parsed .= $tag . $matches[0];
1968
+ $text = substr($text, strlen($matches[0]));
1969
+ }
1970
+ else {
1971
+ # No end marker: just skip it.
1972
+ $parsed .= $tag;
1973
+ }
1974
+ }
1975
+ #
1976
+ # Check for: Opening Block level tag or
1977
+ # Opening Context Block tag (like ins and del)
1978
+ # used as a block tag (tag is alone on it's line).
1979
+ #
1980
+ else if (preg_match('{^<(?:'.$this->block_tags_re.')\b}', $tag) ||
1981
+ ( preg_match('{^<(?:'.$this->context_block_tags_re.')\b}', $tag) &&
1982
+ preg_match($newline_before_re, $parsed) &&
1983
+ preg_match($newline_after_re, $text) )
1984
+ )
1985
+ {
1986
+ # Need to parse tag and following text using the HTML parser.
1987
+ list($block_text, $text) =
1988
+ $this->_hashHTMLBlocks_inHTML($tag . $text, "hashBlock", true);
1989
+
1990
+ # Make sure it stays outside of any paragraph by adding newlines.
1991
+ $parsed .= "\n\n$block_text\n\n";
1992
+ }
1993
+ #
1994
+ # Check for: Clean tag (like script, math)
1995
+ # HTML Comments, processing instructions.
1996
+ #
1997
+ else if (preg_match('{^<(?:'.$this->clean_tags_re.')\b}', $tag) ||
1998
+ $tag{1} == '!' || $tag{1} == '?')
1999
+ {
2000
+ # Need to parse tag and following text using the HTML parser.
2001
+ # (don't check for markdown attribute)
2002
+ list($block_text, $text) =
2003
+ $this->_hashHTMLBlocks_inHTML($tag . $text, "hashClean", false);
2004
+
2005
+ $parsed .= $block_text;
2006
+ }
2007
+ #
2008
+ # Check for: Tag with same name as enclosing tag.
2009
+ #
2010
+ else if ($enclosing_tag_re !== '' &&
2011
+ # Same name as enclosing tag.
2012
+ preg_match('{^</?(?:'.$enclosing_tag_re.')\b}', $tag))
2013
+ {
2014
+ #
2015
+ # Increase/decrease nested tag count.
2016
+ #
2017
+ if ($tag{1} == '/') $depth--;
2018
+ else if ($tag{strlen($tag)-2} != '/') $depth++;
2019
+
2020
+ if ($depth < 0) {
2021
+ #
2022
+ # Going out of parent element. Clean up and break so we
2023
+ # return to the calling function.
2024
+ #
2025
+ $text = $tag . $text;
2026
+ break;
2027
+ }
2028
+
2029
+ $parsed .= $tag;
2030
+ }
2031
+ else {
2032
+ $parsed .= $tag;
2033
+ }
2034
+ } while ($depth >= 0);
2035
+
2036
+ return array($parsed, $text);
2037
+ }
2038
+ function _hashHTMLBlocks_inHTML($text, $hash_method, $md_attr) {
2039
+ #
2040
+ # Parse HTML, calling _HashHTMLBlocks_InMarkdown for block tags.
2041
+ #
2042
+ # * Calls $hash_method to convert any blocks.
2043
+ # * Stops when the first opening tag closes.
2044
+ # * $md_attr indicate if the use of the `markdown="1"` attribute is allowed.
2045
+ # (it is not inside clean tags)
2046
+ #
2047
+ # Returns an array of that form: ( processed text , remaining text )
2048
+ #
2049
+ if ($text === '') return array('', '');
2050
+
2051
+ # Regex to match `markdown` attribute inside of a tag.
2052
+ $markdown_attr_re = '
2053
+ {
2054
+ \s* # Eat whitespace before the `markdown` attribute
2055
+ markdown
2056
+ \s*=\s*
2057
+ (?>
2058
+ (["\']) # $1: quote delimiter
2059
+ (.*?) # $2: attribute value
2060
+ \1 # matching delimiter
2061
+ |
2062
+ ([^\s>]*) # $3: unquoted attribute value
2063
+ )
2064
+ () # $4: make $3 always defined (avoid warnings)
2065
+ }xs';
2066
+
2067
+ # Regex to match any tag.
2068
+ $tag_re = '{
2069
+ ( # $2: Capture hole tag.
2070
+ </? # Any opening or closing tag.
2071
+ [\w:$]+ # Tag name.
2072
+ (?:
2073
+ (?=[\s"\'/a-zA-Z0-9]) # Allowed characters after tag name.
2074
+ (?>
2075
+ ".*?" | # Double quotes (can contain `>`)
2076
+ \'.*?\' | # Single quotes (can contain `>`)
2077
+ .+? # Anything but quotes and `>`.
2078
+ )*?
2079
+ )?
2080
+ > # End of tag.
2081
+ |
2082
+ <!-- .*? --> # HTML Comment
2083
+ |
2084
+ <\?.*?\?> | <%.*?%> # Processing instruction
2085
+ |
2086
+ <!\[CDATA\[.*?\]\]> # CData Block
2087
+ )
2088
+ }xs';
2089
+
2090
+ $original_text = $text; # Save original text in case of faliure.
2091
+
2092
+ $depth = 0; # Current depth inside the tag tree.
2093
+ $block_text = ""; # Temporary text holder for current text.
2094
+ $parsed = ""; # Parsed text that will be returned.
2095
+
2096
+ #
2097
+ # Get the name of the starting tag.
2098
+ # (This pattern makes $base_tag_name_re safe without quoting.)
2099
+ #
2100
+ if (preg_match('/^<([\w:$]*)\b/', $text, $matches))
2101
+ $base_tag_name_re = $matches[1];
2102
+
2103
+ #
2104
+ # Loop through every tag until we find the corresponding closing tag.
2105
+ #
2106
+ do {
2107
+ #
2108
+ # Split the text using the first $tag_match pattern found.
2109
+ # Text before pattern will be first in the array, text after
2110
+ # pattern will be at the end, and between will be any catches made
2111
+ # by the pattern.
2112
+ #
2113
+ $parts = preg_split($tag_re, $text, 2, PREG_SPLIT_DELIM_CAPTURE);
2114
+
2115
+ if (count($parts) < 3) {
2116
+ #
2117
+ # End of $text reached with unbalenced tag(s).
2118
+ # In that case, we return original text unchanged and pass the
2119
+ # first character as filtered to prevent an infinite loop in the
2120
+ # parent function.
2121
+ #
2122
+ return array($original_text{0}, substr($original_text, 1));
2123
+ }
2124
+
2125
+ $block_text .= $parts[0]; # Text before current tag.
2126
+ $tag = $parts[1]; # Tag to handle.
2127
+ $text = $parts[2]; # Remaining text after current tag.
2128
+
2129
+ #
2130
+ # Check for: Auto-close tag (like <hr/>)
2131
+ # Comments and Processing Instructions.
2132
+ #
2133
+ if (preg_match('{^</?(?:'.$this->auto_close_tags_re.')\b}', $tag) ||
2134
+ $tag{1} == '!' || $tag{1} == '?')
2135
+ {
2136
+ # Just add the tag to the block as if it was text.
2137
+ $block_text .= $tag;
2138
+ }
2139
+ else {
2140
+ #
2141
+ # Increase/decrease nested tag count. Only do so if
2142
+ # the tag's name match base tag's.
2143
+ #
2144
+ if (preg_match('{^</?'.$base_tag_name_re.'\b}', $tag)) {
2145
+ if ($tag{1} == '/') $depth--;
2146
+ else if ($tag{strlen($tag)-2} != '/') $depth++;
2147
+ }
2148
+
2149
+ #
2150
+ # Check for `markdown="1"` attribute and handle it.
2151
+ #
2152
+ if ($md_attr &&
2153
+ preg_match($markdown_attr_re, $tag, $attr_m) &&
2154
+ preg_match('/^1|block|span$/', $attr_m[2] . $attr_m[3]))
2155
+ {
2156
+ # Remove `markdown` attribute from opening tag.
2157
+ $tag = preg_replace($markdown_attr_re, '', $tag);
2158
+
2159
+ # Check if text inside this tag must be parsed in span mode.
2160
+ $this->mode = $attr_m[2] . $attr_m[3];
2161
+ $span_mode = $this->mode == 'span' || $this->mode != 'block' &&
2162
+ preg_match('{^<(?:'.$this->contain_span_tags_re.')\b}', $tag);
2163
+
2164
+ # Calculate indent before tag.
2165
+ if (preg_match('/(?:^|\n)( *?)(?! ).*?$/', $block_text, $matches)) {
2166
+ $strlen = $this->utf8_strlen;
2167
+ $indent = $strlen($matches[1], 'UTF-8');
2168
+ } else {
2169
+ $indent = 0;
2170
+ }
2171
+
2172
+ # End preceding block with this tag.
2173
+ $block_text .= $tag;
2174
+ $parsed .= $this->$hash_method($block_text);
2175
+
2176
+ # Get enclosing tag name for the ParseMarkdown function.
2177
+ # (This pattern makes $tag_name_re safe without quoting.)
2178
+ preg_match('/^<([\w:$]*)\b/', $tag, $matches);
2179
+ $tag_name_re = $matches[1];
2180
+
2181
+ # Parse the content using the HTML-in-Markdown parser.
2182
+ list ($block_text, $text)
2183
+ = $this->_hashHTMLBlocks_inMarkdown($text, $indent,
2184
+ $tag_name_re, $span_mode);
2185
+
2186
+ # Outdent markdown text.
2187
+ if ($indent > 0) {
2188
+ $block_text = preg_replace("/^[ ]{1,$indent}/m", "",
2189
+ $block_text);
2190
+ }
2191
+
2192
+ # Append tag content to parsed text.
2193
+ if (!$span_mode) $parsed .= "\n\n$block_text\n\n";
2194
+ else $parsed .= "$block_text";
2195
+
2196
+ # Start over a new block.
2197
+ $block_text = "";
2198
+ }
2199
+ else $block_text .= $tag;
2200
+ }
2201
+
2202
+ } while ($depth > 0);
2203
+
2204
+ #
2205
+ # Hash last block text that wasn't processed inside the loop.
2206
+ #
2207
+ $parsed .= $this->$hash_method($block_text);
2208
+
2209
+ return array($parsed, $text);
2210
+ }
2211
+
2212
+
2213
+ function hashClean($text) {
2214
+ #
2215
+ # Called whenever a tag must be hashed when a function insert a "clean" tag
2216
+ # in $text, it pass through this function and is automaticaly escaped,
2217
+ # blocking invalid nested overlap.
2218
+ #
2219
+ return $this->hashPart($text, 'C');
2220
+ }
2221
+
2222
+
2223
+ function doHeaders($text) {
2224
+ #
2225
+ # Redefined to add id attribute support.
2226
+ #
2227
+ # Setext-style headers:
2228
+ # Header 1 {#header1}
2229
+ # ========
2230
+ #
2231
+ # Header 2 {#header2}
2232
+ # --------
2233
+ #
2234
+ $text = preg_replace_callback(
2235
+ '{
2236
+ (^.+?) # $1: Header text
2237
+ (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # $2: Id attribute
2238
+ [ ]*\n(=+|-+)[ ]*\n+ # $3: Header footer
2239
+ }mx',
2240
+ array(&$this, '_doHeaders_callback_setext'), $text);
2241
+
2242
+ # atx-style headers:
2243
+ # # Header 1 {#header1}
2244
+ # ## Header 2 {#header2}
2245
+ # ## Header 2 with closing hashes ## {#header3}
2246
+ # ...
2247
+ # ###### Header 6 {#header2}
2248
+ #
2249
+ $text = preg_replace_callback('{
2250
+ ^(\#{1,6}) # $1 = string of #\'s
2251
+ [ ]*
2252
+ (.+?) # $2 = Header text
2253
+ [ ]*
2254
+ \#* # optional closing #\'s (not counted)
2255
+ (?:[ ]+\{\#([-_:a-zA-Z0-9]+)\})? # id attribute
2256
+ [ ]*
2257
+ \n+
2258
+ }xm',
2259
+ array(&$this, '_doHeaders_callback_atx'), $text);
2260
+
2261
+ return $text;
2262
+ }
2263
+ function _doHeaders_attr($attr) {
2264
+ if (empty($attr)) return "";
2265
+ return " id=\"$attr\"";
2266
+ }
2267
+ function _doHeaders_callback_setext($matches) {
2268
+ if ($matches[3] == '-' && preg_match('{^- }', $matches[1]))
2269
+ return $matches[0];
2270
+ $level = $matches[3]{0} == '=' ? 1 : 2;
2271
+ $attr = $this->_doHeaders_attr($id =& $matches[2]);
2272
+ $block = "<h$level$attr>".$this->runSpanGamut($matches[1])."</h$level>";
2273
+ return "\n" . $this->hashBlock($block) . "\n\n";
2274
+ }
2275
+ function _doHeaders_callback_atx($matches) {
2276
+ $level = strlen($matches[1]);
2277
+ $attr = $this->_doHeaders_attr($id =& $matches[3]);
2278
+ $block = "<h$level$attr>".$this->runSpanGamut($matches[2])."</h$level>";
2279
+ return "\n" . $this->hashBlock($block) . "\n\n";
2280
+ }
2281
+
2282
+
2283
+ function doTables($text) {
2284
+ #
2285
+ # Form HTML tables.
2286
+ #
2287
+ $less_than_tab = $this->tab_width - 1;
2288
+ #
2289
+ # Find tables with leading pipe.
2290
+ #
2291
+ # | Header 1 | Header 2
2292
+ # | -------- | --------
2293
+ # | Cell 1 | Cell 2
2294
+ # | Cell 3 | Cell 4
2295
+ #
2296
+ $text = preg_replace_callback('
2297
+ {
2298
+ ^ # Start of a line
2299
+ [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2300
+ [|] # Optional leading pipe (present)
2301
+ (.+) \n # $1: Header row (at least one pipe)
2302
+
2303
+ [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2304
+ [|] ([ ]*[-:]+[-| :]*) \n # $2: Header underline
2305
+
2306
+ ( # $3: Cells
2307
+ (?>
2308
+ [ ]* # Allowed whitespace.
2309
+ [|] .* \n # Row content.
2310
+ )*
2311
+ )
2312
+ (?=\n|\Z) # Stop at final double newline.
2313
+ }xm',
2314
+ array(&$this, '_doTable_leadingPipe_callback'), $text);
2315
+
2316
+ #
2317
+ # Find tables without leading pipe.
2318
+ #
2319
+ # Header 1 | Header 2
2320
+ # -------- | --------
2321
+ # Cell 1 | Cell 2
2322
+ # Cell 3 | Cell 4
2323
+ #
2324
+ $text = preg_replace_callback('
2325
+ {
2326
+ ^ # Start of a line
2327
+ [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2328
+ (\S.*[|].*) \n # $1: Header row (at least one pipe)
2329
+
2330
+ [ ]{0,'.$less_than_tab.'} # Allowed whitespace.
2331
+ ([-:]+[ ]*[|][-| :]*) \n # $2: Header underline
2332
+
2333
+ ( # $3: Cells
2334
+ (?>
2335
+ .* [|] .* \n # Row content
2336
+ )*
2337
+ )
2338
+ (?=\n|\Z) # Stop at final double newline.
2339
+ }xm',
2340
+ array(&$this, '_DoTable_callback'), $text);
2341
+
2342
+ return $text;
2343
+ }
2344
+ function _doTable_leadingPipe_callback($matches) {
2345
+ $head = $matches[1];
2346
+ $underline = $matches[2];
2347
+ $content = $matches[3];
2348
+
2349
+ # Remove leading pipe for each row.
2350
+ $content = preg_replace('/^ *[|]/m', '', $content);
2351
+
2352
+ return $this->_doTable_callback(array($matches[0], $head, $underline, $content));
2353
+ }
2354
+ function _doTable_callback($matches) {
2355
+ $head = $matches[1];
2356
+ $underline = $matches[2];
2357
+ $content = $matches[3];
2358
+
2359
+ # Remove any tailing pipes for each line.
2360
+ $head = preg_replace('/[|] *$/m', '', $head);
2361
+ $underline = preg_replace('/[|] *$/m', '', $underline);
2362
+ $content = preg_replace('/[|] *$/m', '', $content);
2363
+
2364
+ # Reading alignement from header underline.
2365
+ $separators = preg_split('/ *[|] */', $underline);
2366
+ foreach ($separators as $n => $s) {
2367
+ if (preg_match('/^ *-+: *$/', $s)) $attr[$n] = ' align="right"';
2368
+ else if (preg_match('/^ *:-+: *$/', $s))$attr[$n] = ' align="center"';
2369
+ else if (preg_match('/^ *:-+ *$/', $s)) $attr[$n] = ' align="left"';
2370
+ else $attr[$n] = '';
2371
+ }
2372
+
2373
+ # Parsing span elements, including code spans, character escapes,
2374
+ # and inline HTML tags, so that pipes inside those gets ignored.
2375
+ $head = $this->parseSpan($head);
2376
+ $headers = preg_split('/ *[|] */', $head);
2377
+ $col_count = count($headers);
2378
+
2379
+ # Write column headers.
2380
+ $text = "<table>\n";
2381
+ $text .= "<thead>\n";
2382
+ $text .= "<tr>\n";
2383
+ foreach ($headers as $n => $header)
2384
+ $text .= " <th$attr[$n]>".$this->runSpanGamut(trim($header))."</th>\n";
2385
+ $text .= "</tr>\n";
2386
+ $text .= "</thead>\n";
2387
+
2388
+ # Split content by row.
2389
+ $rows = explode("\n", trim($content, "\n"));
2390
+
2391
+ $text .= "<tbody>\n";
2392
+ foreach ($rows as $row) {
2393
+ # Parsing span elements, including code spans, character escapes,
2394
+ # and inline HTML tags, so that pipes inside those gets ignored.
2395
+ $row = $this->parseSpan($row);
2396
+
2397
+ # Split row by cell.
2398
+ $row_cells = preg_split('/ *[|] */', $row, $col_count);
2399
+ $row_cells = array_pad($row_cells, $col_count, '');
2400
+
2401
+ $text .= "<tr>\n";
2402
+ foreach ($row_cells as $n => $cell)
2403
+ $text .= " <td$attr[$n]>".$this->runSpanGamut(trim($cell))."</td>\n";
2404
+ $text .= "</tr>\n";
2405
+ }
2406
+ $text .= "</tbody>\n";
2407
+ $text .= "</table>";
2408
+
2409
+ return $this->hashBlock($text) . "\n";
2410
+ }
2411
+
2412
+
2413
+ function doDefLists($text) {
2414
+ #
2415
+ # Form HTML definition lists.
2416
+ #
2417
+ $less_than_tab = $this->tab_width - 1;
2418
+
2419
+ # Re-usable pattern to match any entire dl list:
2420
+ $whole_list_re = '(?>
2421
+ ( # $1 = whole list
2422
+ ( # $2
2423
+ [ ]{0,'.$less_than_tab.'}
2424
+ ((?>.*\S.*\n)+) # $3 = defined term
2425
+ \n?
2426
+ [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2427
+ )
2428
+ (?s:.+?)
2429
+ ( # $4
2430
+ \z
2431
+ |
2432
+ \n{2,}
2433
+ (?=\S)
2434
+ (?! # Negative lookahead for another term
2435
+ [ ]{0,'.$less_than_tab.'}
2436
+ (?: \S.*\n )+? # defined term
2437
+ \n?
2438
+ [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2439
+ )
2440
+ (?! # Negative lookahead for another definition
2441
+ [ ]{0,'.$less_than_tab.'}:[ ]+ # colon starting definition
2442
+ )
2443
+ )
2444
+ )
2445
+ )'; // mx
2446
+
2447
+ $text = preg_replace_callback('{
2448
+ (?>\A\n?|(?<=\n\n))
2449
+ '.$whole_list_re.'
2450
+ }mx',
2451
+ array(&$this, '_doDefLists_callback'), $text);
2452
+
2453
+ return $text;
2454
+ }
2455
+ function _doDefLists_callback($matches) {
2456
+ # Re-usable patterns to match list item bullets and number markers:
2457
+ $list = $matches[1];
2458
+
2459
+ # Turn double returns into triple returns, so that we can make a
2460
+ # paragraph for the last item in a list, if necessary:
2461
+ $result = trim($this->processDefListItems($list));
2462
+ $result = "<dl>\n" . $result . "\n</dl>";
2463
+ return $this->hashBlock($result) . "\n\n";
2464
+ }
2465
+
2466
+
2467
+ function processDefListItems($list_str) {
2468
+ #
2469
+ # Process the contents of a single definition list, splitting it
2470
+ # into individual term and definition list items.
2471
+ #
2472
+ $less_than_tab = $this->tab_width - 1;
2473
+
2474
+ # trim trailing blank lines:
2475
+ $list_str = preg_replace("/\n{2,}\\z/", "\n", $list_str);
2476
+
2477
+ # Process definition terms.
2478
+ $list_str = preg_replace_callback('{
2479
+ (?>\A\n?|\n\n+) # leading line
2480
+ ( # definition terms = $1
2481
+ [ ]{0,'.$less_than_tab.'} # leading whitespace
2482
+ (?![:][ ]|[ ]) # negative lookahead for a definition
2483
+ # mark (colon) or more whitespace.
2484
+ (?> \S.* \n)+? # actual term (not whitespace).
2485
+ )
2486
+ (?=\n?[ ]{0,3}:[ ]) # lookahead for following line feed
2487
+ # with a definition mark.
2488
+ }xm',
2489
+ array(&$this, '_processDefListItems_callback_dt'), $list_str);
2490
+
2491
+ # Process actual definitions.
2492
+ $list_str = preg_replace_callback('{
2493
+ \n(\n+)? # leading line = $1
2494
+ ( # marker space = $2
2495
+ [ ]{0,'.$less_than_tab.'} # whitespace before colon
2496
+ [:][ ]+ # definition mark (colon)
2497
+ )
2498
+ ((?s:.+?)) # definition text = $3
2499
+ (?= \n+ # stop at next definition mark,
2500
+ (?: # next term or end of text
2501
+ [ ]{0,'.$less_than_tab.'} [:][ ] |
2502
+ <dt> | \z
2503
+ )
2504
+ )
2505
+ }xm',
2506
+ array(&$this, '_processDefListItems_callback_dd'), $list_str);
2507
+
2508
+ return $list_str;
2509
+ }
2510
+ function _processDefListItems_callback_dt($matches) {
2511
+ $terms = explode("\n", trim($matches[1]));
2512
+ $text = '';
2513
+ foreach ($terms as $term) {
2514
+ $term = $this->runSpanGamut(trim($term));
2515
+ $text .= "\n<dt>" . $term . "</dt>";
2516
+ }
2517
+ return $text . "\n";
2518
+ }
2519
+ function _processDefListItems_callback_dd($matches) {
2520
+ $leading_line = $matches[1];
2521
+ $marker_space = $matches[2];
2522
+ $def = $matches[3];
2523
+
2524
+ if ($leading_line || preg_match('/\n{2,}/', $def)) {
2525
+ # Replace marker with the appropriate whitespace indentation
2526
+ $def = str_repeat(' ', strlen($marker_space)) . $def;
2527
+ $def = $this->runBlockGamut($this->outdent($def . "\n\n"));
2528
+ $def = "\n". $def ."\n";
2529
+ }
2530
+ else {
2531
+ $def = rtrim($def);
2532
+ $def = $this->runSpanGamut($this->outdent($def));
2533
+ }
2534
+
2535
+ return "\n<dd>" . $def . "</dd>\n";
2536
+ }
2537
+
2538
+
2539
+ function doFencedCodeBlocks($text) {
2540
+ #
2541
+ # Adding the fenced code block syntax to regular Markdown:
2542
+ #
2543
+ # ~~~
2544
+ # Code block
2545
+ # ~~~
2546
+ #
2547
+ $less_than_tab = $this->tab_width;
2548
+
2549
+ $text = preg_replace_callback('{
2550
+ (?:\n|\A)
2551
+ # 1: Opening marker
2552
+ (
2553
+ ~{3,} # Marker: three tilde or more.
2554
+ )
2555
+ [ ]* \n # Whitespace and newline following marker.
2556
+
2557
+ # 2: Content
2558
+ (
2559
+ (?>
2560
+ (?!\1 [ ]* \n) # Not a closing marker.
2561
+ .*\n+
2562
+ )+
2563
+ )
2564
+
2565
+ # Closing marker.
2566
+ \1 [ ]* \n
2567
+ }xm',
2568
+ array(&$this, '_doFencedCodeBlocks_callback'), $text);
2569
+
2570
+ return $text;
2571
+ }
2572
+ function _doFencedCodeBlocks_callback($matches) {
2573
+ $codeblock = $matches[2];
2574
+ $codeblock = htmlspecialchars($codeblock, ENT_NOQUOTES);
2575
+ $codeblock = preg_replace_callback('/^\n+/',
2576
+ array(&$this, '_doFencedCodeBlocks_newlines'), $codeblock);
2577
+ $codeblock = "<pre><code>$codeblock</code></pre>";
2578
+ return "\n\n".$this->hashBlock($codeblock)."\n\n";
2579
+ }
2580
+ function _doFencedCodeBlocks_newlines($matches) {
2581
+ return str_repeat("<br$this->empty_element_suffix",
2582
+ strlen($matches[0]));
2583
+ }
2584
+
2585
+
2586
+ #
2587
+ # Redefining emphasis markers so that emphasis by underscore does not
2588
+ # work in the middle of a word.
2589
+ #
2590
+ var $em_relist = array(
2591
+ '' => '(?:(?<!\*)\*(?!\*)|(?<![a-zA-Z0-9_])_(?!_))(?=\S|$)(?![.,:;]\s)',
2592
+ '*' => '(?<=\S|^)(?<!\*)\*(?!\*)',
2593
+ '_' => '(?<=\S|^)(?<!_)_(?![a-zA-Z0-9_])',
2594
+ );
2595
+ var $strong_relist = array(
2596
+ '' => '(?:(?<!\*)\*\*(?!\*)|(?<![a-zA-Z0-9_])__(?!_))(?=\S|$)(?![.,:;]\s)',
2597
+ '**' => '(?<=\S|^)(?<!\*)\*\*(?!\*)',
2598
+ '__' => '(?<=\S|^)(?<!_)__(?![a-zA-Z0-9_])',
2599
+ );
2600
+ var $em_strong_relist = array(
2601
+ '' => '(?:(?<!\*)\*\*\*(?!\*)|(?<![a-zA-Z0-9_])___(?!_))(?=\S|$)(?![.,:;]\s)',
2602
+ '***' => '(?<=\S|^)(?<!\*)\*\*\*(?!\*)',
2603
+ '___' => '(?<=\S|^)(?<!_)___(?![a-zA-Z0-9_])',
2604
+ );
2605
+
2606
+
2607
+ function formParagraphs($text) {
2608
+ #
2609
+ # Params:
2610
+ # $text - string to process with html <p> tags
2611
+ #
2612
+ # Strip leading and trailing lines:
2613
+ $text = preg_replace('/\A\n+|\n+\z/', '', $text);
2614
+
2615
+ $grafs = preg_split('/\n{2,}/', $text, -1, PREG_SPLIT_NO_EMPTY);
2616
+
2617
+ #
2618
+ # Wrap <p> tags and unhashify HTML blocks
2619
+ #
2620
+ foreach ($grafs as $key => $value) {
2621
+ $value = trim($this->runSpanGamut($value));
2622
+
2623
+ # Check if this should be enclosed in a paragraph.
2624
+ # Clean tag hashes & block tag hashes are left alone.
2625
+ $is_p = !preg_match('/^B\x1A[0-9]+B|^C\x1A[0-9]+C$/', $value);
2626
+
2627
+ if ($is_p) {
2628
+ $value = "<p>$value</p>";
2629
+ }
2630
+ $grafs[$key] = $value;
2631
+ }
2632
+
2633
+ # Join grafs in one text, then unhash HTML tags.
2634
+ $text = implode("\n\n", $grafs);
2635
+
2636
+ # Finish by removing any tag hashes still present in $text.
2637
+ $text = $this->unhash($text);
2638
+
2639
+ return $text;
2640
+ }
2641
+
2642
+
2643
+ ### Footnotes
2644
+
2645
+ function stripFootnotes($text) {
2646
+ #
2647
+ # Strips link definitions from text, stores the URLs and titles in
2648
+ # hash references.
2649
+ #
2650
+ $less_than_tab = $this->tab_width - 1;
2651
+
2652
+ # Link defs are in the form: [^id]: url "optional title"
2653
+ $text = preg_replace_callback('{
2654
+ ^[ ]{0,'.$less_than_tab.'}\[\^(.+?)\][ ]?: # note_id = $1
2655
+ [ ]*
2656
+ \n? # maybe *one* newline
2657
+ ( # text = $2 (no blank lines allowed)
2658
+ (?:
2659
+ .+ # actual text
2660
+ |
2661
+ \n # newlines but
2662
+ (?!\[\^.+?\]:\s)# negative lookahead for footnote marker.
2663
+ (?!\n+[ ]{0,3}\S)# ensure line is not blank and followed
2664
+ # by non-indented content
2665
+ )*
2666
+ )
2667
+ }xm',
2668
+ array(&$this, '_stripFootnotes_callback'),
2669
+ $text);
2670
+ return $text;
2671
+ }
2672
+ function _stripFootnotes_callback($matches) {
2673
+ $note_id = $this->fn_id_prefix . $matches[1];
2674
+ $this->footnotes[$note_id] = $this->outdent($matches[2]);
2675
+ return ''; # String that will replace the block
2676
+ }
2677
+
2678
+
2679
+ function doFootnotes($text) {
2680
+ #
2681
+ # Replace footnote references in $text [^id] with a special text-token
2682
+ # which will be replaced by the actual footnote marker in appendFootnotes.
2683
+ #
2684
+ if (!$this->in_anchor) {
2685
+ $text = preg_replace('{\[\^(.+?)\]}', "F\x1Afn:\\1\x1A:", $text);
2686
+ }
2687
+ return $text;
2688
+ }
2689
+
2690
+
2691
+ function appendFootnotes($text) {
2692
+ #
2693
+ # Append footnote list to text.
2694
+ #
2695
+ $text = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
2696
+ array(&$this, '_appendFootnotes_callback'), $text);
2697
+
2698
+ if (!empty($this->footnotes_ordered)) {
2699
+ $text .= "\n\n";
2700
+ $text .= "<div class=\"footnotes\">\n";
2701
+ $text .= "<hr". $this->empty_element_suffix ."\n";
2702
+ $text .= "<ol>\n\n";
2703
+
2704
+ $attr = " rev=\"footnote\"";
2705
+ if ($this->fn_backlink_class != "") {
2706
+ $class = $this->fn_backlink_class;
2707
+ $class = $this->encodeAttribute($class);
2708
+ $attr .= " class=\"$class\"";
2709
+ }
2710
+ if ($this->fn_backlink_title != "") {
2711
+ $title = $this->fn_backlink_title;
2712
+ $title = $this->encodeAttribute($title);
2713
+ $attr .= " title=\"$title\"";
2714
+ }
2715
+ $num = 0;
2716
+
2717
+ while (!empty($this->footnotes_ordered)) {
2718
+ $footnote = reset($this->footnotes_ordered);
2719
+ $note_id = key($this->footnotes_ordered);
2720
+ unset($this->footnotes_ordered[$note_id]);
2721
+
2722
+ $footnote .= "\n"; # Need to append newline before parsing.
2723
+ $footnote = $this->runBlockGamut("$footnote\n");
2724
+ $footnote = preg_replace_callback('{F\x1Afn:(.*?)\x1A:}',
2725
+ array(&$this, '_appendFootnotes_callback'), $footnote);
2726
+
2727
+ $attr = str_replace("%%", ++$num, $attr);
2728
+ $note_id = $this->encodeAttribute($note_id);
2729
+
2730
+ # Add backlink to last paragraph; create new paragraph if needed.
2731
+ $backlink = "<a href=\"#fnref:$note_id\"$attr>&#8617;</a>";
2732
+ if (preg_match('{</p>$}', $footnote)) {
2733
+ $footnote = substr($footnote, 0, -4) . "&#160;$backlink</p>";
2734
+ } else {
2735
+ $footnote .= "\n\n<p>$backlink</p>";
2736
+ }
2737
+
2738
+ $text .= "<li id=\"fn:$note_id\">\n";
2739
+ $text .= $footnote . "\n";
2740
+ $text .= "</li>\n\n";
2741
+ }
2742
+
2743
+ $text .= "</ol>\n";
2744
+ $text .= "</div>";
2745
+ }
2746
+ return $text;
2747
+ }
2748
+ function _appendFootnotes_callback($matches) {
2749
+ $node_id = $this->fn_id_prefix . $matches[1];
2750
+
2751
+ # Create footnote marker only if it has a corresponding footnote *and*
2752
+ # the footnote hasn't been used by another marker.
2753
+ if (isset($this->footnotes[$node_id])) {
2754
+ # Transfert footnote content to the ordered list.
2755
+ $this->footnotes_ordered[$node_id] = $this->footnotes[$node_id];
2756
+ unset($this->footnotes[$node_id]);
2757
+
2758
+ $num = $this->footnote_counter++;
2759
+ $attr = " rel=\"footnote\"";
2760
+ if ($this->fn_link_class != "") {
2761
+ $class = $this->fn_link_class;
2762
+ $class = $this->encodeAttribute($class);
2763
+ $attr .= " class=\"$class\"";
2764
+ }
2765
+ if ($this->fn_link_title != "") {
2766
+ $title = $this->fn_link_title;
2767
+ $title = $this->encodeAttribute($title);
2768
+ $attr .= " title=\"$title\"";
2769
+ }
2770
+
2771
+ $attr = str_replace("%%", $num, $attr);
2772
+ $node_id = $this->encodeAttribute($node_id);
2773
+
2774
+ return
2775
+ "<sup id=\"fnref:$node_id\">".
2776
+ "<a href=\"#fn:$node_id\"$attr>$num</a>".
2777
+ "</sup>";
2778
+ }
2779
+
2780
+ return "[^".$matches[1]."]";
2781
+ }
2782
+
2783
+
2784
+ ### Abbreviations ###
2785
+
2786
+ function stripAbbreviations($text) {
2787
+ #
2788
+ # Strips abbreviations from text, stores titles in hash references.
2789
+ #
2790
+ $less_than_tab = $this->tab_width - 1;
2791
+
2792
+ # Link defs are in the form: [id]*: url "optional title"
2793
+ $text = preg_replace_callback('{
2794
+ ^[ ]{0,'.$less_than_tab.'}\*\[(.+?)\][ ]?: # abbr_id = $1
2795
+ (.*) # text = $2 (no blank lines allowed)
2796
+ }xm',
2797
+ array(&$this, '_stripAbbreviations_callback'),
2798
+ $text);
2799
+ return $text;
2800
+ }
2801
+ function _stripAbbreviations_callback($matches) {
2802
+ $abbr_word = $matches[1];
2803
+ $abbr_desc = $matches[2];
2804
+ if ($this->abbr_word_re)
2805
+ $this->abbr_word_re .= '|';
2806
+ $this->abbr_word_re .= preg_quote($abbr_word);
2807
+ $this->abbr_desciptions[$abbr_word] = trim($abbr_desc);
2808
+ return ''; # String that will replace the block
2809
+ }
2810
+
2811
+
2812
+ function doAbbreviations($text) {
2813
+ #
2814
+ # Find defined abbreviations in text and wrap them in <abbr> elements.
2815
+ #
2816
+ if ($this->abbr_word_re) {
2817
+ // cannot use the /x modifier because abbr_word_re may
2818
+ // contain significant spaces:
2819
+ $text = preg_replace_callback('{'.
2820
+ '(?<![\w\x1A])'.
2821
+ '(?:'.$this->abbr_word_re.')'.
2822
+ '(?![\w\x1A])'.
2823
+ '}',
2824
+ array(&$this, '_doAbbreviations_callback'), $text);
2825
+ }
2826
+ return $text;
2827
+ }
2828
+ function _doAbbreviations_callback($matches) {
2829
+ $abbr = $matches[0];
2830
+ if (isset($this->abbr_desciptions[$abbr])) {
2831
+ $desc = $this->abbr_desciptions[$abbr];
2832
+ if (empty($desc)) {
2833
+ return $this->hashPart("<abbr>$abbr</abbr>");
2834
+ } else {
2835
+ $desc = $this->encodeAttribute($desc);
2836
+ return $this->hashPart("<abbr title=\"$desc\">$abbr</abbr>");
2837
+ }
2838
+ } else {
2839
+ return $matches[0];
2840
+ }
2841
+ }
2842
+
2843
+ }
2844
+
2845
+
2846
+ /*
2847
+
2848
+ PHP Markdown Extra
2849
+ ==================
2850
+
2851
+ Description
2852
+ -----------
2853
+
2854
+ This is a PHP port of the original Markdown formatter written in Perl
2855
+ by John Gruber. This special "Extra" version of PHP Markdown features
2856
+ further enhancements to the syntax for making additional constructs
2857
+ such as tables and definition list.
2858
+
2859
+ Markdown is a text-to-HTML filter; it translates an easy-to-read /
2860
+ easy-to-write structured text format into HTML. Markdown's text format
2861
+ is most similar to that of plain text email, and supports features such
2862
+ as headers, *emphasis*, code blocks, blockquotes, and links.
2863
+
2864
+ Markdown's syntax is designed not as a generic markup language, but
2865
+ specifically to serve as a front-end to (X)HTML. You can use span-level
2866
+ HTML tags anywhere in a Markdown document, and you can use block level
2867
+ HTML tags (like <div> and <table> as well).
2868
+
2869
+ For more information about Markdown's syntax, see:
2870
+
2871
+ <http://daringfireball.net/projects/markdown/>
2872
+
2873
+
2874
+ Bugs
2875
+ ----
2876
+
2877
+ To file bug reports please send email to:
2878
+
2879
+ <michel.fortin@michelf.com>
2880
+
2881
+ Please include with your report: (1) the example input; (2) the output you
2882
+ expected; (3) the output Markdown actually produced.
2883
+
2884
+
2885
+ Version History
2886
+ ---------------
2887
+
2888
+ See the readme file for detailed release notes for this version.
2889
+
2890
+
2891
+ Copyright and License
2892
+ ---------------------
2893
+
2894
+ PHP Markdown & Extra
2895
+ Copyright (c) 2004-2009 Michel Fortin
2896
+ <http://michelf.com/>
2897
+ All rights reserved.
2898
+
2899
+ Based on Markdown
2900
+ Copyright (c) 2003-2006 John Gruber
2901
+ <http://daringfireball.net/>
2902
+ All rights reserved.
2903
+
2904
+ Redistribution and use in source and binary forms, with or without
2905
+ modification, are permitted provided that the following conditions are
2906
+ met:
2907
+
2908
+ * Redistributions of source code must retain the above copyright notice,
2909
+ this list of conditions and the following disclaimer.
2910
+
2911
+ * Redistributions in binary form must reproduce the above copyright
2912
+ notice, this list of conditions and the following disclaimer in the
2913
+ documentation and/or other materials provided with the distribution.
2914
+
2915
+ * Neither the name "Markdown" nor the names of its contributors may
2916
+ be used to endorse or promote products derived from this software
2917
+ without specific prior written permission.
2918
+
2919
+ This software is provided by the copyright holders and contributors "as
2920
+ is" and any express or implied warranties, including, but not limited
2921
+ to, the implied warranties of merchantability and fitness for a
2922
+ particular purpose are disclaimed. In no event shall the copyright owner
2923
+ or contributors be liable for any direct, indirect, incidental, special,
2924
+ exemplary, or consequential damages (including, but not limited to,
2925
+ procurement of substitute goods or services; loss of use, data, or
2926
+ profits; or business interruption) however caused and on any theory of
2927
+ liability, whether in contract, strict liability, or tort (including
2928
+ negligence or otherwise) arising in any way out of the use of this
2929
+ software, even if advised of the possibility of such damage.
2930
+
2931
+ */
2932
  ?>
includes/licensing.php CHANGED
@@ -1,771 +1,772 @@
1
- <?php
2
-
3
- /**
4
- * What to print in place of license code chars.
5
- * This must not be a symbol that is considered to be a valid license key char.
6
- *
7
- * @since 4.6.10
8
- */
9
- define( 'WPRSS_LICENSE_KEY_MASK_CHAR', '•' );
10
- /**
11
- * How many characters of the license code to print as is.
12
- * Use negative value to indicate that characters at the end of the key are excluded.
13
- *
14
- * @since 4.6.10
15
- */
16
- define( 'WPRSS_LICENSE_KEY_MASK_EXCLUDE_AMOUNT', -4 );
17
-
18
- /**
19
- * Returns all registered addons.
20
- *
21
- * @since 4.4.5
22
- */
23
- function wprss_get_addons() {
24
- return apply_filters( 'wprss_register_addon', array() );
25
- }
26
-
27
-
28
- /**
29
- * Calls the EDD Software Licensing API to perform licensing tasks on the addon's store server.
30
- *
31
- * @since 4.4.5
32
- */
33
- function wprss_edd_licensing_api( $addon, $license_key = NULL, $action = 'check_license', $return = 'license' ) {
34
- // If no license argument was given
35
- if ( $license_key === NULL ) {
36
- // Get the license key
37
- $license_key = wprss_get_license_key( $addon );
38
- }
39
- // Get the license status from the DB
40
- $license_status = wprss_get_license_status( $addon );
41
-
42
- // Prepare constants
43
- $item_name = strtoupper( $addon );
44
- $item_name_constant = constant( "WPRSS_{$item_name}_SL_ITEM_NAME" );
45
- $store_url_constant = constant( "WPRSS_{$item_name}_SL_STORE_URL" );
46
-
47
- // data to send in our API request
48
- $api_params = array(
49
- 'edd_action' => $action,
50
- 'license' => sanitize_text_field( $license_key ),
51
- 'item_name' => urlencode( $item_name_constant ),
52
- 'url' => urlencode( network_site_url() ),
53
- 'time' => time(),
54
- );
55
-
56
- // Send the request to the API
57
- $response = wp_remote_get( add_query_arg( $api_params, $store_url_constant ) );
58
-
59
- // If the response is an error, return the value in the DB
60
- if ( is_wp_error( $response ) ) {
61
- return $license_status;
62
- }
63
-
64
- // decode the license data
65
- $license_data = json_decode( wp_remote_retrieve_body( $response ) );
66
-
67
- // Update the DB option
68
- $license_statuses = get_option( 'wprss_settings_license_statuses' );
69
- $license_statuses["{$addon}_license_status"] = $license_data->license;
70
- $license_statuses["{$addon}_license_expires"] = $license_data->expires;
71
- update_option( 'wprss_settings_license_statuses', $license_statuses );
72
-
73
- // Return the data
74
- if ( strtoupper( $return ) === 'ALL' ) {
75
- return $license_data;
76
- } else {
77
- return $license_data->$return;
78
- }
79
- }
80
-
81
-
82
- /**
83
- * Returns the license status. Also updates the status in the DB.
84
- *
85
- * @since 4.4.5
86
- */
87
- function wprss_edd_check_license( $addon, $license_key = NULL, $return = 'license' ) {
88
- return wprss_edd_licensing_api( $addon, $license_key, 'check_license', $return );
89
- }
90
-
91
-
92
- /**
93
- * Activates an addon's license.
94
- *
95
- * @since 4.4.5
96
- */
97
- function wprss_edd_activate_license( $addon, $license_key = NULL ) {
98
- return wprss_edd_licensing_api( $addon, $license_key, 'activate_license' );
99
- }
100
-
101
-
102
- /**
103
- * Deactivates an addon's license.
104
- *
105
- * @since 4.4.5
106
- */
107
- function wprss_edd_deactivate_license( $addon, $license_key = NULL ) {
108
- return wprss_edd_licensing_api( $addon, $license_key, 'deactivate_license' );
109
- }
110
-
111
-
112
- /**
113
- * Returns an array of the default license settings. Used for plugin activation.
114
- *
115
- * @since 4.4.5
116
- *
117
- */
118
- function wprss_default_license_settings( $addon ) {
119
- // Set up the default license settings
120
- $settings = apply_filters(
121
- 'wprss_default_license_settings',
122
- array(
123
- "{$addon}_license_key" => FALSE,
124
- "{$addon}_license_status" => 'invalid',
125
- "{$addon}_license_expires" => NULL
126
- )
127
- );
128
-
129
- // Return the default settings
130
- return $settings;
131
- }
132
-
133
-
134
- /**
135
- * Returns the saved license code.
136
- *
137
- * @since 4.4.5
138
- */
139
- function wprss_get_license_key( $addon ) {
140
- // Get default and current options
141
- $defaults = wprss_default_license_settings( $addon );
142
- $keys = get_option( 'wprss_settings_license_keys', array() );
143
- // Prepare the array key and target
144
- $k = "{$addon}_license_key";
145
- // Return the appropriate value
146
- return isset( $keys["{$addon}_license_key"] )? $keys[$k] : $defaults[$k];
147
- }
148
-
149
-
150
- /**
151
- * Returns the saved license code.
152
- *
153
- * @since 4.4.5
154
- */
155
- function wprss_get_license_status( $addon ) {
156
- // Get the default and current options
157
- $defaults = wprss_default_license_settings( $addon );
158
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
159
- // Prepare the key
160
- $k = "{$addon}_license_status";
161
- // Return the appropriate value
162
- return isset( $statuses["{$addon}_license_status"] )? $statuses[$k] : $defaults[$k];
163
- }
164
-
165
-
166
- /**
167
- * Returns the saved license expiry.
168
- *
169
- * @since 4.6.7
170
- */
171
- function wprss_get_license_expiry( $addon ) {
172
- // Get default and current options
173
- $defaults = wprss_default_license_settings( $addon );
174
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
175
- // Prepare the key
176
- $k = "{$addon}_license_expires";
177
- // Return the appropriate value
178
- return isset( $statuses[$k] ) ? $statuses[$k] : $defaults[$k];
179
- }
180
-
181
-
182
- add_action( 'admin_init', 'wprss_check_to_show_license_notice');
183
- /**
184
- * Checks whether there are any invalid or expired licenses.
185
- *
186
- * @since 4.6.10
187
- * @return BOOL which is TRUE if any addons are unlicensed, FALSE otherwise.
188
- */
189
- function wprss_unlicensed_addons_exist() {
190
- // Get the license statuses including expiry dates.
191
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
192
-
193
- foreach ($statuses as $key => $value) {
194
- if ( strpos($key, '_license_status') > 0 ) {
195
- if ( $value !== 'valid') {
196
- return TRUE;
197
- }
198
- } else if ( strpos($key, '_license_expires') > 0 ) {
199
- // Check invalid expiry dates.
200
- $expires = strtotime( substr( $value, 0, strpos( $value, " " ) ) );
201
-
202
- if ( $expires == 0 || ( $expires < strtotime("+2 weeks") ) ) {
203
- return TRUE;
204
- }
205
- }
206
- }
207
-
208
- return FALSE;
209
- }
210
-
211
- /**
212
- * Check if any add-ons have a valid license and return a boolean.
213
- *
214
- * @since 4.6.8
215
- */
216
- function wprss_is_premium_user() {
217
- // Iterate through license statuses looking for a valid one.
218
- $statuses = get_option('wprss_settings_license_statuses', array());
219
- foreach ($statuses as $key => $value) {
220
- // If we're looking at a license status key...
221
- if (strpos($key, '_license_status') !== FALSE) {
222
- // ...and the license is valid...
223
- if ($value === 'valid') {
224
- return TRUE;
225
- }
226
- }
227
- }
228
-
229
- return FALSE;
230
- }
231
-
232
- /**
233
- * Returns an array of addon IDs that are licensed.
234
- *
235
- * @since 4.6.10
236
- * @return Array of addon ID strings that have a valid license status.
237
- */
238
- function wprss_get_licensed_addons() {
239
- $addons = array();
240
-
241
- // Get the license statuses.
242
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
243
-
244
- foreach ($statuses as $key => $value) {
245
- if ( strpos($key, '_license_status') > 0 ) {
246
- if ( $value === 'valid') {
247
- $addons[] = strtoupper( substr( $key, 0, strpos( $key, "_" ) ) );
248
- }
249
- }
250
- }
251
-
252
- return $addons;
253
-
254
- }
255
-
256
-
257
- /**
258
- * Checks whether we should show the invalid/expired license notices.
259
- *
260
- * @since 4.6.10
261
- */
262
- function wprss_check_to_show_license_notice() {
263
- // Check if we found any of the licenses to be invalid, expiring or expired
264
- // so that we can show the appropriate license nag.
265
- if (wprss_unlicensed_addons_exist()) {
266
- add_action( 'all_admin_notices', 'wprss_show_license_notice' );
267
- }
268
- }
269
-
270
-
271
- /**
272
- * Shows an admin notice for any invalid/expired licenses.
273
- *
274
- * @since 4.6.9
275
- */
276
- function wprss_show_license_notice() {
277
- // Get the license statuses including expiry dates.
278
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
279
-
280
- // Array of notices to show.
281
- $notices = array();
282
-
283
- foreach ($statuses as $key => $value) {
284
- if ( strpos($key, '_license_status') > 0 ) {
285
- if ( $value === 'expired' ) {
286
- // License is expired, but we'll show the notice for this when checking the *_license_expires key.
287
-
288
- continue;
289
-
290
- } else if ( $value !== 'valid' ) {
291
- // The license is invalid or unactivated.
292
-
293
- $uid = strtoupper( substr( $key, 0, strpos( $key, "_" ) ) );
294
-
295
- // Check if the plugin is currently activated.
296
- if ( !defined("WPRSS_{$uid}_SL_ITEM_NAME") ) {
297
- continue;
298
- } else {
299
- $plugin = constant("WPRSS_{$uid}_SL_ITEM_NAME");
300
- }
301
-
302
- $msg = sprintf(
303
- __( 'Remember to <a href="%s">enter your plugin license code</a> for the WP RSS Aggregator <b>%s</b> add-on to benefit from updates and support.', WPRSS_TEXT_DOMAIN ),
304
- esc_attr(admin_url( 'edit.php?post_type=wprss_feed&page=wprss-aggregator-settings&tab=licenses_settings' )),
305
- $plugin
306
- );
307
-
308
- // Save the notice we're going to display
309
- $notices[$uid] = '<div id="wprss-license-notice-' . $uid . '" class="error wprss-license-notice"><p>' . $msg . '</p></div>';
310
- }
311
- } else if ( strpos($key, '_license_expires') > 0 ) {
312
- // Check for expired licenses
313
-
314
- $expires = strtotime( substr( $value, 0, strpos( $value, " " ) ) );
315
- $id = substr( $key, 0, strpos( $key, "_" ) );
316
- $uid = strtoupper($id);
317
-
318
- // Check if the plugin is currently activated.
319
- if ( !defined("WPRSS_{$uid}_SL_ITEM_NAME") ) {
320
- continue;
321
- } else {
322
- $plugin = constant("WPRSS_{$uid}_SL_ITEM_NAME");
323
- }
324
-
325
- if ( $expires < strtotime("+2 weeks") ) {
326
- // The license is expired or expiring soon.
327
- $license_key = wprss_get_license_key($id);
328
- $msg = sprintf(
329
- __('<a href="%s">Save 30%% on your license renewal</a> for the WP RSS Aggregator <b>%s</b> add-on and continue receiving updates and support.', WPRSS_TEXT_DOMAIN),
330
- esc_attr(WPRSS_SL_STORE_URL . '/checkout/?edd_license_key=' . $license_key),
331
- $plugin
332
- );
333
-
334
- // User can hide expiring/expired license messages.
335
- $hide = '<a href="#" class="ajax-close-addon-notice" style="float:right;" data-addon="categories" data-notice="license">' .
336
- __('Dismiss this notification', WPRSS_TEXT_DOMAIN) . '</a>';
337
-
338
- // Only show this notice if there isn't already a notice to show for this add-on.
339
- if ( !isset($notices[$uid]) ) {
340
- $notices[$uid] = '<div class="error wprss-license-notice"><p>' . $msg . $hide . '</p></div>';
341
- }
342
- }
343
- }
344
- }
345
-
346
- // Display the notices
347
- foreach ($notices as $notice) {
348
- echo $notice;
349
- }
350
- }
351
-
352
-
353
- add_action( 'wp_ajax_wprss_ajax_manage_license', 'wprss_ajax_manage_license' );
354
- /**
355
- * Handles the AJAX request to check a license.
356
- *
357
- * @since 4.7
358
- */
359
- function wprss_ajax_manage_license() {
360
- // Get and sanitize the addon ID we're checking.
361
- if ( isset($_GET['addon']) ) {
362
- $addon = sanitize_text_field($_GET['addon']);
363
- } else {
364
- wprss_echo_error_and_die( __('No addon ID', WPRSS_TEXT_DOMAIN ));
365
- }
366
-
367
- // Check what we've been asked to do with the license.
368
- if ( isset($_GET['event']) ) {
369
- $event = sanitize_text_field($_GET['event']);
370
-
371
- if ($event !== 'activate' && $event !== 'deactivate') {
372
- wprss_echo_error_and_die( __('Invalid event specified', WPRSS_TEXT_DOMAIN), $addon);
373
- }
374
- } else {
375
- wprss_echo_error_and_die( __('No event specified', WPRSS_TEXT_DOMAIN), $addon);
376
- }
377
-
378
- // Get and sanitize the license that was entered.
379
- if ( isset($_GET['license']) ) {
380
- $license = sanitize_text_field($_GET['license']);
381
- } else {
382
- wprss_echo_error_and_die( __('No license', WPRSS_TEXT_DOMAIN), $addon);
383
- }
384
-
385
- // Check the nonce for this particular add-on's validation button.
386
- if ( isset($_GET['nonce']) ) {
387
- $nonce = sanitize_text_field($_GET['nonce']);
388
- $nonce_id = "wprss_{$addon}_license_nonce";
389
-
390
- if ( !wp_verify_nonce($nonce, $nonce_id) ) {
391
- wprss_echo_error_and_die( __('Bad nonce', WPRSS_TEXT_DOMAIN), $addon);
392
- }
393
- } else {
394
- wprss_echo_error_and_die( __('No nonce', WPRSS_TEXT_DOMAIN), $addon);
395
- }
396
-
397
- $license_keys = get_option('wprss_settings_license_keys', array());
398
- // Check if the license key was obfuscated on the client's end.
399
- if ( wprss_license_key_is_obfuscated( $license ) ) {
400
- // If so, use the stored license key for de/activation.
401
- $license = $license_keys[$addon . '_license_key'];
402
- } else {
403
- // Otherwise, update the license key stored in the DB.
404
- $license_keys[$addon . '_license_key'] = $license;
405
- update_option('wprss_settings_license_keys', $license_keys);
406
- }
407
-
408
- // Call the appropriate EDD licensing function.
409
- if ($event === 'activate') {
410
- $status = wprss_edd_activate_license($addon, $license);
411
- } else if ($event === 'deactivate') {
412
- $status = wprss_edd_deactivate_license($addon, $license);
413
- } else {
414
- wprss_echo_error_and_die( __('Invalid event specified', WPRSS_TEXT_DOMAIN), $addon);
415
- }
416
-
417
- // Assemble the JSON data to return.
418
- $ret = array();
419
-
420
- // Set the validity of the license.
421
- if ( $status === 'site_inactive' ) $status = 'inactive';
422
- if ( $status === 'item_name_mismatch' ) $status = 'invalid';
423
- $ret['validity'] = $status;
424
-
425
- // Set the addon ID for use in the callback.
426
- $ret['addon'] = $addon;
427
-
428
- // Set the HTML markup for the new button and validity display.
429
- $ret['html'] = wprss_get_activate_license_button($addon);
430
-
431
- $ret['licensedAddons'] = wprss_get_licensed_addons();
432
-
433
- // Return the JSON data.
434
- echo json_encode($ret);
435
- die();
436
- }
437
-
438
-
439
- add_action( 'wp_ajax_wprss_ajax_fetch_license', 'wprss_ajax_fetch_license' );
440
- /**
441
- * Handles the AJAX request to fetch a license's information.
442
- *
443
- * @since 4.7
444
- */
445
- function wprss_ajax_fetch_license() {
446
- // Get and sanitize the addon ID we're checking.
447
- if ( isset($_GET['addon']) ) {
448
- $addon = sanitize_text_field($_GET['addon']);
449
- } else {
450
- wprss_echo_error_and_die( __('No addon ID', WPRSS_TEXT_DOMAIN ));
451
- }
452
-
453
- // Get the license information from EDD
454
- $ret = wprss_edd_check_license( $addon, NULL, 'ALL' );
455
-
456
- echo json_encode($ret);
457
- die();
458
- }
459
-
460
-
461
- /**
462
- * Helper function that echoes a JSON error along with the new
463
- * activate/deactivate license button HTML markup and then die()s.
464
- *
465
- * @since 4.7
466
- */
467
- function wprss_echo_error_and_die($msg, $addon = '') {
468
- $ret = array(
469
- 'error' => $msg,
470
- 'html' => wprss_get_activate_license_button($addon)
471
- );
472
-
473
- echo json_encode($ret);
474
- die();
475
- }
476
-
477
-
478
- add_action( 'wprss_admin_init', 'wprss_license_settings', 100 );
479
- /**
480
- * Adds the license sections and settings for registered add-ons.
481
- *
482
- * @since 4.4.5
483
- */
484
- function wprss_license_settings() {
485
- $addons = wprss_get_addons();
486
- foreach( $addons as $addon_id => $addon_name ) {
487
- // Settings Section
488
- add_settings_section(
489
- "wprss_settings_{$addon_id}_licenses_section",
490
- $addon_name .' '. __( 'License', WPRSS_TEXT_DOMAIN ),
491
- '__return_empty_string',
492
- 'wprss_settings_license_keys'
493
- );
494
- // License key field
495
- add_settings_field(
496
- "wprss_settings_{$addon_id}_license",
497
- __( 'License Key', WPRSS_TEXT_DOMAIN ),
498
- 'wprss_license_key_field',
499
- 'wprss_settings_license_keys',
500
- "wprss_settings_{$addon_id}_licenses_section",
501
- array( $addon_id )
502
- );
503
- // Activate license button
504
- add_settings_field(
505
- "wprss_settings_{$addon_id}_activate_license",
506
- __( 'Activate License', WPRSS_TEXT_DOMAIN ),
507
- 'wprss_activate_license_button',
508
- 'wprss_settings_license_keys',
509
- "wprss_settings_{$addon_id}_licenses_section",
510
- array( $addon_id )
511
- );
512
- }
513
- }
514
-
515
-
516
- /**
517
- * Renders the license field for a particular add-on.
518
- *
519
- * @since 4.4.5
520
- */
521
- function wprss_license_key_field( $args ) {
522
- $addon_id = $args[0];
523
- $license_key = wprss_get_license_key( $addon_id );
524
- $license_key_length = strlen( $license_key );
525
- $mask_char = WPRSS_LICENSE_KEY_MASK_CHAR;
526
- // How many chars to show
527
- $mask_exclude_amount = WPRSS_LICENSE_KEY_MASK_EXCLUDE_AMOUNT;
528
- $mask_exclude_amount = abs( $mask_exclude_amount ) > ($license_key_length - 1)
529
- ? ($license_key_length - 1) * ( $mask_exclude_amount < 0 ? -1 : 1 ) // Making sure to preserve position of mask
530
- : $mask_exclude_amount;
531
- // How many chars to mask. Always at least one char will be masked.
532
- $mask_length = $license_key_length - abs( $mask_exclude_amount );
533
- $mask = $mask_length > 0 ? str_repeat( $mask_char, $mask_length ) : '';
534
- $excluded_chars = mb_substr( $license_key, $mask_exclude_amount < 0 ? $mask_length : 0, abs( $mask_exclude_amount ) );
535
- $displayed_key = sprintf( $mask_exclude_amount > 0 ? '%1$s%2$s' : '%2$s%1$s', $excluded_chars, $mask); ?>
536
- <input id="wprss-<?php echo $addon_id ?>-license-key" name="wprss_settings_license_keys[<?php echo $addon_id ?>_license_key]"
537
- type="text" value="<?php echo esc_attr( $displayed_key ) ?>" style="width: 300px;"
538
- />
539
- <label class="description" for="wprss-<?php echo $addon_id ?>-license-key">
540
- <?php _e( 'Enter your license key', WPRSS_TEXT_DOMAIN ) ?>
541
- </label><?php
542
- }
543
-
544
-
545
- /**
546
- * Renders the activate/deactivate license button for a particular add-on.
547
- *
548
- * @since 4.4.5
549
- */
550
- function wprss_activate_license_button( $args ) {
551
- $addon_id = $args[0];
552
- $data = wprss_edd_check_license( $addon_id, NULL, 'ALL' );
553
- $status = is_string( $data ) ? $data : $data->license;
554
- if ( $status === 'site_inactive' ) $status = 'inactive';
555
- if ( $status === 'item_name_mismatch' ) $status = 'invalid';
556
-
557
- $valid = $status == 'valid';
558
- $btn_text = $valid ? 'Deactivate License' : 'Activate License';
559
- $btn_name = "wprss_{$addon_id}_license_" . ( $valid? 'deactivate' : 'activate' );
560
- $btn_class = "button-" . ( $valid ? 'deactivate' : 'activate' ) . "-license";
561
- wp_nonce_field( "wprss_{$addon_id}_license_nonce", "wprss_{$addon_id}_license_nonce", false ); ?>
562
-
563
- <input type="button" class="<?php echo $btn_class; ?> button-process-license button-secondary" name="<?php echo $btn_name; ?>" value="<?php _e( $btn_text, WPRSS_TEXT_DOMAIN ); ?>" />
564
- <span id="wprss-<?php echo $addon_id; ?>-license-status-text">
565
- <strong><?php _e('Status', WPRSS_TEXT_DOMAIN); ?>:
566
- <span class="wprss-<?php echo $addon_id; ?>-license-<?php echo $status; ?>">
567
- <?php _e( ucfirst($status), WPRSS_TEXT_DOMAIN ); ?>
568
- <?php if ( $status === 'valid' ) : ?>
569
- <i class="fa fa-check"></i>
570
- <?php elseif( $status === 'invalid' || $status === 'expired' ): ?>
571
- <i class="fa fa-times"></i>
572
- <?php elseif( $status === 'inactive' ): ?>
573
- <i class="fa fa-warning"></i>
574
- <?php endif; ?>
575
- </strong>
576
- </span>
577
- </span>
578
-
579
- <p>
580
- <?php
581
- $license_key = wprss_get_license_key( $addon_id );
582
- if ( ! empty( $license_key ) ) :
583
- if ( is_object( $data ) ) :
584
- $acts_current = $data->site_count;
585
- $acts_left = $data->activations_left;
586
- $acts_limit = $data->license_limit;
587
- $expires = $data->expires;
588
- $expires = substr( $expires, 0, strpos( $expires, " " ) );
589
-
590
- // If the license key is garbage, don't show any of the data.
591
- if ( !empty($data->payment_id) && !empty($data->license_limit ) ) :
592
- ?>
593
- <small>
594
- <?php if ( $status !== 'valid' && $acts_left === 0 ) : ?>
595
- <?php $account_url = 'https://www.wprssaggregator.com/account/?action=manage_licenses&payment_id=' . $data->payment_id; ?>
596
- <a href="<?php echo $account_url; ?>"><?php _e("No activations left. Click here to manage the sites you've activated licenses on.", WPRSS_TEXT_DOMAIN); ?></a>
597
- <br/>
598
- <?php endif; ?>
599
- <?php if ( strtotime($expires) < strtotime("+2 weeks") ) : ?>
600
- <?php $renewal_url = esc_attr(WPRSS_SL_STORE_URL . '/checkout/?edd_license_key=' . $license_key); ?>
601
- <a href="<?php echo $renewal_url; ?>"><?php _e('Renew your license to continue receiving updates and support.', WPRSS_TEXT_DOMAIN); ?></a>
602
- <br/>
603
- <?php endif; ?>
604
- <strong><?php _e('Activations', WPRSS_TEXT_DOMAIN); ?>:</strong>
605
- <?php echo $acts_current.'/'.$acts_limit; ?> (<?php echo $acts_left; ?> left)
606
- <br/>
607
- <strong><?php _e('Expires on', WPRSS_TEXT_DOMAIN); ?>:</strong>
608
- <code><?php echo $expires; ?></code>
609
- <br/>
610
- <strong><?php _e('Registered to', WPRSS_TEXT_DOMAIN); ?>:</strong>
611
- <?php echo $data->customer_name; ?> (<code><?php echo $data->customer_email; ?></code>)
612
- </small>
613
- <?php endif; ?>
614
- <?php else: ?>
615
- <small><?php _e('Failed to get license information. This is a temporary problem. Check your internet connection and try again later.', WPRSS_TEXT_DOMAIN); ?></small>
616
- <?php endif; ?>
617
- <?php endif;
618
- ?>
619
- </p>
620
-
621
- <style type="text/css">
622
- .wprss-<?php echo $addon_id; ?>-license-valid {
623
- color: green;
624
- }
625
- .wprss-<?php echo $addon_id; ?>-license-invalid, .wprss-<?php echo $addon_id; ?>-license-expired {
626
- color: #b71919;
627
- }
628
- .wprss-<?php echo $addon_id; ?>-license-inactive {
629
- color: #d19e5b;
630
- }
631
- #wprss-<?php echo $addon_id; ?>-license-status-text {
632
- margin-left: 8px;
633
- line-height: 27px;
634
- vertical-align: middle;
635
- }
636
- </style>
637
-
638
-
639
- <?php
640
- }
641
-
642
-
643
- /**
644
- * Returns the activate/deactivate license button markup for a particular add-on.
645
- *
646
- * @since 4.7
647
- */
648
- function wprss_get_activate_license_button( $addon ) {
649
- // Buffer the output from the rendering function.
650
- ob_start();
651
-
652
- wprss_activate_license_button(array($addon));
653
- $ret = ob_get_contents();
654
-
655
- ob_end_clean();
656
-
657
- return $ret;
658
- }
659
-
660
-
661
- add_action( 'admin_init', 'wprss_process_addon_license', 10 );
662
- /**
663
- * Handles the activation/deactivation process
664
- *
665
- * @since 1.0
666
- */
667
- function wprss_process_addon_license() {
668
- $addons = wprss_get_addons();
669
-
670
- // Get for each registered addon
671
- foreach( $addons as $id => $name ) {
672
-
673
- // listen for our activate button to be clicked
674
- if( isset( $_POST["wprss_{$id}_license_activate"] ) || isset( $_POST["wprss_{$id}_license_deactivate"] ) ) {
675
- // run a quick security check
676
- if( ! check_admin_referer( "wprss_{$id}_license_nonce", "wprss_{$id}_license_nonce" ) )
677
- continue; // get out if we didn't click the Activate/Deactivate button
678
- }
679
-
680
- // retrieve the license keys and statuses from the database
681
- $license = wprss_get_license_key( $id );
682
- $license_statuses = get_option( 'wprss_settings_license_statuses' );
683
-
684
- // If the license is not saved in DB, but is included in POST
685
- if ( $license == '' && !empty($_POST['wprss_settings_license_keys'][$id.'_license_key']) ) {
686
- // Use the license given in POST
687
- $license = $_POST['wprss_settings_license_keys'][$id.'_license_key'];
688
- }
689
-
690
- // Prepare the action to take
691
- if ( isset( $_POST["wprss_{$id}_license_activate"] ) ) {
692
- wprss_edd_activate_license( $id, $license );
693
- }
694
- elseif ( isset( $_POST["wprss_{$id}_license_deactivate"] ) ) {
695
- wprss_edd_deactivate_license( $id, $license );
696
- }
697
- }
698
- }
699
-
700
-
701
- add_action( 'init', 'wprss_setup_edd_updater' );
702
- /**
703
- * Sets up the EDD updater for all registered add-ons.
704
- *
705
- * @since 4.6.3
706
- */
707
- function wprss_setup_edd_updater() {
708
- // Get all registered addons
709
- $addons = wprss_get_addons();
710
-
711
- // retrieve our license key from the DB
712
- $licenses = get_option( 'wprss_settings_license_keys' );
713
-
714
- // setup the updater
715
- if ( !class_exists( 'EDD_SL_Plugin_Updater' ) ) {
716
- // load our custom updater
717
- include ( WPRSS_INC . 'libraries/EDD_licensing/EDD_SL_Plugin_Updater.php' );
718
- }
719
-
720
- // Iterate the addons
721
- foreach( $addons as $id => $name ) {
722
- // Prepare the data
723
- $license = wprss_get_license_key( $id );
724
- $uid = strtoupper( $id );
725
- $name = constant("WPRSS_{$uid}_SL_ITEM_NAME");
726
- $version = constant("WPRSS_{$uid}_VERSION");
727
- $path = constant("WPRSS_{$uid}_PATH");
728
- // Set up an updater
729
- $edd_updater = new EDD_SL_Plugin_Updater( WPRSS_SL_STORE_URL, $path, array(
730
- 'version' => $version, // current version number
731
- 'license' => $license, // license key (used get_option above to retrieve from DB)
732
- 'item_name' => $name, // name of this plugin
733
- 'author' => 'Jean Galea' // author of this plugin
734
- ));
735
- }
736
- }
737
-
738
-
739
- add_filter( 'wprss_settings_license_key_is_valid', 'wprss_license_validate_key_for_save', 10, 2 );
740
- /**
741
- * Invalidates the key if it is obfuscated, causing the saved version to be used.
742
- * This meanst that the new key will not be saved, as it is considered then to be unchanged.
743
- *
744
- * @since 4.6.10
745
- * @param bool $is_valid Indicates whether the key is currently considered to be valid.
746
- * @param string $key The license key in question
747
- * @return Whether or not the key is still to be considered valid.
748
- */
749
- function wprss_license_validate_key_for_save( $is_valid, $key ) {
750
- if ( wprss_license_key_is_obfuscated( $key ) )
751
- return false;
752
-
753
- return $is_valid;
754
- }
755
-
756
-
757
- /**
758
- * Determines whether or not the license key in question is obfuscated.
759
- *
760
- * This is achieved by searching for the mask character in the key. Because the
761
- * mask character cannot be a valid license character, the presence of at least
762
- * one such character indicates that the key is obfuscated.
763
- *
764
- * @since 4.6.10
765
- * @param string $key The license key in question.
766
- * @return bool Whether or not this key is obfuscated.
767
- */
768
- function wprss_license_key_is_obfuscated( $key ) {
769
- $char = WPRSS_LICENSE_KEY_MASK_CHAR;
770
- return mb_stripos( $key, $char ) !== false;
771
- }
1
+ <?php
2
+
3
+ /**
4
+ * What to print in place of license code chars.
5
+ * This must not be a symbol that is considered to be a valid license key char.
6
+ *
7
+ * @since 4.6.10
8
+ */
9
+ define( 'WPRSS_LICENSE_KEY_MASK_CHAR', '•' );
10
+ /**
11
+ * How many characters of the license code to print as is.
12
+ * Use negative value to indicate that characters at the end of the key are excluded.
13
+ *
14
+ * @since 4.6.10
15
+ */
16
+ define( 'WPRSS_LICENSE_KEY_MASK_EXCLUDE_AMOUNT', -4 );
17
+
18
+ /**
19
+ * Returns all registered addons.
20
+ *
21
+ * @since 4.4.5
22
+ */
23
+ function wprss_get_addons() {
24
+ return apply_filters( 'wprss_register_addon', array() );
25
+ }
26
+
27
+
28
+ /**
29
+ * Calls the EDD Software Licensing API to perform licensing tasks on the addon's store server.
30
+ *
31
+ * @since 4.4.5
32
+ */
33
+ function wprss_edd_licensing_api( $addon, $license_key = NULL, $action = 'check_license', $return = 'license' ) {
34
+ // If no license argument was given
35
+ if ( $license_key === NULL ) {
36
+ // Get the license key
37
+ $license_key = wprss_get_license_key( $addon );
38
+ }
39
+ // Get the license status from the DB
40
+ $license_status = wprss_get_license_status( $addon );
41
+
42
+ // Prepare constants
43
+ $item_name = strtoupper( $addon );
44
+ $item_name_constant = constant( "WPRSS_{$item_name}_SL_ITEM_NAME" );
45
+ $store_url_constant = constant( "WPRSS_{$item_name}_SL_STORE_URL" );
46
+
47
+ // data to send in our API request
48
+ $api_params = array(
49
+ 'edd_action' => $action,
50
+ 'license' => sanitize_text_field( $license_key ),
51
+ 'item_name' => urlencode( $item_name_constant ),
52
+ 'url' => urlencode( network_site_url() ),
53
+ 'time' => time(),
54
+ );
55
+
56
+ // Send the request to the API
57
+ $response = wp_remote_get( add_query_arg( $api_params, $store_url_constant ) );
58
+
59
+ // If the response is an error, return the value in the DB
60
+ if ( is_wp_error( $response ) ) {
61
+ return $license_status;
62
+ }
63
+
64
+ // decode the license data
65
+ $license_data = json_decode( wp_remote_retrieve_body( $response ) );
66
+
67
+ // Update the DB option
68
+ $license_statuses = get_option( 'wprss_settings_license_statuses' );
69
+ $license_statuses["{$addon}_license_status"] = $license_data->license;
70
+ $license_statuses["{$addon}_license_expires"] = $license_data->expires;
71
+ update_option( 'wprss_settings_license_statuses', $license_statuses );
72
+
73
+ // Return the data
74
+ if ( strtoupper( $return ) === 'ALL' ) {
75
+ return $license_data;
76
+ } else {
77
+ return $license_data->$return;
78
+ }
79
+ }
80
+
81
+
82
+ /**
83
+ * Returns the license status. Also updates the status in the DB.
84
+ *
85
+ * @since 4.4.5
86
+ */
87
+ function wprss_edd_check_license( $addon, $license_key = NULL, $return = 'license' ) {
88
+ return wprss_edd_licensing_api( $addon, $license_key, 'check_license', $return );
89
+ }
90
+
91
+
92
+ /**
93
+ * Activates an addon's license.
94
+ *
95
+ * @since 4.4.5
96
+ */
97
+ function wprss_edd_activate_license( $addon, $license_key = NULL ) {
98
+ return wprss_edd_licensing_api( $addon, $license_key, 'activate_license' );
99
+ }
100
+
101
+
102
+ /**
103
+ * Deactivates an addon's license.
104
+ *
105
+ * @since 4.4.5
106
+ */
107
+ function wprss_edd_deactivate_license( $addon, $license_key = NULL ) {
108
+ return wprss_edd_licensing_api( $addon, $license_key, 'deactivate_license' );
109
+ }
110
+
111
+
112
+ /**
113
+ * Returns an array of the default license settings. Used for plugin activation.
114
+ *
115
+ * @since 4.4.5
116
+ *
117
+ */
118
+ function wprss_default_license_settings( $addon ) {
119
+ // Set up the default license settings
120
+ $settings = apply_filters(
121
+ 'wprss_default_license_settings',
122
+ array(
123
+ "{$addon}_license_key" => FALSE,
124
+ "{$addon}_license_status" => 'invalid',
125
+ "{$addon}_license_expires" => NULL
126
+ )
127
+ );
128
+
129
+ // Return the default settings
130
+ return $settings;
131
+ }
132
+
133
+
134
+ /**
135
+ * Returns the saved license code.
136
+ *
137
+ * @since 4.4.5
138
+ */
139
+ function wprss_get_license_key( $addon ) {
140
+ // Get default and current options
141
+ $defaults = wprss_default_license_settings( $addon );
142
+ $keys = get_option( 'wprss_settings_license_keys', array() );
143
+ // Prepare the array key and target
144
+ $k = "{$addon}_license_key";
145
+ // Return the appropriate value
146
+ return isset( $keys["{$addon}_license_key"] )? $keys[$k] : $defaults[$k];
147
+ }
148
+
149
+
150
+ /**
151
+ * Returns the saved license code.
152
+ *
153
+ * @since 4.4.5
154
+ */
155
+ function wprss_get_license_status( $addon ) {
156
+ // Get the default and current options
157
+ $defaults = wprss_default_license_settin