+++ /dev/null
-<?xml version="1.0"?>
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://www.woltlab.com" targetNamespace="http://www.woltlab.com" elementFormDefault="qualified">
- <!-- include types -->
- <xs:include schemaLocation="types.xsd" />
-
- <!-- data element -->
- <xs:element name="data">
- <xs:complexType>
- <xs:sequence>
- <xs:element name="spider" type="spider" minOccurs="0" maxOccurs="unbounded" />
- </xs:sequence>
- </xs:complexType>
- </xs:element>
-
- <!-- spider element type -->
- <xs:complexType name="spider">
- <xs:attribute name="ident" type="woltlab_varchar" />
- <xs:complexContent>
- <xs:all>
- <xs:element name="name" type="woltlab_varchar" minOccurs="1" />
- <xs:element name="url" type="woltlab_varchar" minOccurs="0" />
- </xs:all>
- </xs:complexContent>
- </xs:complexType>
-</xs:schema>
<expression type="random">@daily</expression>
<canbedisabled>0</canbedisabled>
</cronjob>
- <cronjob name="com.woltlab.wcf.refreshSearchRobots">
- <classname>wcf\system\cronjob\RefreshSearchRobotsCronjob</classname>
- <description>Refreshes list of search robots</description>
- <description language="de">Aktualisiert die Liste der Suchroboter</description>
- <expression type="random">@monthly</expression>
- </cronjob>
<cronjob name="com.woltlab.wcf.dailyCleanUp">
<classname>wcf\system\cronjob\DailyCleanUpCronjob</classname>
<description>Executes daily Cleanup</description>
<canbedisabled>0</canbedisabled>
</cronjob>
</import>
+ <delete>
+ <cronjob name="com.woltlab.wcf.refreshSearchRobots">
+ <classname>wcf\system\cronjob\RefreshSearchRobotsCronjob</classname>
+ </cronjob>
+ </delete>
</data>
<dl class="plain inlineDataList small">
<dt>{lang}wcf.user.usersOnline.ipAddress{/lang}</dt>
<dd title="{$user->getFormattedIPAddress()}">{@$user->getFormattedIPAddress()|ipSearch}</dd>
-
- {if !$user->spiderID}
+
+ {if !$user->spiderIdentifier}
<dt>{lang}wcf.user.usersOnline.userAgent{/lang}</dt>
<dd title="{$user->userAgent}">{$user->getBrowser()|truncate:30}</dd>
{/if}
{/capture}
{assign var=usersOnline value=$usersOnline+1}
- {elseif $user->spiderID}
+ {elseif $user->spiderIdentifier}
{* search robot *}
{capture append=robotsOnlineList}
<li>
<div class="details userInformation">
<div class="containerHeadline">
- <h3>{if $user->getSpider()->spiderURL}<a {anchorAttributes url=$user->getSpider()->spiderURL}>{$user->getSpider()->spiderName}</a>{else}{$user->getSpider()->spiderName}{/if}</h3>
+ <h3>{if $user->getSpider()->url}
+ <a {anchorAttributes url=$user->getSpider()->url}>{$user->getSpider()->name}</a>{else}{$user->getSpider()->name}{/if}
+ </h3>
{@$locationData}
</div>
--- /dev/null
+<?php
+
+/**
+ * Drops old spider related columns and index from `wcf1_session` table.
+ *
+ * @author Olaf Braun
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+
+use wcf\system\database\table\column\IntDatabaseTableColumn;
+use wcf\system\database\table\index\DatabaseTableForeignKey;
+use wcf\system\database\table\index\DatabaseTableIndex;
+use wcf\system\database\table\PartialDatabaseTable;
+
+return [
+ PartialDatabaseTable::create('wcf1_session')
+ ->columns([
+ IntDatabaseTableColumn::create('spiderID')
+ ->length(10)
+ ->drop(),
+ ])
+ ->indices([
+ DatabaseTableIndex::create('packageID')
+ ->columns(['lastActivityTime', 'spiderID'])
+ ->drop(),
+ ])
+ ->foreignKeys([
+ DatabaseTableForeignKey::create()
+ ->columns(['spiderID'])
+ ->referencedTable('wcf1_spider')
+ ->referencedColumns(['spiderID'])
+ ->onDelete('CASCADE')
+ ->drop(),
+ ]),
+];
--- /dev/null
+<?php
+
+/**
+ * Drops olds `wcf1_spider` table and create new columns to identify spiders in `wcf1_session` table.
+ *
+ * @author Olaf Braun
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+
+use wcf\system\database\table\column\VarcharDatabaseTableColumn;
+use wcf\system\database\table\index\DatabaseTableIndex;
+use wcf\system\database\table\PartialDatabaseTable;
+
+return [
+ PartialDatabaseTable::create('wcf1_spider')
+ ->drop(),
+ PartialDatabaseTable::create('wcf1_session')
+ ->columns([
+ VarcharDatabaseTableColumn::create('spiderIdentifier')
+ ->length(191)
+ ->defaultValue(null),
+ ])
+ ->indices([
+ DatabaseTableIndex::create('packageID')
+ ->columns(['lastActivityTime', 'spiderIdentifier']),
+ ]),
+];
{
parent::readParameters();
- if (WCF::getSession()->spiderID) {
+ if (WCF::getSession()->spiderIdentifier) {
throw new PermissionDeniedException();
}
}
{
parent::readParameters();
- if (WCF::getSession()->spiderID) {
+ if (WCF::getSession()->spiderIdentifier) {
throw new PermissionDeniedException();
}
}
* @property-read int|null $pageObjectID id of the object the latest page visited belongs to
* @property-read int|null $parentPageID id of the parent page of latest page visited
* @property-read int|null $parentPageObjectID id of the object the parent page of latest page visited belongs to
- * @property-read int $spiderID id of the spider the session belongs to
+ * @property-read int $spiderIdentifier identifier of the spider
*/
class Session extends ACPSession
{
+++ /dev/null
-<?php
-
-namespace wcf\data\spider;
-
-use wcf\data\DatabaseObject;
-
-/**
- * Represents a spider.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- *
- * @property-read int $spiderID unique id of the spider
- * @property-read string $spiderIdentifier unique textual identifier of the spider
- * @property-read string $spiderName name of the spider
- * @property-read string $spiderURL link to the spider's website or empty if no such website exists
- */
-class Spider extends DatabaseObject
-{
-}
+++ /dev/null
-<?php
-
-namespace wcf\data\spider;
-
-use wcf\data\AbstractDatabaseObjectAction;
-
-/**
- * Executes spider-related actions.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- *
- * @method Spider create()
- * @method SpiderEditor[] getObjects()
- * @method SpiderEditor getSingleObject()
- */
-class SpiderAction extends AbstractDatabaseObjectAction
-{
- /**
- * @inheritDoc
- */
- protected $className = SpiderEditor::class;
-}
+++ /dev/null
-<?php
-
-namespace wcf\data\spider;
-
-use wcf\data\DatabaseObjectEditor;
-
-/**
- * Provides functions to edit spiders.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- *
- * @method static Spider create(array $parameters = [])
- * @method Spider getDecoratedObject()
- * @mixin Spider
- */
-class SpiderEditor extends DatabaseObjectEditor
-{
- /**
- * @inheritDoc
- */
- protected static $baseClass = Spider::class;
-}
+++ /dev/null
-<?php
-
-namespace wcf\data\spider;
-
-use wcf\data\DatabaseObjectList;
-
-/**
- * Represents a list of spiders.
- *
- * @author Alexander Ebert
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- *
- * @method Spider current()
- * @method Spider[] getObjects()
- * @method Spider|null getSingleObject()
- * @method Spider|null search($objectID)
- * @property Spider[] $objects
- */
-class SpiderList extends DatabaseObjectList
-{
- /**
- * @inheritDoc
- */
- public $className = Spider::class;
-}
namespace wcf\data\user\online;
use wcf\data\page\PageCache;
-use wcf\data\spider\Spider;
use wcf\data\user\UserProfile;
-use wcf\system\cache\builder\SpiderCacheBuilder;
use wcf\system\event\EventHandler;
use wcf\system\page\handler\IOnlineLocationPageHandler;
+use wcf\system\spider\Spider;
+use wcf\system\spider\SpiderHandler;
use wcf\system\WCF;
use wcf\util\StringUtil;
use wcf\util\UserAgent;
* @property-read int|null $pageObjectID id of the object the last visited page belongs to
* @property-read int|null $parentPageObjectID id of the parent of the object the last visited page belongs to
* @property-read string|null $userOnlineMarking HTML code used to print the formatted name of a user group member
+ * @property-read string $spiderIdentifier identifier of the spider
*/
class UserOnline extends UserProfile
{
/**
* spider object
- * @var Spider
*/
- protected $spider;
+ protected ?Spider $spider;
/**
* Returns the formatted username.
/**
* Returns the spider object
- *
- * @return Spider|null
*/
- public function getSpider()
+ public function getSpider(): ?Spider
{
- if (!$this->spiderID) {
+ if (!$this->spiderIdentifier) {
return null;
}
- if ($this->spider === null) {
- $spiderList = SpiderCacheBuilder::getInstance()->getData();
- $this->spider = $spiderList[$this->spiderID];
+ if (!isset($this->spider)) {
+ $this->spider = SpiderHandler::getInstance()->getSpider($this->spiderIdentifier);
}
return $this->spider;
public function readStats()
{
$conditionBuilder = clone $this->getConditionBuilder();
- $conditionBuilder->add('session.spiderID IS NULL');
+ $conditionBuilder->add('session.spiderIdentifier IS NULL');
$sql = "SELECT user_option_value.userOption" . User::getUserOptionID('canViewOnlineStatus') . " AS canViewOnlineStatus, session.userID
FROM wcf" . WCF_N . "_session session
public function show()
{
// update profile hits
- if ($this->user->userID != WCF::getUser()->userID && !WCF::getSession()->spiderID && !$this->user->isProtected()) {
+ if ($this->user->userID != WCF::getUser()->userID && !WCF::getSession()->spiderIdentifier && !$this->user->isProtected()) {
$editor = new UserEditor($this->user->getDecoratedObject());
$editor->updateCounters(['profileHits' => 1]);
$this->objectList->checkRecord();
if (!USERS_ONLINE_SHOW_ROBOTS) {
- $this->objectList->getConditionBuilder()->add('session.spiderID IS NULL');
+ $this->objectList->getConditionBuilder()->add('session.spiderIdentifier IS NULL');
}
if (!USERS_ONLINE_SHOW_GUESTS) {
if (USERS_ONLINE_SHOW_ROBOTS) {
- $this->objectList->getConditionBuilder()->add('(session.userID IS NOT NULL OR session.spiderID IS NOT NULL)');
+ $this->objectList->getConditionBuilder()->add('(session.userID IS NOT NULL OR session.spiderIdentifier IS NOT NULL)');
} else {
$this->objectList->getConditionBuilder()->add('session.userID IS NOT NULL');
}
}
$this->objectList->sqlSelects .= ", CASE WHEN session.userID IS NULL THEN 1 ELSE 0 END AS userIsGuest";
- $this->objectList->sqlSelects .= ", CASE WHEN session.spiderID IS NOT NULL THEN 1 ELSE 0 END AS userIsRobot";
+ $this->objectList->sqlSelects .= ", CASE WHEN session.spiderIdentifier IS NOT NULL THEN 1 ELSE 0 END AS userIsRobot";
}
/**
+++ /dev/null
-<?php
-
-namespace wcf\system\cache\builder;
-
-use wcf\data\spider\SpiderList;
-
-/**
- * Caches the list of search engine spiders.
- *
- * @author Marcel Werk
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- */
-class SpiderCacheBuilder extends AbstractCacheBuilder
-{
- /**
- * @inheritDoc
- */
- public function rebuild(array $parameters)
- {
- $spiderList = new SpiderList();
- $spiderList->sqlOrderBy = "spider.spiderID ASC";
- $spiderList->readObjects();
-
- if (isset($parameters['fastLookup'])) {
- $firstCharacter = [];
- $mapping = [];
- foreach ($spiderList as $spider) {
- if (!isset($firstCharacter[$spider->spiderIdentifier[0]])) {
- $firstCharacter[$spider->spiderIdentifier[0]] = [];
- }
- $firstCharacter[$spider->spiderIdentifier[0]][] = \substr($spider->spiderIdentifier, 1);
-
- $mapping[$spider->spiderIdentifier] = $spider->spiderID;
- }
-
- $regex = '';
- foreach ($firstCharacter as $char => $spiders) {
- if ($regex !== '') {
- $regex .= '|';
- }
- $regex .= \sprintf(
- '(?:%s(?:%s))',
- \preg_quote($char, '/'),
- \implode('|', \array_map(static function ($identifier) {
- return \preg_quote($identifier, '/');
- }, $spiders))
- );
- }
-
- if ($regex === '') {
- // This regex will never match anything.
- $regex = '(?!)';
- }
-
- return [
- 'regex' => "/{$regex}/",
- 'mapping' => $mapping,
- ];
- }
-
- return $spiderList->getObjects();
- }
-}
+++ /dev/null
-<?php
-
-namespace wcf\system\cronjob;
-
-use GuzzleHttp\Psr7\Request;
-use wcf\data\cronjob\Cronjob;
-use wcf\system\cache\builder\SpiderCacheBuilder;
-use wcf\system\io\HttpFactory;
-use wcf\system\WCF;
-use wcf\util\XML;
-
-/**
- * Refreshes list of search robots.
- *
- * @author Marcel Werk
- * @copyright 2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- */
-class RefreshSearchRobotsCronjob extends AbstractCronjob
-{
- /**
- * URL to the spider list.
- */
- private const SPIDER_LIST_URL = 'http://assets.woltlab.com/spiderlist/typhoon/list.xml';
-
- /**
- * @inheritDoc
- */
- public function execute(Cronjob $cronjob)
- {
- parent::execute($cronjob);
-
- $client = HttpFactory::getDefaultClient();
- $request = new Request('GET', self::SPIDER_LIST_URL);
- $response = $client->send($request);
-
- $xml = new XML();
- $xml->loadXML('list.xml', (string)$response->getBody());
-
- $xpath = $xml->xpath();
-
- // fetch spiders
- $spiders = $xpath->query('/ns:data/ns:spider');
-
- if (!empty($spiders)) {
- $existingSpiders = SpiderCacheBuilder::getInstance()->getData();
- $statementParameters = [];
-
- /** @var \DOMElement $spider */
- foreach ($spiders as $spider) {
- $identifier = \mb_strtolower($spider->getAttribute('ident'));
- $name = $xpath->query('ns:name', $spider)->item(0);
- $info = $xpath->query('ns:url', $spider)->item(0);
-
- $statementParameters[$identifier] = [
- 'spiderIdentifier' => $identifier,
- 'spiderName' => $name->nodeValue,
- 'spiderURL' => $info ? $info->nodeValue : '',
- ];
- }
-
- if (!empty($statementParameters)) {
- $sql = "INSERT INTO wcf" . WCF_N . "_spider
- (spiderIdentifier, spiderName, spiderURL)
- VALUES (?, ?, ?)
- ON DUPLICATE KEY UPDATE spiderName = VALUES(spiderName),
- spiderURL = VALUES(spiderURL)";
- $statement = WCF::getDB()->prepareStatement($sql);
-
- WCF::getDB()->beginTransaction();
- foreach ($statementParameters as $parameters) {
- $statement->execute([
- $parameters['spiderIdentifier'],
- $parameters['spiderName'],
- $parameters['spiderURL'],
- ]);
- }
- WCF::getDB()->commitTransaction();
- }
-
- // delete obsolete entries
- $sql = "DELETE FROM wcf" . WCF_N . "_spider WHERE spiderIdentifier = ?";
- $statement = WCF::getDB()->prepareStatement($sql);
- foreach ($existingSpiders as $spider) {
- if (!isset($statementParameters[$spider->spiderIdentifier])) {
- $statement->execute([$spider->spiderIdentifier]);
- }
- }
-
- // clear spider cache
- SpiderCacheBuilder::getInstance()->reset();
- }
- }
-}
use wcf\data\session\SessionEditor;
use wcf\data\user\User;
use wcf\data\user\UserEditor;
-use wcf\system\cache\builder\SpiderCacheBuilder;
use wcf\system\cache\builder\UserGroupOptionCacheBuilder;
use wcf\system\cache\builder\UserGroupPermissionCacheBuilder;
use wcf\system\database\exception\DatabaseQueryExecutionException;
use wcf\system\request\RouteHandler;
use wcf\system\session\event\PreserveVariablesCollecting;
use wcf\system\SingletonFactory;
+use wcf\system\spider\SpiderHandler;
use wcf\system\user\storage\UserStorageHandler;
use wcf\system\WCF;
use wcf\system\WCFACP;
* @property-read int|null $pageObjectID id of the object the latest page visited belongs to
* @property-read int|null $parentPageID id of the parent page of latest page visited
* @property-read int|null $parentPageObjectID id of the object the parent page of latest page visited belongs to
- * @property-read int $spiderID id of the spider the session belongs to
+ * @property-read int $spiderIdentifier identifier of the spider
*/
final class SessionHandler extends SingletonFactory
{
return $this->sessionID;
case 'userID':
return $this->user->userID;
- case 'spiderID':
+ case 'spiderIdentifier':
if ($this->userID) {
return null;
}
return null;
}
- return $this->legacySession->spiderID;
+ return $this->legacySession->spiderIdentifier;
case 'pageID':
case 'pageObjectID':
case 'parentPageID':
$condition->add('userID = ?', [$row['userID']]);
} else {
$condition->add('userID IS NULL');
- $condition->add('(sessionID = ? OR spiderID = ?)', [
+ $condition->add('(sessionID = ? OR spiderIdentifier = ?)', [
$row['sessionID'],
- $this->getSpiderID(UserUtil::getUserAgent()),
+ $this->getSpiderIdentifier(UserUtil::getUserAgent()),
]);
}
if (!$this->isACP) {
// Try to find an existing spider session. Order by lastActivityTime to maintain a
// stable selection in case duplicates exist for some reason.
- $spiderID = $this->getSpiderID(UserUtil::getUserAgent());
- if ($spiderID) {
+ $spiderIdentifier = $this->getSpiderIdentifier(UserUtil::getUserAgent());
+ if ($spiderIdentifier) {
$sql = "SELECT *
FROM wcf1_session
- WHERE spiderID = ?
+ WHERE spiderIdentifier = ?
AND userID IS NULL
ORDER BY lastActivityTime DESC";
$statement = WCF::getDB()->prepare($sql);
- $statement->execute([$spiderID]);
+ $statement->execute([$spiderIdentifier]);
$this->legacySession = $statement->fetchSingleObject(LegacySession::class);
}
private function createLegacySession(): LegacySession
{
- $spiderID = null;
+ $spiderIdentifier = null;
if (!$this->user->userID) {
- $spiderID = $this->getSpiderID(UserUtil::getUserAgent());
+ $spiderIdentifier = $this->getSpiderIdentifier(UserUtil::getUserAgent());
}
// save session
'lastActivityTime' => TIME_NOW,
'requestURI' => UserUtil::getRequestURI(),
'requestMethod' => !empty($_SERVER['REQUEST_METHOD']) ? \substr($_SERVER['REQUEST_METHOD'], 0, 7) : '',
- 'spiderID' => $spiderID,
+ 'spiderIdentifier' => $spiderIdentifier,
];
return SessionEditor::create($sessionData);
}
/**
- * Returns the spider id for given user agent.
+ * Returns the spider identifier for given user agent.
*/
- private function getSpiderID(string $userAgent): ?int
+ private function getSpiderIdentifier(string $userAgent): ?string
{
- $data = SpiderCacheBuilder::getInstance()->getData(['fastLookup' => true]);
- $userAgent = \strtolower($userAgent);
-
- if (!\preg_match($data['regex'], $userAgent, $matches)) {
- return null;
- }
-
- return $data['mapping'][$matches[0]];
+ return SpiderHandler::getInstance()->getIdentifier($userAgent);
}
/**
--- /dev/null
+<?php
+
+namespace wcf\system\spider;
+
+/**
+ * @author Olaf Braun
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+final readonly class Spider
+{
+ public function __construct(
+ public string $identifier,
+ public string $name,
+ public ?string $url
+ ) {
+ }
+}
--- /dev/null
+<?php
+
+namespace wcf\system\spider;
+
+use wcf\system\SingletonFactory;
+
+/**
+ * @author Olaf Braun
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ */
+final class SpiderHandler extends SingletonFactory
+{
+ public function getSpider(string $identifier): ?Spider
+ {
+ //TODO
+ return null;
+ }
+
+ public function getIdentifier(string $userAgent): ?string
+ {
+ $userAgent = \strtolower($userAgent);
+ //TODO
+
+ return null;
+ }
+}
VALUES (?, ?, ?, ?)";
$statement = WCF::getDB()->prepare($sql);
$statement->execute([$this->getObjectTypeID($objectType), $objectID, WCF::getUser()->userID, $time]);
- } elseif (WCF::getSession()->spiderID === null) {
+ } elseif (WCF::getSession()->spiderIdentifier === null) {
WCF::getSession()->register(
'trackedUserVisit_' . $this->getObjectTypeID($objectType) . '_' . $objectID,
$time
// reset storage
UserStorageHandler::getInstance()->reset([WCF::getUser()->userID], 'trackedUserVisits');
- } elseif (WCF::getSession()->spiderID === null) {
+ } elseif (WCF::getSession()->spiderIdentifier === null) {
$this->getVisitTime($objectType);
$this->userVisits[$this->getObjectTypeID($objectType)] = $time;
WCF::getSession()->register('trackedUserVisits', $this->userVisits);
@\header('Content-Type: text/html; charset=UTF-8');
// send no cache headers
- if (!WCF::getSession()->spiderID) {
+ if (!WCF::getSession()->spiderIdentifier) {
self::sendNoCacheHeaders();
}
pageObjectID INT(10),
parentPageID INT(10),
parentPageObjectID INT(10),
- spiderID INT(10),
- KEY packageID (lastActivityTime, spiderID),
+ spiderIdentifier VARCHAR(191) DEFAULT NULL,
+ KEY packageID (lastActivityTime, spiderIdentifier),
KEY pageID (pageID, pageObjectID),
KEY parentPageID (parentPageID, parentPageObjectID),
UNIQUE KEY uniqueUserID (userID)
UNIQUE KEY smileyCode (smileyCode)
);
-DROP TABLE IF EXISTS wcf1_spider;
-CREATE TABLE wcf1_spider (
- spiderID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
- spiderIdentifier VARCHAR(191) DEFAULT '',
- spiderName VARCHAR(255) DEFAULT '',
- spiderURL VARCHAR(255) DEFAULT '',
- UNIQUE KEY spiderIdentifier (spiderIdentifier)
-);
-
DROP TABLE IF EXISTS wcf1_stat_daily;
CREATE TABLE wcf1_stat_daily (
statID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
/* SQL_PARSER_OFFSET */
ALTER TABLE wcf1_session ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
-ALTER TABLE wcf1_session ADD FOREIGN KEY (spiderID) REFERENCES wcf1_spider (spiderID) ON DELETE CASCADE;
ALTER TABLE wcf1_session ADD FOREIGN KEY (pageID) REFERENCES wcf1_page (pageID) ON DELETE SET NULL;
ALTER TABLE wcf1_session ADD FOREIGN KEY (parentPageID) REFERENCES wcf1_page (pageID) ON DELETE SET NULL;