qa-xml-sitemap.php 7.55 KB
Newer Older
Scott committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<?php
/*
	Question2Answer by Gideon Greenspan and contributors
	http://www.question2answer.org/

	File: qa-plugin/xml-sitemap/qa-xml-sitemap.php
	Description: Page module class for XML sitemap plugin


	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	More about this license: http://www.question2answer.org/license.php
*/

Scott committed
23 24 25 26 27 28 29 30 31 32 33
class qa_xml_sitemap
{
	public function option_default($option)
	{
		switch ($option) {
			case 'xml_sitemap_show_questions':
			case 'xml_sitemap_show_users':
			case 'xml_sitemap_show_tag_qs':
			case 'xml_sitemap_show_category_qs':
			case 'xml_sitemap_show_categories':
				return true;
Scott committed
34
		}
Scott committed
35
	}
Scott committed
36 37


Scott committed
38 39
	public function admin_form()
	{
Scott committed
40
		require_once QA_INCLUDE_DIR . 'util/sort.php';
Scott committed
41

Scott committed
42
		$saved = false;
Scott committed
43

Scott committed
44 45
		if (qa_clicked('xml_sitemap_save_button')) {
			qa_opt('xml_sitemap_show_questions', (int)qa_post_text('xml_sitemap_show_questions_field'));
Scott committed
46

Scott committed
47 48
			if (!QA_FINAL_EXTERNAL_USERS)
				qa_opt('xml_sitemap_show_users', (int)qa_post_text('xml_sitemap_show_users_field'));
Scott committed
49

Scott committed
50 51
			if (qa_using_tags())
				qa_opt('xml_sitemap_show_tag_qs', (int)qa_post_text('xml_sitemap_show_tag_qs_field'));
Scott committed
52

Scott committed
53 54 55
			if (qa_using_categories()) {
				qa_opt('xml_sitemap_show_category_qs', (int)qa_post_text('xml_sitemap_show_category_qs_field'));
				qa_opt('xml_sitemap_show_categories', (int)qa_post_text('xml_sitemap_show_categories_field'));
Scott committed
56 57
			}

Scott committed
58
			$saved = true;
Scott committed
59 60
		}

Scott committed
61
		$form = array(
Scott committed
62
			'ok' => $saved ? 'XML sitemap settings saved' : null,
Scott committed
63

Scott committed
64 65 66 67 68 69
			'fields' => array(
				'questions' => array(
					'label' => 'Include question pages',
					'type' => 'checkbox',
					'value' => (int)qa_opt('xml_sitemap_show_questions'),
					'tags' => 'name="xml_sitemap_show_questions_field"',
Scott committed
70
				),
Scott committed
71
			),
Scott committed
72

Scott committed
73 74 75 76
			'buttons' => array(
				array(
					'label' => 'Save Changes',
					'tags' => 'name="xml_sitemap_save_button"',
Scott committed
77
				),
Scott committed
78 79 80
			),
		);

Scott committed
81 82
		if (!QA_FINAL_EXTERNAL_USERS) {
			$form['fields']['users'] = array(
Scott committed
83 84 85 86
				'label' => 'Include user pages',
				'type' => 'checkbox',
				'value' => (int)qa_opt('xml_sitemap_show_users'),
				'tags' => 'name="xml_sitemap_show_users_field"',
Scott committed
87
			);
Scott committed
88
		}
Scott committed
89

Scott committed
90 91
		if (qa_using_tags()) {
			$form['fields']['tagqs'] = array(
Scott committed
92 93 94 95 96
				'label' => 'Include question list for each tag',
				'type' => 'checkbox',
				'value' => (int)qa_opt('xml_sitemap_show_tag_qs'),
				'tags' => 'name="xml_sitemap_show_tag_qs_field"',
			);
Scott committed
97
		}
Scott committed
98

Scott committed
99
		if (qa_using_categories()) {
Scott committed
100
			$form['fields']['categoryqs'] = array(
Scott committed
101 102 103 104 105
				'label' => 'Include question list for each category',
				'type' => 'checkbox',
				'value' => (int)qa_opt('xml_sitemap_show_category_qs'),
				'tags' => 'name="xml_sitemap_show_category_qs_field"',
			);
Scott committed
106

Scott committed
107
			$form['fields']['categories'] = array(
Scott committed
108 109 110 111 112
				'label' => 'Include category browser',
				'type' => 'checkbox',
				'value' => (int)qa_opt('xml_sitemap_show_categories'),
				'tags' => 'name="xml_sitemap_show_categories_field"',
			);
Scott committed
113 114
		}

Scott committed
115 116
		return $form;
	}
Scott committed
117 118


Scott committed
119 120 121 122 123 124 125 126 127 128
	public function suggest_requests()
	{
		return array(
			array(
				'title' => 'XML Sitemap',
				'request' => 'sitemap.xml',
				'nav' => null, // 'M'=main, 'F'=footer, 'B'=before main, 'O'=opposite main, null=none
			),
		);
	}
Scott committed
129 130


Scott committed
131 132
	public function match_request($request)
	{
Scott committed
133
		return ($request == 'sitemap.xml');
Scott committed
134
	}
Scott committed
135 136


Scott committed
137 138 139
	public function process_request($request)
	{
		@ini_set('display_errors', 0); // we don't want to show PHP errors inside XML
Scott committed
140

Scott committed
141
		header('Content-type: text/xml; charset=utf-8');
Scott committed
142

Scott committed
143 144
		echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
		echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
Scott committed
145 146


Scott committed
147
		// Question pages
Scott committed
148

Scott committed
149
		if (qa_opt('xml_sitemap_show_questions')) {
Scott committed
150
			$hotstats = qa_db_read_one_assoc(qa_db_query_sub(
Scott committed
151 152
				"SELECT MIN(hotness) AS base, MAX(hotness)-MIN(hotness) AS spread FROM ^posts WHERE type='Q'"
			));
Scott committed
153

Scott committed
154
			$nextpostid = 0;
Scott committed
155

Scott committed
156
			while (1) {
Scott committed
157
				$questions = qa_db_read_all_assoc(qa_db_query_sub(
Scott committed
158 159 160
					"SELECT postid, title, hotness FROM ^posts WHERE postid>=# AND type='Q' ORDER BY postid LIMIT 100",
					$nextpostid
				));
Scott committed
161

Scott committed
162 163 164 165 166
				if (!count($questions))
					break;

				foreach ($questions as $question) {
					$this->sitemap_output(qa_q_request($question['postid'], $question['title']),
Scott committed
167 168
						0.1 + 0.9 * ($question['hotness'] - $hotstats['base']) / (1 + $hotstats['spread']));
					$nextpostid = max($nextpostid, $question['postid'] + 1);
Scott committed
169 170
				}
			}
Scott committed
171
		}
Scott committed
172 173


Scott committed
174
		// User pages
Scott committed
175

Scott committed
176
		if (!QA_FINAL_EXTERNAL_USERS && qa_opt('xml_sitemap_show_users')) {
Scott committed
177
			$nextuserid = 0;
Scott committed
178

Scott committed
179
			while (1) {
Scott committed
180
				$users = qa_db_read_all_assoc(qa_db_query_sub(
Scott committed
181 182 183
					"SELECT userid, handle FROM ^users WHERE userid>=# ORDER BY userid LIMIT 100",
					$nextuserid
				));
Scott committed
184

Scott committed
185 186
				if (!count($users))
					break;
Scott committed
187

Scott committed
188
				foreach ($users as $user) {
Scott committed
189 190
					$this->sitemap_output('user/' . $user['handle'], 0.25);
					$nextuserid = max($nextuserid, $user['userid'] + 1);
Scott committed
191 192
				}
			}
Scott committed
193
		}
Scott committed
194 195


Scott committed
196
		// Tag pages
Scott committed
197

Scott committed
198
		if (qa_using_tags() && qa_opt('xml_sitemap_show_tag_qs')) {
Scott committed
199
			$nextwordid = 0;
Scott committed
200

Scott committed
201
			while (1) {
Scott committed
202
				$tagwords = qa_db_read_all_assoc(qa_db_query_sub(
Scott committed
203 204 205
					"SELECT wordid, word, tagcount FROM ^words WHERE wordid>=# AND tagcount>0 ORDER BY wordid LIMIT 100",
					$nextwordid
				));
Scott committed
206

Scott committed
207 208
				if (!count($tagwords))
					break;
Scott committed
209

Scott committed
210
				foreach ($tagwords as $tagword) {
Scott committed
211 212
					$this->sitemap_output('tag/' . $tagword['word'], 0.5 / (1 + (1 / $tagword['tagcount']))); // priority between 0.25 and 0.5 depending on tag frequency
					$nextwordid = max($nextwordid, $tagword['wordid'] + 1);
Scott committed
213 214
				}
			}
Scott committed
215
		}
Scott committed
216 217


Scott committed
218
		// Question list for each category
Scott committed
219

Scott committed
220
		if (qa_using_categories() && qa_opt('xml_sitemap_show_category_qs')) {
Scott committed
221
			$nextcategoryid = 0;
Scott committed
222

Scott committed
223
			while (1) {
Scott committed
224
				$categories = qa_db_read_all_assoc(qa_db_query_sub(
Scott committed
225 226 227
					"SELECT categoryid, backpath FROM ^categories WHERE categoryid>=# AND qcount>0 ORDER BY categoryid LIMIT 2",
					$nextcategoryid
				));
Scott committed
228

Scott committed
229 230
				if (!count($categories))
					break;
Scott committed
231

Scott committed
232
				foreach ($categories as $category) {
Scott committed
233 234
					$this->sitemap_output('questions/' . implode('/', array_reverse(explode('/', $category['backpath']))), 0.5);
					$nextcategoryid = max($nextcategoryid, $category['categoryid'] + 1);
Scott committed
235 236
				}
			}
Scott committed
237
		}
Scott committed
238 239


Scott committed
240
		// Pages in category browser
Scott committed
241

Scott committed
242 243
		if (qa_using_categories() && qa_opt('xml_sitemap_show_categories')) {
			$this->sitemap_output('categories', 0.5);
Scott committed
244

Scott committed
245
			$nextcategoryid = 0;
Scott committed
246

Scott committed
247
			while (1) { // only find categories with a child
Scott committed
248 249
				$categories = qa_db_read_all_assoc(qa_db_query_sub(
					"SELECT parent.categoryid, parent.backpath FROM ^categories AS parent " .
Scott committed
250 251 252
					"JOIN ^categories AS child ON child.parentid=parent.categoryid WHERE parent.categoryid>=# GROUP BY parent.categoryid LIMIT 100",
					$nextcategoryid
				));
Scott committed
253

Scott committed
254 255
				if (!count($categories))
					break;
Scott committed
256

Scott committed
257
				foreach ($categories as $category) {
Scott committed
258 259
					$this->sitemap_output('categories/' . implode('/', array_reverse(explode('/', $category['backpath']))), 0.5);
					$nextcategoryid = max($nextcategoryid, $category['categoryid'] + 1);
Scott committed
260 261 262 263
				}
			}
		}

Scott committed
264 265 266
		echo "</urlset>\n";

		return null;
Scott committed
267 268 269
	}


270 271 272 273 274
	/**
	 * @deprecated This function will become private in Q2A 1.8. It is specific to this plugin and
	 * should not be used by outside code.
	 */
	public function sitemap_output($request, $priority)
Scott committed
275
	{
Scott committed
276 277 278
		echo "\t<url>\n" .
			"\t\t<loc>" . qa_xml(qa_path($request, null, qa_opt('site_url'))) . "</loc>\n" .
			"\t\t<priority>" . max(0, min(1.0, $priority)) . "</priority>\n" .
Scott committed
279 280 281
			"\t</url>\n";
	}
}