Merge branch '2.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / package / plugin / SQLPackageInstallationPlugin.class.php
1 <?php
2 namespace wcf\system\package\plugin;
3 use wcf\data\package\Package;
4 use wcf\data\package\PackageList;
5 use wcf\system\exception\SystemException;
6 use wcf\system\package\PackageArchive;
7 use wcf\system\package\PackageInstallationSQLParser;
8 use wcf\system\WCF;
9
10 /**
11 * Executes the delivered sql file.
12 *
13 * @author Alexander Ebert
14 * @copyright 2001-2014 WoltLab GmbH
15 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
16 * @package com.woltlab.wcf
17 * @subpackage system.package.plugin
18 * @category Community Framework
19 */
20 class SQLPackageInstallationPlugin extends AbstractPackageInstallationPlugin {
21 /**
22 * @see \wcf\system\package\plugin\AbstractPackageInstallationPlugin::$tableName
23 */
24 public $tableName = 'package_installation_sql_log';
25
26 /**
27 * @see \wcf\system\package\plugin\IPackageInstallationPlugin::install()
28 */
29 public function install() {
30 parent::install();
31
32 // extract sql file from archive
33 if ($queries = $this->getSQL($this->instruction['value'])) {
34 $package = $this->installation->getPackage();
35
36 // replace app1_ with app{WCF_N}_ in the table names for
37 // all applications
38 $packageList = new PackageList();
39 $packageList->getConditionBuilder()->add('package.isApplication = ?', array(1));
40 $packageList->readObjects();
41 foreach ($packageList as $package) {
42 $abbreviation = Package::getAbbreviation($package->package);
43
44 $queries = str_replace($abbreviation.'1_', $abbreviation.WCF_N.'_', $queries);
45 }
46
47 // check queries
48 $parser = new PackageInstallationSQLParser($queries, $this->installation->getPackage(), $this->installation->getAction());
49 $conflicts = $parser->test();
50 if (!empty($conflicts) && (isset($conflicts['CREATE TABLE']) || isset($conflicts['DROP TABLE']))) {
51 $unknownCreateTable = isset($conflicts['CREATE TABLE']) ? $conflicts['CREATE TABLE'] : array();
52 $unknownDropTable = isset($conflicts['DROP TABLE']) ? $conflicts['DROP TABLE'] : array();
53
54 $errorMessage = "Can't";
55 if (!empty($unknownDropTable)) {
56 $errorMessage .= " drop unknown table";
57 if (count($unknownDropTable) > 1) {
58 $errorMessage .= "s";
59 }
60 $errorMessage .= " '".implode("', '", $unknownDropTable)."'";
61 }
62 if (!empty($unknownCreateTable)) {
63 if (!empty($unknownDropTable)) {
64 $errorMessage .= " and can't";
65 }
66
67 $errorMessage .= " overwrite unknown table";
68 if (count($unknownCreateTable) > 1) {
69 $errorMessage .= "s";
70 }
71 $errorMessage .= " '".implode("', '", $unknownCreateTable)."'";
72 }
73
74 throw new SystemException($errorMessage);
75 }
76
77 // execute queries
78 $parser->execute();
79
80 // log changes
81 $parser->log();
82 }
83 }
84
85 /**
86 * @see \wcf\system\package\plugin\IPackageInstallationPlugin::uninstall()
87 */
88 public function uninstall() {
89 // get logged sql tables/columns
90 $sql = "SELECT *
91 FROM wcf".WCF_N."_package_installation_sql_log
92 WHERE packageID = ?
93 ORDER BY sqlIndex DESC, sqlColumn DESC";
94 $statement = WCF::getDB()->prepareStatement($sql);
95 $statement->execute(array($this->installation->getPackageID()));
96 $entries = array();
97 while ($row = $statement->fetchArray()) {
98 $entries[] = $row;
99 }
100
101 // get all tablenames from database
102 $existingTableNames = WCF::getDB()->getEditor()->getTablenames();
103
104 // delete or alter tables
105 foreach ($entries as $entry) {
106 // don't alter table if it should be dropped
107 if (!empty($entry['sqlColumn'])/* || !empty($entry['sqlIndex'])*/) {
108 $isDropped = false;
109 foreach ($entries as $entry2) {
110 if ($entry['sqlTable'] == $entry2['sqlTable'] && empty($entry2['sqlColumn']) && empty($entry2['sqlIndex'])) {
111 $isDropped = true;
112 }
113 }
114 if ($isDropped) continue;
115 }
116 // drop table
117 if (!empty($entry['sqlTable']) && empty($entry['sqlColumn']) && empty($entry['sqlIndex'])) {
118 WCF::getDB()->getEditor()->dropTable($entry['sqlTable']);
119 }
120 // drop column
121 else if (in_array($entry['sqlTable'], $existingTableNames) && !empty($entry['sqlColumn'])) {
122 WCF::getDB()->getEditor()->dropColumn($entry['sqlTable'], $entry['sqlColumn']);
123 }
124 // drop index
125 else if (in_array($entry['sqlTable'], $existingTableNames) && !empty($entry['sqlIndex'])) {
126 if (substr($entry['sqlIndex'], -3) == '_fk') {
127 WCF::getDB()->getEditor()->dropForeignKey($entry['sqlTable'], $entry['sqlIndex']);
128 }
129 else {
130 WCF::getDB()->getEditor()->dropIndex($entry['sqlTable'], $entry['sqlIndex']);
131 }
132 }
133 }
134 // delete from log table
135 parent::uninstall();
136 }
137
138 /**
139 * Extracts and returns the sql file.
140 * If the specified sql file was not found, an error message is thrown.
141 *
142 * @param string $filename
143 * @return string
144 */
145 protected function getSQL($filename) {
146 // search sql files in package archive
147 if (($fileindex = $this->installation->getArchive()->getTar()->getIndexByFilename($filename)) === false) {
148 throw new SystemException("SQL file '".$filename."' not found.");
149 }
150
151 // extract sql file to string
152 return $this->installation->getArchive()->getTar()->extractToString($fileindex);
153 }
154
155 /**
156 * @see \wcf\system\package\plugin\IPackageInstallationPlugin::isValid()
157 */
158 public static function isValid(PackageArchive $archive, $instruction) {
159 if (preg_match('~\.sql$~', $instruction)) {
160 // check if file actually exists
161 try {
162 if ($archive->getTar()->getIndexByFilename($instruction) === false) {
163 return false;
164 }
165 }
166 catch (\SystemException $e) {
167 return false;
168 }
169
170 return true;
171 }
172
173 return false;
174 }
175 }