1 | <?php |
||||
2 | class Digest |
||||
3 | { |
||||
4 | |||||
5 | /** |
||||
6 | * Send by mail a digest of last articles. |
||||
7 | * |
||||
8 | * @param mixed $link The database connection. |
||||
9 | * @param integer $limit The maximum number of articles by digest. |
||||
10 | * @return boolean Return false if digests are not enabled. |
||||
11 | */ |
||||
12 | public static function send_headlines_digests() { |
||||
13 | |||||
14 | $user_limit = 15; // amount of users to process (e.g. emails to send out) |
||||
15 | $limit = 1000; // maximum amount of headlines to include |
||||
16 | |||||
17 | Debug::log("Sending digests, batch of max $user_limit users, headline limit = $limit"); |
||||
18 | |||||
19 | if (DB_TYPE == "pgsql") { |
||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||
20 | $interval_qpart = "last_digest_sent < NOW() - INTERVAL '1 days'"; |
||||
21 | } else if (DB_TYPE == "mysql") { |
||||
22 | $interval_qpart = "last_digest_sent < DATE_SUB(NOW(), INTERVAL 1 DAY)"; |
||||
23 | } |
||||
24 | |||||
25 | $pdo = Db::pdo(); |
||||
26 | |||||
27 | $res = $pdo->query("SELECT id,email FROM ttrss_users |
||||
28 | WHERE email != '' AND (last_digest_sent IS NULL OR $interval_qpart)"); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
29 | |||||
30 | while ($line = $res->fetch()) { |
||||
31 | |||||
32 | if (@get_pref('DIGEST_ENABLE', $line['id'], false)) { |
||||
33 | $preferred_ts = strtotime(get_pref('DIGEST_PREFERRED_TIME', $line['id'], '00:00')); |
||||
34 | |||||
35 | // try to send digests within 2 hours of preferred time |
||||
36 | if ($preferred_ts && time() >= $preferred_ts && |
||||
37 | time() - $preferred_ts <= 7200 |
||||
38 | ) { |
||||
39 | |||||
40 | Debug::log("Sending digest for UID:".$line['id']." - ".$line["email"]); |
||||
41 | |||||
42 | $do_catchup = get_pref('DIGEST_CATCHUP', $line['id'], false); |
||||
43 | |||||
44 | global $tz_offset; |
||||
45 | |||||
46 | // reset tz_offset global to prevent tz cache clash between users |
||||
47 | $tz_offset = -1; |
||||
48 | |||||
49 | $tuple = Digest::prepare_headlines_digest($line["id"], 1, $limit); |
||||
50 | $digest = $tuple[0]; |
||||
51 | $headlines_count = $tuple[1]; |
||||
52 | $affected_ids = $tuple[2]; |
||||
53 | $digest_text = $tuple[3]; |
||||
54 | |||||
55 | if ($headlines_count > 0) { |
||||
56 | |||||
57 | $mailer = new Mailer(); |
||||
58 | |||||
59 | //$rc = $mail->quickMail($line["email"], $line["login"], DIGEST_SUBJECT, $digest, $digest_text); |
||||
60 | |||||
61 | $rc = $mailer->mail(["to_name" => $line["login"], |
||||
62 | "to_address" => $line["email"], |
||||
63 | "subject" => DIGEST_SUBJECT, |
||||
0 ignored issues
–
show
|
|||||
64 | "message" => $digest_text, |
||||
65 | "message_html" => $digest]); |
||||
66 | |||||
67 | //if (!$rc && $debug) Debug::log("ERROR: " . $mailer->lastError()); |
||||
68 | |||||
69 | Debug::log("RC=$rc"); |
||||
70 | |||||
71 | if ($rc && $do_catchup) { |
||||
72 | Debug::log("Marking affected articles as read..."); |
||||
73 | Article::catchupArticlesById($affected_ids, 0, $line["id"]); |
||||
74 | } |
||||
75 | } else { |
||||
76 | Debug::log("No headlines"); |
||||
77 | } |
||||
78 | |||||
79 | $sth = $pdo->prepare("UPDATE ttrss_users SET last_digest_sent = NOW() |
||||
80 | WHERE id = ?"); |
||||
81 | $sth->execute([$line["id"]]); |
||||
82 | |||||
83 | } |
||||
84 | } |
||||
85 | } |
||||
86 | |||||
87 | Debug::log("All done."); |
||||
88 | |||||
89 | } |
||||
90 | |||||
91 | public static function prepare_headlines_digest($user_id, $days = 1, $limit = 1000) { |
||||
92 | |||||
93 | require_once "lib/MiniTemplator.class.php"; |
||||
94 | |||||
95 | $tpl = new MiniTemplator; |
||||
96 | $tpl_t = new MiniTemplator; |
||||
97 | |||||
98 | $tpl->readTemplateFromFile("templates/digest_template_html.txt"); |
||||
99 | $tpl_t->readTemplateFromFile("templates/digest_template.txt"); |
||||
100 | |||||
101 | $user_tz_string = get_pref('USER_TIMEZONE', $user_id); |
||||
102 | $local_ts = convert_timestamp(time(), 'UTC', $user_tz_string); |
||||
103 | |||||
104 | $tpl->setVariable('CUR_DATE', date('Y/m/d', $local_ts)); |
||||
105 | $tpl->setVariable('CUR_TIME', date('G:i', $local_ts)); |
||||
106 | $tpl->setVariable('TTRSS_HOST', SELF_URL_PATH); |
||||
0 ignored issues
–
show
|
|||||
107 | |||||
108 | $tpl_t->setVariable('CUR_DATE', date('Y/m/d', $local_ts)); |
||||
109 | $tpl_t->setVariable('CUR_TIME', date('G:i', $local_ts)); |
||||
110 | $tpl_t->setVariable('TTRSS_HOST', SELF_URL_PATH); |
||||
111 | |||||
112 | $affected_ids = array(); |
||||
113 | |||||
114 | $days = (int) $days; |
||||
115 | |||||
116 | if (DB_TYPE == "pgsql") { |
||||
0 ignored issues
–
show
|
|||||
117 | $interval_qpart = "ttrss_entries.date_updated > NOW() - INTERVAL '$days days'"; |
||||
118 | } else if (DB_TYPE == "mysql") { |
||||
119 | $interval_qpart = "ttrss_entries.date_updated > DATE_SUB(NOW(), INTERVAL $days DAY)"; |
||||
120 | } |
||||
121 | |||||
122 | $pdo = Db::pdo(); |
||||
123 | |||||
124 | $sth = $pdo->prepare("SELECT ttrss_entries.title, |
||||
125 | ttrss_feeds.title AS feed_title, |
||||
126 | COALESCE(ttrss_feed_categories.title, '" . __('Uncategorized')."') AS cat_title, |
||||
127 | date_updated, |
||||
128 | ttrss_user_entries.ref_id, |
||||
129 | link, |
||||
130 | score, |
||||
131 | content, |
||||
132 | " . SUBSTRING_FOR_DATE."(last_updated,1,19) AS last_updated |
||||
133 | FROM |
||||
134 | ttrss_user_entries,ttrss_entries,ttrss_feeds |
||||
135 | LEFT JOIN |
||||
136 | ttrss_feed_categories ON (cat_id = ttrss_feed_categories.id) |
||||
137 | WHERE |
||||
138 | ref_id = ttrss_entries.id AND feed_id = ttrss_feeds.id |
||||
139 | AND include_in_digest = true |
||||
140 | AND $interval_qpart |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
141 | AND ttrss_user_entries.owner_uid = :user_id |
||||
142 | AND unread = true |
||||
143 | AND score >= 0 |
||||
144 | ORDER BY ttrss_feed_categories.title, ttrss_feeds.title, score DESC, date_updated DESC |
||||
145 | LIMIT :limit"); |
||||
146 | $sth->bindParam(':user_id', intval($user_id, 10), PDO::PARAM_INT); |
||||
0 ignored issues
–
show
intval($user_id, 10) cannot be passed to PDOStatement::bindParam() as the parameter $variable expects a reference.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
147 | $sth->bindParam(':limit', intval($limit, 10), PDO::PARAM_INT); |
||||
0 ignored issues
–
show
intval($limit, 10) cannot be passed to PDOStatement::bindParam() as the parameter $variable expects a reference.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
148 | $sth->execute(); |
||||
149 | |||||
150 | $headlines_count = 0; |
||||
151 | $headlines = array(); |
||||
152 | |||||
153 | while ($line = $sth->fetch()) { |
||||
154 | array_push($headlines, $line); |
||||
155 | $headlines_count++; |
||||
156 | } |
||||
157 | |||||
158 | for ($i = 0; $i < sizeof($headlines); $i++) { |
||||
0 ignored issues
–
show
It seems like you are calling the size function
sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.
If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration: for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}
// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
![]() |
|||||
159 | |||||
160 | $line = $headlines[$i]; |
||||
161 | |||||
162 | array_push($affected_ids, $line["ref_id"]); |
||||
163 | |||||
164 | $updated = make_local_datetime($line['last_updated'], false, |
||||
165 | $user_id); |
||||
166 | |||||
167 | if (get_pref('ENABLE_FEED_CATS', $user_id)) { |
||||
168 | $line['feed_title'] = $line['cat_title']." / ".$line['feed_title']; |
||||
169 | } |
||||
170 | |||||
171 | $article_labels = Article::get_article_labels($line["ref_id"], $user_id); |
||||
172 | $article_labels_formatted = ""; |
||||
173 | |||||
174 | if (is_array($article_labels) && count($article_labels) > 0) { |
||||
175 | $article_labels_formatted = implode(", ", array_map(function($a) { |
||||
176 | return $a[1]; |
||||
177 | }, $article_labels)); |
||||
178 | } |
||||
179 | |||||
180 | $tpl->setVariable('FEED_TITLE', $line["feed_title"]); |
||||
181 | $tpl->setVariable('ARTICLE_TITLE', $line["title"]); |
||||
182 | $tpl->setVariable('ARTICLE_LINK', $line["link"]); |
||||
183 | $tpl->setVariable('ARTICLE_UPDATED', $updated); |
||||
184 | $tpl->setVariable('ARTICLE_EXCERPT', |
||||
185 | truncate_string(strip_tags($line["content"]), 300)); |
||||
186 | // $tpl->setVariable('ARTICLE_CONTENT', |
||||
187 | // strip_tags($article_content)); |
||||
188 | $tpl->setVariable('ARTICLE_LABELS', $article_labels_formatted, true); |
||||
189 | |||||
190 | $tpl->addBlock('article'); |
||||
191 | |||||
192 | $tpl_t->setVariable('FEED_TITLE', $line["feed_title"]); |
||||
193 | $tpl_t->setVariable('ARTICLE_TITLE', $line["title"]); |
||||
194 | $tpl_t->setVariable('ARTICLE_LINK', $line["link"]); |
||||
195 | $tpl_t->setVariable('ARTICLE_UPDATED', $updated); |
||||
196 | $tpl_t->setVariable('ARTICLE_LABELS', $article_labels_formatted, true); |
||||
197 | $tpl_t->setVariable('ARTICLE_EXCERPT', |
||||
198 | truncate_string(strip_tags($line["content"]), 300, "..."), true); |
||||
199 | |||||
200 | $tpl_t->addBlock('article'); |
||||
201 | |||||
202 | if ($headlines[$i]['feed_title'] != $headlines[$i + 1]['feed_title']) { |
||||
203 | $tpl->addBlock('feed'); |
||||
204 | $tpl_t->addBlock('feed'); |
||||
205 | } |
||||
206 | |||||
207 | } |
||||
208 | |||||
209 | $tpl->addBlock('digest'); |
||||
210 | $tpl->generateOutputToString($tmp); |
||||
211 | |||||
212 | $tpl_t->addBlock('digest'); |
||||
213 | $tpl_t->generateOutputToString($tmp_t); |
||||
214 | |||||
215 | return array($tmp, $headlines_count, $affected_ids, $tmp_t); |
||||
216 | } |
||||
217 | |||||
218 | } |
||||
219 |