edit.php (114876B)
1 <?php 2 // Edit extension, https://github.com/annaesvensson/yellow-edit 3 4 class YellowEdit { 5 const VERSION = "0.9.18"; 6 public $yellow; // access to API 7 public $response; // web response 8 public $merge; // text merge 9 public $editable; // page can be edited? (boolean) 10 11 // Handle initialisation 12 public function onLoad($yellow) { 13 $this->yellow = $yellow; 14 $this->response = new YellowEditResponse($yellow); 15 $this->merge = new YellowEditMerge($yellow); 16 $this->yellow->system->setDefault("editLocation", "/edit/"); 17 $this->yellow->system->setDefault("editUploadNewLocation", "/media/@group/@filename"); 18 $this->yellow->system->setDefault("editUploadExtensions", ".gif, .jpeg, .jpg, .mp3, .mp4, .ogg, .pdf, .png, .svg, .zip"); 19 $this->yellow->system->setDefault("editKeyboardShortcuts", "ctrl+b bold, ctrl+i italic, ctrl+k strikethrough, ctrl+e code, ctrl+s save, ctrl+alt+p preview"); 20 $this->yellow->system->setDefault("editToolbarButtons", "auto"); 21 $this->yellow->system->setDefault("editEndOfLine", "auto"); 22 $this->yellow->system->setDefault("editNewFile", "page-new-(.*).md"); 23 $this->yellow->system->setDefault("editUserPasswordMinLength", "8"); 24 $this->yellow->system->setDefault("editUserHashAlgorithm", "bcrypt"); 25 $this->yellow->system->setDefault("editUserHashCost", "10"); 26 $this->yellow->system->setDefault("editUserAccess", "create, edit, delete, restore, upload"); 27 $this->yellow->system->setDefault("editUserHome", "/"); 28 $this->yellow->system->setDefault("editLoginRestriction", "0"); 29 $this->yellow->system->setDefault("editLoginSessionTimeout", "2592000"); 30 $this->yellow->system->setDefault("editBruteForceProtection", "25"); 31 } 32 33 // Handle update 34 public function onUpdate($action) { 35 if ($action=="clean" || $action=="daily") { 36 $cleanup = false; 37 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 38 $fileData = $this->yellow->toolbox->readFile($fileNameUser); 39 $fileDataNew = ""; 40 foreach ($this->yellow->toolbox->getTextLines($fileData) as $line) { 41 if (preg_match("/^\s*(.*?)\s*:\s*(.*?)\s*$/", $line, $matches)) { 42 if (lcfirst($matches[1])=="email" && !is_string_empty($matches[2])) { 43 $status = $this->yellow->user->getUser("status", $matches[2]); 44 $reserved = strtotime($this->yellow->user->getUser("modified", $matches[2])) + 60*60*24; 45 $cleanup = $status!="active" && $status!="inactive" && $reserved<=time(); 46 } 47 } 48 if (!$cleanup) $fileDataNew .= $line; 49 } 50 $fileDataNew = rtrim($fileDataNew)."\n"; 51 if ($fileData!=$fileDataNew && !$this->yellow->toolbox->writeFile($fileNameUser, $fileDataNew)) { 52 $this->yellow->toolbox->log("error", "Can't write file '$fileNameUser'!"); 53 } 54 } 55 } 56 57 // Handle request 58 public function onRequest($scheme, $address, $base, $location, $fileName) { 59 $statusCode = 0; 60 if ($this->isEditLocation($location)) { 61 $this->editable = true; 62 $scheme = $this->yellow->system->get("coreServerScheme"); 63 $address = $this->yellow->system->get("coreServerAddress"); 64 $base = rtrim($this->yellow->system->get("coreServerBase").$this->yellow->system->get("editLocation"), "/"); 65 list($scheme, $address, $base, $location, $fileName) = $this->yellow->lookup->getRequestInformation($scheme, $address, $base); 66 $this->yellow->page->setRequestInformation($scheme, $address, $base, $location, $fileName, false); 67 $statusCode = $this->processRequest($scheme, $address, $base, $location, $fileName); 68 } 69 return $statusCode; 70 } 71 72 // Handle command 73 public function onCommand($command, $text) { 74 switch ($command) { 75 case "user": $statusCode = $this->processCommandUser($command, $text); break; 76 default: $statusCode = 0; 77 } 78 return $statusCode; 79 } 80 81 // Handle command help 82 public function onCommandHelp() { 83 return "user [option email password]"; 84 } 85 86 // Handle page meta data 87 public function onParseMetaData($page) { 88 $page->set("editPageUrl", $this->yellow->lookup->normaliseUrl( 89 $this->yellow->system->get("coreServerScheme"), 90 $this->yellow->system->get("coreServerAddress"), 91 $this->yellow->system->get("coreServerBase"), 92 rtrim($this->yellow->system->get("editLocation"), "/").$page->location)); 93 } 94 95 // Handle page content element 96 public function onParseContentElement($page, $name, $text, $attributes, $type) { 97 $output = null; 98 if ($name=="edit" && $type=="inline") { 99 list($target, $description) = $this->yellow->toolbox->getTextList($text, " ", 2); 100 if (is_string_empty($target) || $target=="-") $target = "main"; 101 if (is_string_empty($description)) $description = ucfirst($name); 102 $pageTarget = $target=="main" ? $page->getPage("main") : $page->getPage("main")->getPage($target); 103 $output = "<a href=\"".$pageTarget->get("editPageUrl")."\">".htmlspecialchars($description)."</a>"; 104 } 105 return $output; 106 } 107 108 // Handle page layout 109 public function onParsePageLayout($page, $name) { 110 if ($this->editable) { 111 $this->response->processPageData($page); 112 } 113 } 114 115 // Handle page extra data 116 public function onParsePageExtra($page, $name) { 117 $output = null; 118 if ($this->editable && $name=="header") { 119 $assetLocation = $this->yellow->system->get("coreServerBase").$this->yellow->system->get("coreAssetLocation"); 120 $output = "<link rel=\"stylesheet\" type=\"text/css\" media=\"all\" href=\"{$assetLocation}edit.css\" />\n"; 121 $output .= "<script type=\"text/javascript\" src=\"{$assetLocation}edit.js\"></script>\n"; 122 $output .= "<script type=\"text/javascript\">\n"; 123 $output .= "// <![CDATA[\n"; 124 $output .= "yellow.page = ".json_encode($this->response->getPageData($page)).";\n"; 125 $output .= "yellow.system = ".json_encode($this->response->getSystemData()).";\n"; 126 $output .= "yellow.user = ".json_encode($this->response->getUserData()).";\n"; 127 $output .= "yellow.language = ".json_encode($this->response->getLanguageData()).";\n"; 128 $output .= "// ]]>\n"; 129 $output .= "</script>\n"; 130 } 131 return $output; 132 } 133 134 // Process command to update user account 135 public function processCommandUser($command, $text) { 136 list($option) = $this->yellow->toolbox->getTextArguments($text); 137 switch ($option) { 138 case "": $statusCode = $this->userShow($command, $text); break; 139 case "add": $statusCode = $this->userAdd($command, $text); break; 140 case "change": $statusCode = $this->userChange($command, $text); break; 141 case "remove": $statusCode = $this->userRemove($command, $text); break; 142 default: $statusCode = 400; echo "Yellow $command: Invalid arguments\n"; 143 } 144 return $statusCode; 145 } 146 147 // Show user accounts 148 public function userShow($command, $text) { 149 $data = array(); 150 foreach ($this->yellow->user->settings as $key=>$value) { 151 $data[$key] = "$value[email] - User account by $value[name]."; 152 } 153 uksort($data, "strnatcasecmp"); 154 foreach ($data as $line) echo "$line\n"; 155 if (is_array_empty($data)) echo "Yellow $command: No user accounts\n"; 156 return 200; 157 } 158 159 // Add user account 160 public function userAdd($command, $text) { 161 $status = "ok"; 162 list($option, $email, $password) = $this->yellow->toolbox->getTextArguments($text); 163 if (is_string_empty($email) || is_string_empty($password)) $status = $this->response->status = "incomplete"; 164 if ($status=="ok") $status = $this->getUserAccount("add", $email, $password); 165 if ($status=="ok" && $this->isUserAccountTaken($email)) $status = "taken"; 166 switch ($status) { 167 case "incomplete": echo "ERROR updating settings: Please enter email and password!\n"; break; 168 case "invalid": echo "ERROR updating settings: Please enter a valid email!\n"; break; 169 case "taken": echo "ERROR updating settings: Please enter a different email!\n"; break; 170 case "weak": echo "ERROR updating settings: Please enter a different password!\n"; break; 171 case "short": echo "ERROR updating settings: Please enter a longer password!\n"; break; 172 } 173 if ($status=="ok") { 174 $name = $this->yellow->system->get("sitename"); 175 $userLanguage = $this->yellow->system->get("language"); 176 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 177 $settings = array( 178 "name" => $name, 179 "description" => $this->yellow->language->getText("editUserDescription", $userLanguage), 180 "language" => $userLanguage, 181 "access" => $this->yellow->system->get("editUserAccess"), 182 "home" => $this->yellow->system->get("editUserHome"), 183 "hash" => $this->response->createHash($password), 184 "stamp" => $this->response->createStamp(), 185 "pending" => "none", 186 "failed" => "0", 187 "modified" => date("Y-m-d H:i:s", time()), 188 "status" => "active"); 189 $status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 190 if ($status=="error") echo "ERROR updating settings: Can't write file '$fileNameUser'!\n"; 191 $this->yellow->toolbox->log($status=="ok" ? "info" : "error", "Add user '".strtok($name, " ")."'"); 192 } 193 if ($status=="ok") { 194 $algorithm = $this->yellow->system->get("editUserHashAlgorithm"); 195 $status = substru($this->yellow->user->getUser("hash", $email), 0, 10)!="error-hash" ? "ok" : "error"; 196 if ($status=="error") echo "ERROR updating settings: Hash algorithm '$algorithm' not supported!\n"; 197 } 198 $statusCode = $status=="ok" ? 200 : 500; 199 echo "Yellow $command: User account ".($statusCode!=200 ? "not " : "")."added\n"; 200 return $statusCode; 201 } 202 203 // Change user account 204 public function userChange($command, $text) { 205 $status = "ok"; 206 list($option, $email, $password) = $this->yellow->toolbox->getTextArguments($text); 207 if (is_string_empty($email)) $status = $this->response->status = "invalid"; 208 if ($status=="ok") $status = $this->getUserAccount("change", $email, $password); 209 if ($status=="ok" && !$this->yellow->user->isExisting($email)) $status = "unknown"; 210 switch ($status) { 211 case "invalid": echo "ERROR updating settings: Please enter a valid email!\n"; break; 212 case "unknown": echo "ERROR updating settings: Can't find email '$email'!\n"; break; 213 case "weak": echo "ERROR updating settings: Please enter a different password!\n"; break; 214 case "short": echo "ERROR updating settings: Please enter a longer password!\n"; break; 215 } 216 if ($status=="ok") { 217 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 218 $settings = array( 219 "hash" => is_string_empty($password) ? $this->yellow->user->getUser("hash", $email) : $this->response->createHash($password), 220 "failed" => "0", 221 "modified" => date("Y-m-d H:i:s", time())); 222 $status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 223 if ($status=="error") echo "ERROR updating settings: Can't write file '$fileNameUser'!\n"; 224 } 225 $statusCode = $status=="ok" ? 200 : 500; 226 echo "Yellow $command: User account ".($statusCode!=200 ? "not " : "")."changed\n"; 227 return $statusCode; 228 } 229 230 // Remove user account 231 public function userRemove($command, $text) { 232 $status = "ok"; 233 list($option, $email) = $this->yellow->toolbox->getTextArguments($text); 234 if (is_string_empty($email)) $status = $this->response->status = "invalid"; 235 if ($status=="ok") $status = $this->getUserAccount("remove", $email, ""); 236 if ($status=="ok" && !$this->yellow->user->isExisting($email)) $status = "unknown"; 237 switch ($status) { 238 case "invalid": echo "ERROR updating settings: Please enter a valid email!\n"; break; 239 case "unknown": echo "ERROR updating settings: Can't find email '$email'!\n"; break; 240 } 241 if ($status=="ok") { 242 $name = $this->yellow->user->getUser("name", $email); 243 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 244 $status = $this->yellow->user->remove($fileNameUser, $email) ? "ok" : "error"; 245 if ($status=="error") echo "ERROR updating settings: Can't write file '$fileNameUser'!\n"; 246 $this->yellow->toolbox->log($status=="ok" ? "info" : "error", "Remove user '".strtok($name, " ")."'"); 247 } 248 $statusCode = $status=="ok" ? 200 : 500; 249 echo "Yellow $command: User account ".($statusCode!=200 ? "not " : "")."removed\n"; 250 return $statusCode; 251 } 252 253 // Process request 254 public function processRequest($scheme, $address, $base, $location, $fileName) { 255 $statusCode = 0; 256 if ($this->checkUserAuth($scheme, $address, $base, $location, $fileName)) { 257 switch ($this->yellow->page->getRequest("action")) { 258 case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break; 259 case "login": $statusCode = $this->processRequestLogin($scheme, $address, $base, $location, $fileName); break; 260 case "logout": $statusCode = $this->processRequestLogout($scheme, $address, $base, $location, $fileName); break; 261 case "quit": $statusCode = $this->processRequestQuit($scheme, $address, $base, $location, $fileName); break; 262 case "account": $statusCode = $this->processRequestAccount($scheme, $address, $base, $location, $fileName); break; 263 case "configure": $statusCode = $this->processRequestConfigure($scheme, $address, $base, $location, $fileName); break; 264 case "update": $statusCode = $this->processRequestUpdate($scheme, $address, $base, $location, $fileName); break; 265 case "create": $statusCode = $this->processRequestCreate($scheme, $address, $base, $location, $fileName); break; 266 case "edit": $statusCode = $this->processRequestEdit($scheme, $address, $base, $location, $fileName); break; 267 case "delete": $statusCode = $this->processRequestDelete($scheme, $address, $base, $location, $fileName); break; 268 case "restore": $statusCode = $this->processRequestRestore($scheme, $address, $base, $location, $fileName); break; 269 case "preview": $statusCode = $this->processRequestPreview($scheme, $address, $base, $location, $fileName); break; 270 case "upload": $statusCode = $this->processRequestUpload($scheme, $address, $base, $location, $fileName); break; 271 } 272 } elseif ($this->checkUserUnauth($scheme, $address, $base, $location, $fileName)) { 273 $this->yellow->lookup->requestHandler = "core"; 274 switch ($this->yellow->page->getRequest("action")) { 275 case "": $statusCode = $this->processRequestShow($scheme, $address, $base, $location, $fileName); break; 276 case "signup": $statusCode = $this->processRequestSignup($scheme, $address, $base, $location, $fileName); break; 277 case "forgot": $statusCode = $this->processRequestForgot($scheme, $address, $base, $location, $fileName); break; 278 case "confirm": $statusCode = $this->processRequestConfirm($scheme, $address, $base, $location, $fileName); break; 279 case "approve": $statusCode = $this->processRequestApprove($scheme, $address, $base, $location, $fileName); break; 280 case "recover": $statusCode = $this->processRequestRecover($scheme, $address, $base, $location, $fileName); break; 281 case "reactivate": $statusCode = $this->processRequestReactivate($scheme, $address, $base, $location, $fileName); break; 282 case "verify": $statusCode = $this->processRequestVerify($scheme, $address, $base, $location, $fileName); break; 283 case "change": $statusCode = $this->processRequestChange($scheme, $address, $base, $location, $fileName); break; 284 case "remove": $statusCode = $this->processRequestRemove($scheme, $address, $base, $location, $fileName); break; 285 } 286 } 287 if ($statusCode==0) $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 288 $this->checkUserFailed($scheme, $address, $base, $location, $fileName); 289 return $statusCode; 290 } 291 292 // Process request to show file 293 public function processRequestShow($scheme, $address, $base, $location, $fileName) { 294 $statusCode = 0; 295 if (is_readable($fileName)) { 296 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 297 } else { 298 if ($this->yellow->lookup->isRedirectLocation($location)) { 299 $location = $this->yellow->lookup->getRedirectLocation($location); 300 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); 301 $statusCode = $this->yellow->sendStatus(301, $location); 302 } else { 303 $statusCode = 404; 304 if ($this->response->isUserAccess("create", $location) && $this->response->isCreateLocation($location)) 305 $statusCode = 434; 306 if ($this->response->isUserAccess("restore", $location) && $this->response->isDeletedLocation($location)) { 307 $statusCode = 435; 308 } 309 $this->yellow->page->error($statusCode); 310 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 311 } 312 } 313 return $statusCode; 314 } 315 316 // Process request for user login 317 public function processRequestLogin($scheme, $address, $base, $location, $fileName) { 318 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 319 $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time())); 320 if ($this->yellow->user->save($fileNameUser, $this->response->userEmail, $settings)) { 321 $home = $this->yellow->user->getUser("home", $this->response->userEmail); 322 if (substru($location, 0, strlenu($home))==$home) { 323 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); 324 $statusCode = $this->yellow->sendStatus(303, $location); 325 } else { 326 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $home); 327 $statusCode = $this->yellow->sendStatus(302, $location); 328 } 329 } else { 330 $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 331 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 332 } 333 return $statusCode; 334 } 335 336 // Process request for user logout 337 public function processRequestLogout($scheme, $address, $base, $location, $fileName) { 338 $this->response->userEmail = ""; 339 $this->response->destroyCookies($scheme, $address, $base); 340 $location = $this->yellow->lookup->normaliseUrl( 341 $this->yellow->system->get("coreServerScheme"), 342 $this->yellow->system->get("coreServerAddress"), 343 $this->yellow->system->get("coreServerBase"), 344 $location); 345 $statusCode = $this->yellow->sendStatus(302, $location); 346 return $statusCode; 347 } 348 349 // Process request for user signup 350 public function processRequestSignup($scheme, $address, $base, $location, $fileName) { 351 $this->response->action = "signup"; 352 $this->response->status = "ok"; 353 $name = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $this->yellow->page->getRequest("name"))); 354 $email = trim($this->yellow->page->getRequest("email")); 355 $password = trim($this->yellow->page->getRequest("password")); 356 $consent = trim($this->yellow->page->getRequest("consent")); 357 if (is_string_empty($name) || is_string_empty($email) || is_string_empty($password) || is_string_empty($consent)) $this->response->status = "incomplete"; 358 if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($this->response->action, $email, $password); 359 if ($this->response->status=="ok" && $this->response->isLoginRestriction()) $this->response->status = "next"; 360 if ($this->response->status=="ok" && $this->isUserAccountTaken($email)) $this->response->status = "next"; 361 if ($this->response->status=="ok") { 362 $userLanguage = $this->yellow->lookup->findContentLanguage($fileName, $this->yellow->system->get("language")); 363 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 364 $settings = array( 365 "name" => $name, 366 "description" => $this->yellow->language->getText("editUserDescription", $userLanguage), 367 "language" => $userLanguage, 368 "access" => $this->yellow->system->get("editUserAccess"), 369 "home" => $this->yellow->system->get("editUserHome"), 370 "hash" => $this->response->createHash($password), 371 "stamp" => $this->response->createStamp(), 372 "pending" => "none", 373 "failed" => "0", 374 "modified" => date("Y-m-d H:i:s", time()), 375 "status" => "unconfirmed"); 376 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 377 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 378 } 379 if ($this->response->status=="ok") { 380 $algorithm = $this->yellow->system->get("editUserHashAlgorithm"); 381 $this->response->status = substru($this->yellow->user->getUser("hash", $email), 0, 10)!="error-hash" ? "ok" : "error"; 382 if ($this->response->status=="error") $this->yellow->page->error(500, "Hash algorithm '$algorithm' not supported!"); 383 } 384 if ($this->response->status=="ok") { 385 $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "confirm") ? "next" : "error"; 386 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email message!"); 387 } 388 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 389 return $statusCode; 390 } 391 392 // Process request to confirm user signup 393 public function processRequestConfirm($scheme, $address, $base, $location, $fileName) { 394 $this->response->action = "confirm"; 395 $this->response->status = "ok"; 396 $email = $this->yellow->page->getRequest("email"); 397 $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); 398 if ($this->response->status=="ok") { 399 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 400 $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "unapproved"); 401 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 402 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 403 } 404 if ($this->response->status=="ok") { 405 $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "approve") ? "done" : "error"; 406 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email message!"); 407 } 408 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 409 return $statusCode; 410 } 411 412 // Process request to approve user signup 413 public function processRequestApprove($scheme, $address, $base, $location, $fileName) { 414 $this->response->action = "approve"; 415 $this->response->status = "ok"; 416 $email = $this->yellow->page->getRequest("email"); 417 $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); 418 if ($this->response->status=="ok") { 419 $name = $this->yellow->user->getUser("name", $email); 420 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 421 $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "active"); 422 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 423 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 424 $this->yellow->toolbox->log($this->response->status=="ok" ? "info" : "error", "Add user '".strtok($name, " ")."'"); 425 } 426 if ($this->response->status=="ok") { 427 $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "welcome") ? "done" : "error"; 428 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email message!"); 429 } 430 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 431 return $statusCode; 432 } 433 434 // Process request for forgotten password 435 public function processRequestForgot($scheme, $address, $base, $location, $fileName) { 436 $this->response->action = "forgot"; 437 $this->response->status = "ok"; 438 $email = trim($this->yellow->page->getRequest("email")); 439 if (!filter_var($email, FILTER_VALIDATE_EMAIL)) $this->response->status = "invalid"; 440 if ($this->response->status=="ok" && !$this->yellow->user->isExisting($email)) $this->response->status = "next"; 441 if ($this->response->status=="ok") { 442 $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "recover") ? "next" : "error"; 443 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email message!"); 444 } 445 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 446 return $statusCode; 447 } 448 449 // Process request to recover password 450 public function processRequestRecover($scheme, $address, $base, $location, $fileName) { 451 $this->response->action = "recover"; 452 $this->response->status = "ok"; 453 $email = trim($this->yellow->page->getRequest("email")); 454 $password = trim($this->yellow->page->getRequest("password")); 455 $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); 456 if ($this->response->status=="ok") { 457 if (is_string_empty($password)) $this->response->status = "password"; 458 if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($this->response->action, $email, $password); 459 if ($this->response->status=="ok") { 460 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 461 $settings = array("hash" => $this->response->createHash($password), "failed" => "0", "modified" => date("Y-m-d H:i:s", time())); 462 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 463 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 464 } 465 if ($this->response->status=="ok") { 466 $this->response->destroyCookies($scheme, $address, $base); 467 $this->response->status = "done"; 468 } 469 } 470 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 471 return $statusCode; 472 } 473 474 // Process request to reactivate account 475 public function processRequestReactivate($scheme, $address, $base, $location, $fileName) { 476 $this->response->action = "reactivate"; 477 $this->response->status = "ok"; 478 $email = $this->yellow->page->getRequest("email"); 479 $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); 480 if ($this->response->status=="ok") { 481 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 482 $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "active"); 483 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "done" : "error"; 484 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 485 } 486 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 487 return $statusCode; 488 } 489 490 // Process request to verify email 491 public function processRequestVerify($scheme, $address, $base, $location, $fileName) { 492 $this->response->action = "verify"; 493 $this->response->status = "ok"; 494 $email = $emailSource = $this->yellow->page->getRequest("email"); 495 $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); 496 if ($this->response->status=="ok") { 497 $emailSource = $this->yellow->user->getUser("pending", $email); 498 if ($this->yellow->user->getUser("status", $emailSource)!="active") $this->response->status = "done"; 499 } 500 if ($this->response->status=="ok") { 501 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 502 $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "unchanged"); 503 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 504 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 505 } 506 if ($this->response->status=="ok") { 507 $this->response->status = $this->response->sendMail($scheme, $address, $base, $emailSource, "change") ? "done" : "error"; 508 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email message!"); 509 } 510 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 511 return $statusCode; 512 } 513 514 // Process request to change email or password 515 public function processRequestChange($scheme, $address, $base, $location, $fileName) { 516 $this->response->action = "change"; 517 $this->response->status = "ok"; 518 $email = $emailSource = trim($this->yellow->page->getRequest("email")); 519 $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); 520 if ($this->response->status=="ok") { 521 list($email, $hash) = $this->yellow->toolbox->getTextList($this->yellow->user->getUser("pending", $email), ":", 2); 522 if (!$this->yellow->user->isExisting($email) || is_string_empty($hash)) $this->response->status = "done"; 523 } 524 if ($this->response->status=="ok") { 525 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 526 $settings = array( 527 "hash" => $hash, 528 "pending" => "none", 529 "failed" => "0", 530 "modified" => date("Y-m-d H:i:s", time()), 531 "status" => "active"); 532 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 533 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 534 } 535 if ($this->response->status=="ok" && $email!=$emailSource) { 536 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 537 $this->response->status = $this->yellow->user->remove($fileNameUser, $emailSource) ? "ok" : "error"; 538 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 539 } 540 if ($this->response->status=="ok") { 541 $this->response->destroyCookies($scheme, $address, $base); 542 $this->response->status = "done"; 543 } 544 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 545 return $statusCode; 546 } 547 548 // Process request to quit account 549 public function processRequestQuit($scheme, $address, $base, $location, $fileName) { 550 $this->response->action = "quit"; 551 $this->response->status = "ok"; 552 $name = trim($this->yellow->page->getRequest("name")); 553 $email = $this->response->userEmail; 554 if (is_string_empty($name)) $this->response->status = "none"; 555 if ($this->response->status=="ok" && $name!=$this->yellow->user->getUser("name", $email)) $this->response->status = "mismatch"; 556 if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($this->response->action, $email, ""); 557 if ($this->response->status=="ok") { 558 $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "remove") ? "next" : "error"; 559 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email message!"); 560 } 561 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 562 return $statusCode; 563 } 564 565 // Process request to remove account 566 public function processRequestRemove($scheme, $address, $base, $location, $fileName) { 567 $this->response->action = "remove"; 568 $this->response->status = "ok"; 569 $email = $this->yellow->page->getRequest("email"); 570 $this->response->status = $this->getUserStatus($email, $this->yellow->page->getRequest("action")); 571 if ($this->response->status=="ok") { 572 $name = $this->yellow->user->getUser("name", $email); 573 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 574 $settings = array("failed" => "0", "modified" => date("Y-m-d H:i:s", time()), "status" => "removed"); 575 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 576 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 577 $this->yellow->toolbox->log($this->response->status=="ok" ? "info" : "error", "Remove user '".strtok($name, " ")."'"); 578 } 579 if ($this->response->status=="ok") { 580 $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, "goodbye") ? "ok" : "error"; 581 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email message!"); 582 } 583 if ($this->response->status=="ok") { 584 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 585 $this->response->status = $this->yellow->user->remove($fileNameUser, $email) ? "ok" : "error"; 586 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 587 } 588 if ($this->response->status=="ok") { 589 $this->response->destroyCookies($scheme, $address, $base); 590 $this->response->status = "done"; 591 } 592 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 593 return $statusCode; 594 } 595 596 // Process request to change account settings 597 public function processRequestAccount($scheme, $address, $base, $location, $fileName) { 598 $this->response->action = "account"; 599 $this->response->status = "ok"; 600 $email = trim($this->yellow->page->getRequest("email")); 601 $emailSource = $this->response->userEmail; 602 $password = trim($this->yellow->page->getRequest("password")); 603 $name = trim(preg_replace("/[^\pL\d\-\. ]/u", "-", $this->yellow->page->getRequest("name"))); 604 $language = trim($this->yellow->page->getRequest("language")); 605 if ($email!=$emailSource || !is_string_empty($password)) { 606 if (is_string_empty($email)) $this->response->status = "invalid"; 607 if ($this->response->status=="ok") $this->response->status = $this->getUserAccount($this->response->action, $email, $password); 608 if ($this->response->status=="ok" && $email!=$emailSource && $this->isUserAccountTaken($email)) $this->response->status = "taken"; 609 if ($this->response->status=="ok" && $email!=$emailSource) { 610 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 611 $settings = array( 612 "name" => $name, 613 "description" => $this->yellow->user->getUser("description", $emailSource), 614 "language" => $language, 615 "access" => $this->yellow->user->getUser("access", $emailSource), 616 "home" => $this->yellow->user->getUser("home", $emailSource), 617 "hash" => $this->response->createHash("none"), 618 "stamp" => $this->response->createStamp(), 619 "pending" => $emailSource, 620 "failed" => "0", 621 "modified" => date("Y-m-d H:i:s", time()), 622 "status" => "unverified"); 623 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "ok" : "error"; 624 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 625 } 626 if ($this->response->status=="ok") { 627 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 628 $settings = array( 629 "name" => $name, 630 "language" => $language, 631 "pending" => $email.":".(is_string_empty($password) ? $this->yellow->user->getUser("hash", $emailSource) : $this->response->createHash($password)), 632 "failed" => "0", 633 "modified" => date("Y-m-d H:i:s", time())); 634 $this->response->status = $this->yellow->user->save($fileNameUser, $emailSource, $settings) ? "ok" : "error"; 635 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 636 } 637 if ($this->response->status=="ok") { 638 $action = $email!=$emailSource ? "verify" : "change"; 639 $this->response->status = $this->response->sendMail($scheme, $address, $base, $email, $action) ? "next" : "error"; 640 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't send email message!"); 641 } 642 } else { 643 if ($this->response->status=="ok") { 644 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 645 $settings = array("name" => $name, "language" => $language, "failed" => "0", "modified" => date("Y-m-d H:i:s", time())); 646 $this->response->status = $this->yellow->user->save($fileNameUser, $email, $settings) ? "done" : "error"; 647 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 648 } 649 } 650 if ($this->response->status=="done") { 651 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); 652 $statusCode = $this->yellow->sendStatus(303, $location); 653 } else { 654 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 655 } 656 return $statusCode; 657 } 658 659 // Process request to change settings 660 public function processRequestConfigure($scheme, $address, $base, $location, $fileName) { 661 $statusCode = 0; 662 if ($this->response->isUserAccess("configure")) { 663 $this->response->action = "configure"; 664 $this->response->status = "ok"; 665 $sitename = trim($this->yellow->page->getRequest("sitename")); 666 $author = trim($this->yellow->page->getRequest("author")); 667 $email = trim($this->yellow->page->getRequest("email")); 668 if ($email!=$this->yellow->system->get("email")) { 669 if (is_string_empty($email) || !filter_var($email, FILTER_VALIDATE_EMAIL)) $this->response->status = "invalid"; 670 } 671 if ($this->response->status=="ok") { 672 $fileNameSystem = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreSystemFile"); 673 $settings = array("sitename" => $sitename, "author" => $author, "email" => $email); 674 $file = $this->response->getFileSystem($scheme, $address, $base, $location, $fileNameSystem, $settings); 675 $this->response->status = (!$file->isError() && $this->yellow->system->save($fileNameSystem, $settings)) ? "done" : "error"; 676 if ($this->response->status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameSystem'!"); 677 } 678 if ($this->response->status=="done") { 679 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); 680 $statusCode = $this->yellow->sendStatus(303, $location); 681 } else { 682 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 683 } 684 } 685 return $statusCode; 686 } 687 688 // Process request to update website 689 public function processRequestUpdate($scheme, $address, $base, $location, $fileName) { 690 $statusCode = 0; 691 if ($this->response->isUserAccess("update")) { 692 $this->response->action = "update"; 693 $this->response->status = "ok"; 694 if ($this->yellow->page->getRequest("option")=="check") { 695 list($statusCode, $rawData) = $this->response->getUpdateInformation(); 696 $this->response->status = is_string_empty($rawData) ? "ok" : "updates"; 697 $this->response->rawDataOutput = $rawData; 698 if ($statusCode!=200) { 699 $this->response->status = "error"; 700 $this->response->rawDataOutput = ""; 701 } 702 } else { 703 $this->response->status = $this->yellow->command("update all")==0 ? "done" : "error"; 704 } 705 if ($this->response->status=="done") { 706 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); 707 $statusCode = $this->yellow->sendStatus(303, $location); 708 } else { 709 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 710 } 711 } 712 return $statusCode; 713 } 714 715 // Process request to create page 716 public function processRequestCreate($scheme, $address, $base, $location, $fileName) { 717 $statusCode = 0; 718 if ($this->response->isUserAccess("create", $location) && !is_string_empty($this->yellow->page->getRequest("rawdataedit"))) { 719 $this->response->rawDataSource = $this->yellow->page->getRequest("rawdatasource"); 720 $this->response->rawDataEdit = $this->yellow->page->getRequest("rawdatasource"); 721 $this->response->rawDataEndOfLine = $this->yellow->page->getRequest("rawdataendofline"); 722 $rawData = $this->yellow->page->getRequest("rawdataedit"); 723 $page = $this->response->getPageNew($scheme, $address, $base, $location, $fileName, 724 $rawData, $this->response->getEndOfLine()); 725 if (!$page->isError()) { 726 if ($this->yellow->toolbox->writeFile($page->fileName, $page->rawData, true)) { 727 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); 728 $statusCode = $this->yellow->sendStatus(303, $location); 729 } else { 730 $this->yellow->page->error(500, "Can't write file '$page->fileName'!"); 731 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 732 } 733 } else { 734 $this->yellow->page->error(500, $page->errorMessage); 735 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 736 } 737 } 738 return $statusCode; 739 } 740 741 // Process request to edit page 742 public function processRequestEdit($scheme, $address, $base, $location, $fileName) { 743 $statusCode = 0; 744 if ($this->response->isUserAccess("edit", $location) && !is_string_empty($this->yellow->page->getRequest("rawdataedit"))) { 745 $this->response->rawDataSource = $this->yellow->page->getRequest("rawdatasource"); 746 $this->response->rawDataEdit = $this->yellow->page->getRequest("rawdataedit"); 747 $this->response->rawDataEndOfLine = $this->yellow->page->getRequest("rawdataendofline"); 748 $rawDataFile = $this->yellow->toolbox->readFile($fileName); 749 $page = $this->response->getPageEdit($scheme, $address, $base, $location, $fileName, 750 $this->response->rawDataSource, $this->response->rawDataEdit, $rawDataFile, $this->response->rawDataEndOfLine); 751 if (!$page->isError()) { 752 if ($this->yellow->lookup->isFileLocation($location)) { 753 $ok = $this->yellow->toolbox->renameFile($fileName, $page->fileName, true) && 754 $this->yellow->toolbox->writeFile($page->fileName, $page->rawData); 755 } else { 756 $ok = $this->yellow->toolbox->renameDirectory(dirname($fileName), dirname($page->fileName), true) && 757 $this->yellow->toolbox->writeFile($page->fileName, $page->rawData); 758 } 759 if ($ok) { 760 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $page->location); 761 $statusCode = $this->yellow->sendStatus(303, $location); 762 } else { 763 $this->yellow->page->error(500, "Can't write file '$page->fileName'!"); 764 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 765 } 766 } else { 767 $this->yellow->page->error(500, $page->errorMessage); 768 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 769 } 770 } 771 return $statusCode; 772 } 773 774 // Process request to delete page 775 public function processRequestDelete($scheme, $address, $base, $location, $fileName) { 776 $statusCode = 0; 777 if ($this->response->isUserAccess("delete", $location) && is_file($fileName)) { 778 $this->response->rawDataSource = $this->yellow->page->getRequest("rawdatasource"); 779 $this->response->rawDataEdit = $this->yellow->page->getRequest("rawdatasource"); 780 $this->response->rawDataEndOfLine = $this->yellow->page->getRequest("rawdataendofline"); 781 $rawDataFile = $this->yellow->toolbox->readFile($fileName); 782 $page = $this->response->getPageDelete($scheme, $address, $base, $location, $fileName, 783 $rawDataFile, $this->response->rawDataEndOfLine); 784 if (!$page->isError()) { 785 if ($this->yellow->lookup->isFileLocation($location)) { 786 $ok = $this->response->deleteFileLocation($location, $fileName); 787 } else { 788 $ok = $this->response->deleteDirectoryLocation($location, $fileName); 789 } 790 if ($ok) { 791 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); 792 $statusCode = $this->yellow->sendStatus(303, $location); 793 } else { 794 $this->yellow->page->error(500, "Can't delete file '$fileName'!"); 795 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 796 } 797 } else { 798 $this->yellow->page->error(500, $page->errorMessage); 799 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 800 } 801 } 802 return $statusCode; 803 } 804 805 // Process request to restore deleted page 806 public function processRequestRestore($scheme, $address, $base, $location, $fileName) { 807 $statusCode = 0; 808 if ($this->response->isUserAccess("restore", $location) && !is_file($fileName)) { 809 $page = $this->response->getPageRestore($scheme, $address, $base, $location, $fileName); 810 if (!$page->isError()) { 811 if ($this->yellow->lookup->isFileLocation($location)) { 812 $ok = $this->response->restoreFileLocation($location); 813 } else { 814 $ok = $this->response->restoreDirectoryLocation($location); 815 } 816 if ($ok) { 817 $location = $this->yellow->lookup->normaliseUrl($scheme, $address, $base, $location); 818 $statusCode = $this->yellow->sendStatus(303, $location); 819 } else { 820 $this->yellow->page->error(500, "Can't restore file '$fileName'!"); 821 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 822 } 823 } else { 824 $this->yellow->page->error(500, $page->errorMessage); 825 $statusCode = $this->yellow->processRequest($scheme, $address, $base, $location, $fileName, false); 826 } 827 } 828 return $statusCode; 829 } 830 831 // Process request to show preview 832 public function processRequestPreview($scheme, $address, $base, $location, $fileName) { 833 $page = $this->response->getPagePreview($scheme, $address, $base, $location, $fileName, 834 $this->yellow->page->getRequest("rawdataedit"), $this->yellow->page->getRequest("rawdataendofline")); 835 $page->headerData = array( 836 "Cache-Control"=>"no-cache, no-store", 837 "Content-Type"=>$this->yellow->toolbox->getMimeContentType("a.html"), 838 "Last-Modified"=>$this->yellow->toolbox->getHttpDateFormatted(time())); 839 $statusCode = $this->yellow->sendData($page->statusCode, $page->headerData, $page->outputData); 840 if ($this->yellow->system->get("coreDebugMode")>=1) echo "YellowEdit::processRequestPreview file:$fileName<br />\n"; 841 return $statusCode; 842 } 843 844 // Process request to upload file 845 public function processRequestUpload($scheme, $address, $base, $location, $fileName) { 846 $data = array(); 847 $fileNameTemp = $_FILES["file"]["tmp_name"]; 848 $fileNameShort = preg_replace("/[^\pL\d\-\.]/u", "-", basename($_FILES["file"]["name"])); 849 $fileSizeMax = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize")); 850 $extension = strtoloweru(($pos = strrposu($fileNameShort, ".")) ? substru($fileNameShort, $pos) : ""); 851 $extensions = preg_split("/\s*,\s*/", $this->yellow->system->get("editUploadExtensions")); 852 if ($this->response->isUserAccess("upload", $location) && is_uploaded_file($fileNameTemp) && 853 filesize($fileNameTemp)<=$fileSizeMax && in_array($extension, $extensions)) { 854 $file = $this->response->getFileUpload($scheme, $address, $base, $location, $fileNameTemp, $fileNameShort); 855 if (!$file->isError() && $this->yellow->toolbox->copyFile($fileNameTemp, $file->fileName, true)) { 856 $data["location"] = $file->getLocation(); 857 } else { 858 $data["error"] = "Can't write file '$file->fileName'!"; 859 } 860 } else { 861 $data["error"] = "Can't write file '$fileNameShort'!"; 862 } 863 $headerData = array( 864 "Cache-Control"=>"no-cache, no-store", 865 "Content-Type"=>$this->yellow->toolbox->getMimeContentType("a.json"), 866 "Last-Modified"=>$this->yellow->toolbox->getHttpDateFormatted(time())); 867 return $this->yellow->sendData(isset($data["error"]) ? 500 : 200, $headerData, json_encode($data)); 868 } 869 870 // Check user authentication 871 public function checkUserAuth($scheme, $address, $base, $location, $fileName) { 872 $action = $this->yellow->page->getRequest("action"); 873 $authToken = $this->yellow->toolbox->getCookie("yellowauthtoken"); 874 $csrfToken = $this->yellow->toolbox->getCookie("yellowcsrftoken"); 875 if (is_string_empty($action) || $this->isRequestSameSite("POST", $scheme, $address)) { 876 if ($action=="login") { 877 $email = $this->yellow->page->getRequest("email"); 878 $password = $this->yellow->page->getRequest("password"); 879 if ($this->response->checkAuthLogin($email, $password)) { 880 $this->response->createCookies($scheme, $address, $base, $email); 881 $this->response->userEmail = $email; 882 $this->response->language = $this->getUserLanguage($email); 883 } else { 884 $this->response->userFailedError = "login"; 885 $this->response->userFailedEmail = $email; 886 $this->response->userFailedExpire = PHP_INT_MAX; 887 } 888 } elseif (!is_string_empty($authToken) && !is_string_empty($csrfToken)) { 889 $csrfTokenReceived = isset($_POST["yellowcsrftoken"]) ? $_POST["yellowcsrftoken"] : ""; 890 $csrfTokenIrrelevant = is_string_empty($action); 891 if ($this->response->checkAuthToken($authToken, $csrfToken, $csrfTokenReceived, $csrfTokenIrrelevant)) { 892 $this->response->userEmail = $email = $this->response->getAuthEmail($authToken); 893 $this->response->language = $this->getUserLanguage($email); 894 } else { 895 $this->response->userFailedError = "auth"; 896 $this->response->userFailedEmail = $this->response->getAuthEmail($authToken); 897 $this->response->userFailedExpire = $this->response->getAuthExpire($authToken); 898 } 899 } 900 $this->yellow->user->set($this->response->userEmail); 901 } 902 return $this->response->isUser(); 903 } 904 905 // Check user without authentication 906 public function checkUserUnauth($scheme, $address, $base, $location, $fileName) { 907 $ok = false; 908 $action = $this->yellow->page->getRequest("action"); 909 if (is_string_empty($action) || $action=="signup" || $action=="forgot") { 910 $ok = true; 911 } elseif ($this->yellow->page->isRequest("actiontoken")) { 912 $actionToken = $this->yellow->page->getRequest("actiontoken"); 913 $email = $this->yellow->page->getRequest("email"); 914 $action = $this->yellow->page->getRequest("action"); 915 $expire = $this->yellow->page->getRequest("expire"); 916 $language = $this->yellow->page->getRequest("language"); 917 if ($this->response->checkActionToken($actionToken, $email, $action, $expire)) { 918 $ok = true; 919 $this->response->language = $this->getActionLanguage($language); 920 } else { 921 $this->response->userFailedError = "action"; 922 $this->response->userFailedEmail = $email; 923 $this->response->userFailedExpire = $expire; 924 } 925 } 926 return $ok; 927 } 928 929 // Check user failed 930 public function checkUserFailed($scheme, $address, $base, $location, $fileName) { 931 if (!is_string_empty($this->response->userFailedError)) { 932 if ($this->response->userFailedExpire>time() && $this->yellow->user->isExisting($this->response->userFailedEmail)) { 933 $email = $this->response->userFailedEmail; 934 $failed = $this->yellow->user->getUser("failed", $email)+1; 935 $fileNameUser = $this->yellow->system->get("coreExtensionDirectory").$this->yellow->system->get("coreUserFile"); 936 $status = $this->yellow->user->save($fileNameUser, $email, array("failed" => $failed)) ? "ok" : "error"; 937 if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 938 if ($failed==$this->yellow->system->get("editBruteForceProtection")) { 939 $statusBeforeProtection = $this->yellow->user->getUser("status", $email); 940 $statusAfterProtection = ($statusBeforeProtection=="active" || $statusBeforeProtection=="inactive") ? "inactive" : "failed"; 941 if ($status=="ok") { 942 $status = $this->yellow->user->save($fileNameUser, $email, array("status" => $statusAfterProtection)) ? "ok" : "error"; 943 if ($status=="error") $this->yellow->page->error(500, "Can't write file '$fileNameUser'!"); 944 } 945 if ($status=="ok" && $statusBeforeProtection=="active") { 946 $status = $this->response->sendMail($scheme, $address, $base, $email, "reactivate") ? "done" : "error"; 947 if ($status=="error") $this->yellow->page->error(500, "Can't send email message!"); 948 } 949 } 950 } 951 if ($this->response->userFailedError=="login" || $this->response->userFailedError=="auth") { 952 $this->response->destroyCookies($scheme, $address, $base); 953 $this->response->status = "error"; 954 $this->yellow->page->error(430); 955 } else { 956 $this->response->status = "error"; 957 $this->yellow->page->error(500, "Link has expired!"); 958 } 959 } 960 } 961 962 // Return user status changes 963 public function getUserStatus($email, $action) { 964 switch ($action) { 965 case "confirm": $statusExpected = "unconfirmed"; break; 966 case "approve": $statusExpected = "unapproved"; break; 967 case "recover": $statusExpected = "active"; break; 968 case "reactivate": $statusExpected = "inactive"; break; 969 case "verify": $statusExpected = "unverified"; break; 970 case "change": $statusExpected = "active"; break; 971 case "remove": $statusExpected = "active"; break; 972 } 973 return $this->yellow->user->getUser("status", $email)==$statusExpected ? "ok" : "done"; 974 } 975 976 // Return user account changes 977 public function getUserAccount($action, $email, $password) { 978 $status = null; 979 foreach ($this->yellow->extension->data as $key=>$value) { 980 if (method_exists($value["object"], "onEditUserAccount")) { 981 $status = $value["object"]->onEditUserAccount($action, $email, $password); 982 if (!is_null($status)) break; 983 } 984 } 985 if (is_null($status)) { 986 $status = "ok"; 987 if (!is_string_empty($password) && strlenu($password)<$this->yellow->system->get("editUserPasswordMinLength")) $status = "short"; 988 if (!is_string_empty($password) && $password==$email) $status = "weak"; 989 if (!is_string_empty($email) && !filter_var($email, FILTER_VALIDATE_EMAIL)) $status = "invalid"; 990 } 991 return $status; 992 } 993 994 // Return user language 995 public function getUserLanguage($email) { 996 $language = $this->yellow->user->getUser("language", $email); 997 if (!$this->yellow->language->isExisting($language)) $language = $this->yellow->system->get("language"); 998 return $language; 999 } 1000 1001 // Return action language 1002 public function getActionLanguage($language) { 1003 if (!$this->yellow->language->isExisting($language)) $language = $this->yellow->system->get("language"); 1004 return $language; 1005 } 1006 1007 // Check if user account is taken 1008 public function isUserAccountTaken($email) { 1009 $taken = false; 1010 if ($this->yellow->user->isExisting($email)) { 1011 $status = $this->yellow->user->getUser("status", $email); 1012 $reserved = strtotime($this->yellow->user->getUser("modified", $email)) + 60*60*24; 1013 if ($status=="active" || $status=="inactive" || $reserved>time()) $taken = true; 1014 } 1015 return $taken; 1016 } 1017 1018 // Check if request came from same site 1019 public function isRequestSameSite($method, $scheme, $address) { 1020 $origin = ""; 1021 if (preg_match("#^(\w+)://([^/]+)(.*)$#", $this->yellow->toolbox->getServer("HTTP_REFERER"), $matches)) $origin = "$matches[1]://$matches[2]"; 1022 if ($this->yellow->toolbox->getServer("HTTP_ORIGIN")) $origin = $this->yellow->toolbox->getServer("HTTP_ORIGIN"); 1023 return $this->yellow->toolbox->getServer("REQUEST_METHOD")==$method && $origin=="$scheme://$address"; 1024 } 1025 1026 // Check if edit location 1027 public function isEditLocation($location) { 1028 $locationLength = strlenu($this->yellow->system->get("editLocation")); 1029 return substru($location, 0, $locationLength)==$this->yellow->system->get("editLocation"); 1030 } 1031 } 1032 1033 class YellowEditResponse { 1034 public $yellow; // access to API 1035 public $extension; // access to extension 1036 public $userEmail; // user email 1037 public $userFailedError; // error of failed authentication 1038 public $userFailedEmail; // email of failed authentication 1039 public $userFailedExpire; // expiration time of failed authentication 1040 public $rawDataSource; // raw data of page for comparison 1041 public $rawDataEdit; // raw data of page for editing 1042 public $rawDataOutput; // raw data of dynamic output 1043 public $rawDataReadonly; // raw data is read only? (boolean) 1044 public $rawDataEndOfLine; // end of line format for raw data 1045 public $language; // response language 1046 public $action; // response action 1047 public $status; // response status 1048 1049 public function __construct($yellow) { 1050 $this->yellow = $yellow; 1051 $this->extension = $yellow->extension->get("edit"); 1052 $this->userEmail = ""; 1053 } 1054 1055 // Process page data 1056 public function processPageData($page) { 1057 if ($this->isUser()) { 1058 if (is_string_empty($this->rawDataSource)) $this->rawDataSource = $page->rawData; 1059 if (is_string_empty($this->rawDataEdit)) $this->rawDataEdit = $page->rawData; 1060 if (is_string_empty($this->rawDataEndOfLine)) $this->rawDataEndOfLine = $this->getEndOfLine($page->rawData); 1061 if ($page->statusCode==404 || $this->yellow->toolbox->isLocationArguments()) { 1062 $this->rawDataEdit = $this->getRawDataGenerated($page); 1063 $this->rawDataReadonly = true; 1064 } 1065 if ($page->statusCode==434 || $page->statusCode==435) { 1066 $this->rawDataEdit = $this->getRawDataNew($page, true); 1067 $this->rawDataReadonly = false; 1068 } 1069 } 1070 if (is_string_empty($this->language)) $this->language = $page->get("language"); 1071 if (is_string_empty($this->action)) $this->action = $this->isUser() ? "none" : "login"; 1072 if (is_string_empty($this->status)) $this->status = "none"; 1073 if ($this->status=="error") $this->action = "error"; 1074 } 1075 1076 // Return new page 1077 public function getPageNew($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) { 1078 $rawData = $this->yellow->lookup->normaliseLines($rawData, $endOfLine); 1079 $page = new YellowPage($this->yellow); 1080 $page->setRequestInformation($scheme, $address, $base, $location, $fileName, false); 1081 $page->parseMeta($rawData, 200); 1082 $this->editContentFile($page, "precreate", $this->userEmail); 1083 if ($this->yellow->content->find($page->location)) { 1084 $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("editNewLocation")); 1085 $page->fileName = $this->getPageNewFile($page->location, $page->fileName, $page->get("editNewPrefix")); 1086 $pageCounter = 0; 1087 while ($this->yellow->content->find($page->location) || is_string_empty($page->fileName)) { 1088 $page->rawData = $this->yellow->toolbox->setMetaData($page->rawData, "title", $this->getTitleNext($page->rawData)); 1089 $page->rawData = $this->yellow->lookup->normaliseLines($page->rawData, $endOfLine); 1090 $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("editNewLocation")); 1091 $page->fileName = $this->getPageNewFile($page->location, $page->fileName, $page->get("editNewPrefix")); 1092 if (++$pageCounter>999) break; 1093 } 1094 if ($this->yellow->content->find($page->location) || is_string_empty($page->fileName)) { 1095 $page->error(500, "Page '".$page->get("title")."' is not possible!"); 1096 } 1097 } else { 1098 $page->fileName = $this->getPageNewFile($page->location); 1099 } 1100 if (!$this->isUserAccess("create", $page->location)) { 1101 $page->error(500, "Page '".$page->get("title")."' is restricted!"); 1102 } 1103 $this->editContentFile($page, "create", $this->userEmail); 1104 return $page; 1105 } 1106 1107 // Return modified page 1108 public function getPageEdit($scheme, $address, $base, $location, $fileName, $rawDataSource, $rawDataEdit, $rawDataFile, $endOfLine) { 1109 $rawDataSource = $this->yellow->lookup->normaliseLines($rawDataSource, $endOfLine); 1110 $rawDataEdit = $this->yellow->lookup->normaliseLines($rawDataEdit, $endOfLine); 1111 $rawDataFile = $this->yellow->lookup->normaliseLines($rawDataFile, $endOfLine); 1112 $rawData = $this->extension->merge->merge($rawDataSource, $rawDataEdit, $rawDataFile); 1113 $page = new YellowPage($this->yellow); 1114 $page->setRequestInformation($scheme, $address, $base, $location, $fileName, false); 1115 $page->parseMeta($rawData, 200); 1116 $this->editContentFile($page, "preedit", $this->userEmail); 1117 $pageSource = new YellowPage($this->yellow); 1118 $pageSource->setRequestInformation($scheme, $address, $base, $location, $fileName, false); 1119 $pageSource->parseMeta($rawDataSource, 200); 1120 if ($this->isMetaModified($pageSource, $page) && $page->location!=$this->yellow->content->getHomeLocation($page->location)) { 1121 $page->location = $this->getPageNewLocation($page->rawData, $page->location, $page->get("editNewLocation"), true); 1122 $page->fileName = $this->getPageNewFile($page->location, $page->fileName, $page->get("editNewPrefix")); 1123 if ($page->location!=$pageSource->location && ($this->yellow->content->find($page->location) || is_string_empty($page->fileName))) { 1124 $page->error(500, "Page '".$page->get("title")."' is not possible!"); 1125 } 1126 } 1127 if (is_string_empty($page->rawData)) $page->error(500, "Page has been modified by someone else!"); 1128 if (!$this->isUserAccess("edit", $page->location) || 1129 !$this->isUserAccess("edit", $pageSource->location)) { 1130 $page->error(500, "Page '".$page->get("title")."' is restricted!"); 1131 } 1132 $this->editContentFile($page, "edit", $this->userEmail); 1133 return $page; 1134 } 1135 1136 // Return deleted page 1137 public function getPageDelete($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) { 1138 $rawData = $this->yellow->lookup->normaliseLines($rawData, $endOfLine); 1139 $page = new YellowPage($this->yellow); 1140 $page->setRequestInformation($scheme, $address, $base, $location, $fileName, false); 1141 $page->parseMeta($rawData, 200); 1142 if (!$this->isUserAccess("delete", $page->location)) { 1143 $page->error(500, "Page '".$page->get("title")."' is restricted!"); 1144 } 1145 $this->editContentFile($page, "delete", $this->userEmail); 1146 return $page; 1147 } 1148 1149 // Return restored page 1150 public function getPageRestore($scheme, $address, $base, $location, $fileName) { 1151 $page = new YellowPage($this->yellow); 1152 $page->setRequestInformation($scheme, $address, $base, $location, $fileName, false); 1153 $page->parseMeta(""); 1154 if (!$this->isUserAccess("restore", $page->location)) { 1155 $page->error(500, "Page '".$page->get("title")."' is restricted!"); 1156 } 1157 $this->editContentFile($page, "restore", $this->userEmail); 1158 return $page; 1159 } 1160 1161 // Return preview page 1162 public function getPagePreview($scheme, $address, $base, $location, $fileName, $rawData, $endOfLine) { 1163 $rawData = $this->yellow->lookup->normaliseLines($rawData, $endOfLine); 1164 $page = new YellowPage($this->yellow); 1165 $page->setRequestInformation($scheme, $address, $base, $location, $fileName, false); 1166 $page->parseMeta($rawData, 200); 1167 $this->yellow->language->set($page->get("language")); 1168 $class = "page-preview layout-".$page->get("layout"); 1169 $output = "<div class=\"".htmlspecialchars($class)."\"><div class=\"content\"><div class=\"main\">"; 1170 if ($this->yellow->system->get("editToolbarButtons")!="none") $output .= "<h1>".$page->getHtml("titleContent")."</h1>\n"; 1171 $output .= $page->getContentHtml(); 1172 $output .= "</div></div></div>"; 1173 $page->statusCode = 200; 1174 $page->outputData = $output; 1175 return $page; 1176 } 1177 1178 // Return uploaded file 1179 public function getFileUpload($scheme, $address, $base, $pageLocation, $fileNameTemp, $fileNameShort) { 1180 $file = new YellowPage($this->yellow); 1181 $file->setRequestInformation($scheme, $address, $base, "/".$fileNameShort, $fileNameShort, false); 1182 $file->parseMeta(null); 1183 $file->set("fileNameTemp", $fileNameTemp); 1184 $file->set("fileNameShort", $fileNameShort); 1185 if ($file->get("type")=="html" || $file->get("type")=="svg") { 1186 $fileData = $this->yellow->toolbox->readFile($fileNameTemp); 1187 $fileData = $this->yellow->lookup->normaliseData($fileData, $file->get("type")); 1188 if (is_string_empty($fileData) || !$this->yellow->toolbox->writeFile($fileNameTemp, $fileData)) { 1189 $file->error(500, "Can't write file '$fileNameTemp'!"); 1190 } 1191 } 1192 $this->editMediaFile($file, "upload", $this->userEmail); 1193 $fileNameShort = basename($file->fileName); 1194 $file->location = $this->getFileNewLocation($fileNameShort, $pageLocation, $file->get("fileNewLocation")); 1195 $file->fileName = substru($file->location, 1); 1196 $fileCounter = 0; 1197 while (is_file($file->fileName)) { 1198 $fileNameShort = $this->getFileNext(basename($file->fileName)); 1199 $file->location = $this->getFileNewLocation($fileNameShort, $pageLocation, $file->get("fileNewLocation")); 1200 $file->fileName = substru($file->location, 1); 1201 if (++$fileCounter>999) break; 1202 } 1203 if (is_file($file->fileName)) $file->error(500, "File '".$file->get("fileNameShort")."' is not possible!"); 1204 return $file; 1205 } 1206 1207 // Return system file 1208 public function getFileSystem($scheme, $address, $base, $pageLocation, $fileNameSystem, $settings) { 1209 $file = new YellowPage($this->yellow); 1210 $file->setRequestInformation($scheme, $address, $base, "/".$fileNameSystem, $fileNameSystem, false); 1211 $file->parseMeta(null); 1212 foreach ($settings as $key=>$value) $file->set($key, $value); 1213 $this->editSystemFile($file, "configure", $this->userEmail); 1214 return $file; 1215 } 1216 1217 // Return page data including status information 1218 public function getPageData($page) { 1219 $data = array(); 1220 $data["scheme"] = $this->yellow->page->scheme; 1221 $data["address"] = $this->yellow->page->address; 1222 $data["base"] = $this->yellow->page->base; 1223 $data["location"] = $this->yellow->page->location; 1224 if ($this->isUser()) { 1225 $data["title"] = $this->yellow->toolbox->getMetaData($this->rawDataEdit, "title"); 1226 $data["rawDataSource"] = $this->rawDataSource; 1227 $data["rawDataEdit"] = $this->rawDataEdit; 1228 $data["rawDataNew"] = $this->getRawDataNew($page); 1229 $data["rawDataOutput"] = strval($this->rawDataOutput); 1230 $data["rawDataReadonly"] = intval($this->rawDataReadonly); 1231 $data["rawDataEndOfLine"] = $this->rawDataEndOfLine; 1232 } 1233 if ($this->action!="none") $data = array_merge($data, $this->getRequestData()); 1234 $data["action"] = $this->action; 1235 $data["status"] = $this->status; 1236 $data["statusCode"] = $this->yellow->page->statusCode; 1237 return $data; 1238 } 1239 1240 // Return system data 1241 public function getSystemData() { 1242 $data = array(); 1243 $data["coreServerScheme"] = $this->yellow->system->get("coreServerScheme"); 1244 $data["coreServerAddress"] = $this->yellow->system->get("coreServerAddress"); 1245 $data["coreServerBase"] = $this->yellow->system->get("coreServerBase"); 1246 $data["coreDebugMode"] = $this->yellow->system->get("coreDebugMode"); 1247 $data = array_merge($data, $this->yellow->system->getSettings("", "Location")); 1248 if ($this->isUser()) { 1249 $data["coreFileSizeMax"] = $this->yellow->toolbox->getNumberBytes(ini_get("upload_max_filesize")); 1250 $data["coreProductRelease"] = "Datenstrom Yellow ".YellowCore::RELEASE; 1251 $data["coreExtensions"] = array(); 1252 foreach ($this->yellow->extension->data as $key=>$value) { 1253 $data["coreExtensions"][$key] = $value["class"]; 1254 } 1255 $data["coreLanguages"] = array(); 1256 foreach ($this->yellow->toolbox->enumerate("language") as $language) { 1257 $data["coreLanguages"][$language] = $this->yellow->language->getTextHtml("languageDescription", $language); 1258 } 1259 $data["editSettingsActions"] = $this->getSettingsActions(); 1260 $data["editUploadExtensions"] = $this->yellow->system->get("editUploadExtensions"); 1261 $data["editKeyboardShortcuts"] = $this->yellow->system->get("editKeyboardShortcuts"); 1262 $data["editToolbarButtons"] = $this->getToolbarButtons(); 1263 $data["editStatusValues"] = $this->getStatusValues(); 1264 $data["emojiToolbarButtons"] = $this->yellow->system->get("emojiToolbarButtons"); 1265 $data["iconToolbarButtons"] = $this->yellow->system->get("iconToolbarButtons"); 1266 if ($this->isUserAccess("configure")) { 1267 $data["sitename"] = $this->yellow->system->get("sitename"); 1268 $data["author"] = $this->yellow->system->get("author"); 1269 $data["email"] = $this->yellow->system->get("email"); 1270 } 1271 } else { 1272 $data["editLoginEmail"] = $this->yellow->page->get("editLoginEmail"); 1273 $data["editLoginPassword"] = $this->yellow->page->get("editLoginPassword"); 1274 $data["editLoginRestriction"] = intval($this->isLoginRestriction()); 1275 } 1276 return $data; 1277 } 1278 1279 // Return user data 1280 public function getUserData() { 1281 $data = array(); 1282 if ($this->isUser()) { 1283 $data["email"] = $this->userEmail; 1284 $data["name"] = $this->yellow->user->getUser("name", $this->userEmail); 1285 $data["description"] = $this->yellow->user->getUser("description", $this->userEmail); 1286 $data["language"] = $this->yellow->user->getUser("language", $this->userEmail); 1287 $data["status"] = $this->yellow->user->getUser("status", $this->userEmail); 1288 $data["access"] = $this->yellow->user->getUser("access", $this->userEmail); 1289 $data["home"] = $this->yellow->user->getUser("home", $this->userEmail); 1290 } 1291 return $data; 1292 } 1293 1294 // Return language data 1295 public function getLanguageData() { 1296 $dataLanguage = $this->yellow->language->getSettings("language", "", $this->language); 1297 $dataEdit = $this->yellow->language->getSettings("edit", "", $this->language); 1298 return array_merge($dataLanguage, $dataEdit); 1299 } 1300 1301 // Return request data 1302 public function getRequestData() { 1303 $data = array(); 1304 foreach ($_REQUEST as $key=>$value) { 1305 if ($key=="password" || $key=="yellowauthtoken" || $key=="yellowcsrftoken" || 1306 substru($key, 0, 7)=="rawdata") { 1307 continue; 1308 } 1309 $data["request".ucfirst($key)] = trim($value); 1310 } 1311 return $data; 1312 } 1313 1314 // Return settings actions 1315 public function getSettingsActions() { 1316 $settingsActions = "account"; 1317 if ($this->isUserAccess("configure")) $settingsActions .= ", configure"; 1318 if ($this->isUserAccess("update")) $settingsActions .= ", update"; 1319 return $settingsActions=="account" ? "none" : $settingsActions; 1320 } 1321 1322 // Return toolbar buttons 1323 public function getToolbarButtons() { 1324 $toolbarButtons = $this->yellow->system->get("editToolbarButtons"); 1325 if ($toolbarButtons=="auto") { 1326 $toolbarButtons = "format, bold, italic, strikethrough, code, separator, list, link, file"; 1327 if ($this->yellow->extension->isExisting("emoji")) $toolbarButtons .= ", emoji"; 1328 if ($this->yellow->extension->isExisting("icon")) $toolbarButtons .= ", icon"; 1329 $toolbarButtons .= ", status, preview"; 1330 } 1331 return $toolbarButtons; 1332 } 1333 1334 // Return status values 1335 public function getStatusValues() { 1336 $statusValues = ""; 1337 if ($this->yellow->extension->isExisting("private")) $statusValues .= ", private"; 1338 if ($this->yellow->extension->isExisting("draft")) $statusValues .= ", draft"; 1339 $statusValues .= ", unlisted"; 1340 return ltrim($statusValues, ", "); 1341 } 1342 1343 // Return end of line format 1344 public function getEndOfLine($rawData = "") { 1345 $endOfLine = $this->yellow->system->get("editEndOfLine"); 1346 if ($endOfLine=="auto") { 1347 $rawData = is_string_empty($rawData) ? PHP_EOL : substru($rawData, 0, 4096); 1348 $endOfLine = strposu($rawData, "\r")===false ? "lf" : "crlf"; 1349 } 1350 return $endOfLine; 1351 } 1352 1353 // Return update information 1354 public function getUpdateInformation() { 1355 $statusCode = 200; 1356 $rawData = ""; 1357 if ($this->yellow->extension->isExisting("update")) { 1358 list($statusCodeCurrent, $settingsCurrent) = $this->yellow->extension->get("update")->getExtensionSettings(true); 1359 list($statusCodeLatest, $settingsLatest) = $this->yellow->extension->get("update")->getExtensionSettings(false); 1360 $statusCode = max($statusCodeCurrent, $statusCodeLatest); 1361 foreach ($settingsCurrent as $key=>$value) { 1362 if ($settingsLatest->isExisting($key)) { 1363 $versionCurrent = $settingsCurrent[$key]->get("version"); 1364 $versionLatest = $settingsLatest[$key]->get("version"); 1365 if (strnatcasecmp($versionCurrent, $versionLatest)<0) { 1366 $rawData .= htmlspecialchars(ucfirst($key)." $versionLatest")."<br />"; 1367 } 1368 } 1369 } 1370 if (!is_string_empty($rawData)) $rawData = "<p>$rawData</p>\n"; 1371 } 1372 return array($statusCode, $rawData); 1373 } 1374 1375 // Return raw data for generated page 1376 public function getRawDataGenerated($page) { 1377 $title = $page->get("title"); 1378 $text = $this->yellow->language->getText("editDataGenerated", $page->get("language")); 1379 return "---\nTitle: $title\n---\n$text"; 1380 } 1381 1382 // Return raw data for new page 1383 public function getRawDataNew($page, $customTitle = false) { 1384 $fileName = ""; 1385 foreach ($this->yellow->content->path($page->location)->reverse() as $ancestor) { 1386 if ($ancestor->isExisting("layoutNew")) { 1387 $name = $this->yellow->lookup->normaliseName($ancestor->get("layoutNew")); 1388 $location = $this->yellow->content->getHomeLocation($page->location)."shared/"; 1389 $fileName = $this->yellow->lookup->findFileFromContentLocation($location, true).$this->yellow->system->get("editNewFile"); 1390 $fileName = str_replace("(.*)", $name, $fileName); 1391 if (is_file($fileName)) break; 1392 } 1393 } 1394 if (!is_file($fileName)) { 1395 $name = $this->yellow->lookup->normaliseName($this->yellow->system->get("layout")); 1396 $location = $this->yellow->content->getHomeLocation($page->location)."shared/"; 1397 $fileName = $this->yellow->lookup->findFileFromContentLocation($location, true).$this->yellow->system->get("editNewFile"); 1398 $fileName = str_replace("(.*)", $name, $fileName); 1399 } 1400 if (is_file($fileName)) { 1401 $rawData = $this->yellow->toolbox->readFile($fileName); 1402 $rawData = preg_replace("/@timestamp/i", time(), $rawData); 1403 $rawData = preg_replace("/@datetime/i", date("Y-m-d H:i:s"), $rawData); 1404 $rawData = preg_replace("/@date/i", date("Y-m-d"), $rawData); 1405 $rawData = preg_replace("/@usershort/i", strtok($this->yellow->user->getUser("name", $this->userEmail), " "), $rawData); 1406 $rawData = preg_replace("/@username/i", $this->yellow->user->getUser("name", $this->userEmail), $rawData); 1407 $rawData = preg_replace("/@userlanguage/i", $this->yellow->user->getUser("language", $this->userEmail), $rawData); 1408 } else { 1409 $rawData = "---\nTitle: Page\n---\n"; 1410 } 1411 if ($customTitle) { 1412 $title = $this->yellow->toolbox->createTextTitle($page->location); 1413 $rawData = $this->yellow->toolbox->setMetaData($rawData, "title", $title); 1414 } 1415 return $rawData; 1416 } 1417 1418 // Return location for new/modified page 1419 public function getPageNewLocation($rawData, $pageLocation, $editNewLocation, $pageMatchLocation = false) { 1420 $location = is_string_empty($editNewLocation) ? "@title" : $editNewLocation; 1421 $location = preg_replace("/@title/i", $this->getPageNewTitle($rawData), $location); 1422 $location = preg_replace("/@timestamp/i", $this->getPageNewData($rawData, "published", "U"), $location); 1423 $location = preg_replace("/@date/i", $this->getPageNewData($rawData, "published", "Y-m-d"), $location); 1424 $location = preg_replace("/@year/i", $this->getPageNewData($rawData, "published", "Y"), $location); 1425 $location = preg_replace("/@month/i", $this->getPageNewData($rawData, "published", "m"), $location); 1426 $location = preg_replace("/@day/i", $this->getPageNewData($rawData, "published", "d"), $location); 1427 $location = preg_replace("/@tag/i", $this->getPageNewData($rawData, "tag"), $location); 1428 $location = preg_replace("/@author/i", $this->getPageNewData($rawData, "author"), $location); 1429 if (!preg_match("/^\//", $location)) { 1430 if ($this->yellow->lookup->isFileLocation($pageLocation) || !$pageMatchLocation) { 1431 $location = $this->yellow->lookup->getDirectoryLocation($pageLocation).$location; 1432 } else { 1433 $location = $this->yellow->lookup->getDirectoryLocation(rtrim($pageLocation, "/")).$location; 1434 } 1435 } 1436 if (preg_match("/\d/", $location)) { 1437 $locationNew = ""; 1438 $tokens = explode("/", $location); 1439 for ($i=1; $i<count($tokens); ++$i) { 1440 $locationNew .= "/".$this->yellow->lookup->normaliseToken($tokens[$i]); 1441 } 1442 $location = $locationNew; 1443 } 1444 if ($pageMatchLocation) { 1445 $location = rtrim($location, "/").($this->yellow->lookup->isFileLocation($pageLocation) ? "" : "/"); 1446 } 1447 return $location; 1448 } 1449 1450 // Return title for new/modified page 1451 public function getPageNewTitle($rawData) { 1452 $title = $this->yellow->toolbox->getMetaData($rawData, "title"); 1453 $titleSlug = $this->yellow->toolbox->getMetaData($rawData, "titleSlug"); 1454 $value = is_string_empty($titleSlug) ? $title : $titleSlug; 1455 $value = $this->yellow->lookup->normaliseName($value, false, false, true); 1456 return trim(preg_replace("/-+/", "-", $value), "-"); 1457 } 1458 1459 // Return data for new/modified page 1460 public function getPageNewData($rawData, $key, $dateFormat = "") { 1461 $value = $this->yellow->toolbox->getMetaData($rawData, $key); 1462 if (preg_match("/^(.*?)\,(.*)$/", $value, $matches)) $value = $matches[1]; 1463 if (!is_string_empty($dateFormat)) $value = date($dateFormat, strtotime($value)); 1464 if (is_string_empty($value)) $value = "none"; 1465 $value = $this->yellow->lookup->normaliseName($value, false, false, true); 1466 return trim(preg_replace("/-+/", "-", $value), "-"); 1467 } 1468 1469 // Return file name for new/modified page 1470 public function getPageNewFile($location, $pageFileName = "", $pagePrefix = "") { 1471 $fileName = $this->yellow->lookup->findFileFromContentLocation($location); 1472 if (!is_string_empty($fileName)) { 1473 if (!is_dir(dirname($fileName))) { 1474 $path = ""; 1475 $tokens = explode("/", $fileName); 1476 for ($i=0; $i<count($tokens)-1; ++$i) { 1477 if (!is_dir($path.$tokens[$i])) { 1478 if (!preg_match("/^[\d\-\_\.]+(.*)$/", $tokens[$i])) { 1479 $number = 1; 1480 foreach ($this->yellow->toolbox->getDirectoryEntries($path, "/^[\d\-\_\.]+(.*)$/", true, true, false) as $entry) { 1481 if ($number!=1 && $number!=intval($entry)) break; 1482 $number = intval($entry)+1; 1483 } 1484 $tokens[$i] = "$number-".$tokens[$i]; 1485 } 1486 $tokens[$i] = $this->yellow->lookup->normaliseName($tokens[$i], false, false, true); 1487 } 1488 $path .= $tokens[$i]."/"; 1489 } 1490 $fileName = $path.$tokens[$i]; 1491 $pageFileName = is_string_empty($pageFileName) ? $fileName : $pageFileName; 1492 } 1493 $prefix = $this->getPageNewPrefix($location, $pageFileName, $pagePrefix); 1494 if ($this->yellow->lookup->isFileLocation($location)) { 1495 if (preg_match("#^(.*)\/(.+?)$#", $fileName, $matches)) { 1496 $path = $matches[1]; 1497 $text = $this->yellow->lookup->normaliseName($matches[2], true, true); 1498 if (preg_match("/^[\d\-\_\.]*$/", $text)) $prefix = ""; 1499 $fileName = $path."/".$prefix.$text.$this->yellow->system->get("coreContentExtension"); 1500 } 1501 } else { 1502 if (preg_match("#^(.*)\/(.+?)$#", dirname($fileName), $matches)) { 1503 $path = $matches[1]; 1504 $text = $this->yellow->lookup->normaliseName($matches[2], true, false); 1505 if (preg_match("/^[\d\-\_\.]*$/", $text)) $prefix = ""; 1506 $fileName = $path."/".$prefix.$text."/".$this->yellow->system->get("coreContentDefaultFile"); 1507 } 1508 } 1509 } 1510 return $fileName; 1511 } 1512 1513 // Return prefix for new/modified page 1514 public function getPageNewPrefix($location, $pageFileName, $pagePrefix) { 1515 if (is_string_empty($pagePrefix)) { 1516 if ($this->yellow->lookup->isFileLocation($location)) { 1517 if (preg_match("#^(.*)\/(.+?)$#", $pageFileName, $matches)) $pagePrefix = $matches[2]; 1518 } else { 1519 if (preg_match("#^(.*)\/(.+?)$#", dirname($pageFileName), $matches)) $pagePrefix = $matches[2]; 1520 } 1521 } 1522 return $this->yellow->lookup->normalisePrefix($pagePrefix, true); 1523 } 1524 1525 // Return location for new file 1526 public function getFileNewLocation($fileNameShort, $pageLocation, $fileNewLocation) { 1527 $location = is_string_empty($fileNewLocation) ? $this->yellow->system->get("editUploadNewLocation") : $fileNewLocation; 1528 $location = preg_replace("/@timestamp/i", time(), $location); 1529 $location = preg_replace("/@date/i", date("Y-m-d"), $location); 1530 $location = preg_replace("/@type/i", $this->yellow->toolbox->getFileType($fileNameShort), $location); 1531 $location = preg_replace("/@group/i", $this->getFileNewGroup($fileNameShort), $location); 1532 $location = preg_replace("/@folder/i", $this->getFileNewFolder($pageLocation), $location); 1533 $location = preg_replace("/@filename/i", strtoloweru($fileNameShort), $location); 1534 if (!preg_match("/^\//", $location)) { 1535 $location = $this->yellow->system->get("coreMediaLocation").$location; 1536 } 1537 return $location; 1538 } 1539 1540 // Return group for new file 1541 public function getFileNewGroup($fileNameShort) { 1542 $group = "none"; 1543 $fileType = $this->yellow->toolbox->getFileType($fileNameShort); 1544 $locationMedia = $this->yellow->system->get("coreMediaLocation"); 1545 $locationGroup = $this->yellow->system->get("coreDownloadLocation"); 1546 if (preg_match("/(gif|jpeg|jpg|png|svg)$/", $fileType)) { 1547 $locationGroup = $this->yellow->system->get("coreImageLocation"); 1548 } 1549 if (preg_match("#^$locationMedia(.+?)\/#", $locationGroup, $matches)) { 1550 $group = strtoloweru($matches[1]); 1551 } 1552 return $group; 1553 } 1554 1555 // Return folder for new file 1556 public function getFileNewFolder($pageLocation) { 1557 $parentTopLocation = $this->yellow->content->getParentTopLocation($pageLocation); 1558 if ($parentTopLocation==$this->yellow->content->getHomeLocation($pageLocation)) $parentTopLocation .= "home"; 1559 return strtoloweru(trim($parentTopLocation, "/")); 1560 } 1561 1562 // Return next file name 1563 public function getFileNext($fileNameShort) { 1564 $fileText = $fileNumber = $fileExtension = ""; 1565 if (preg_match("/^(.*?)(\d*)(\..*?)?$/", $fileNameShort, $matches)) { 1566 $fileText = $matches[1]; 1567 $fileNumber = is_string_empty($matches[2]) ? "-2" : $matches[2]+1; 1568 $fileExtension = $matches[3]; 1569 } 1570 return $fileText.$fileNumber.$fileExtension; 1571 } 1572 1573 // Return next title 1574 public function getTitleNext($rawData) { 1575 $titleText = $titleNumber = ""; 1576 if (preg_match("/^(.*?)(\d*)$/", $this->yellow->toolbox->getMetaData($rawData, "title"), $matches)) { 1577 $titleText = $matches[1]; 1578 $titleNumber = is_string_empty($matches[2]) ? " 2" : $matches[2]+1; 1579 } 1580 return $titleText.$titleNumber; 1581 } 1582 1583 // Send mail to user 1584 public function sendMail($scheme, $address, $base, $email, $action) { 1585 if ($action=="approve") { 1586 $userName = $this->yellow->system->get("author"); 1587 $userEmail = $this->yellow->system->get("email"); 1588 $userLanguage = $this->extension->getUserLanguage($userEmail); 1589 } else { 1590 $userName = $this->yellow->user->getUser("name", $email); 1591 $userEmail = $email; 1592 $userLanguage = $this->extension->getUserLanguage($email); 1593 } 1594 if ($action=="welcome" || $action=="goodbye") { 1595 $url = "$scheme://$address$base/"; 1596 } else { 1597 $expire = time() + 60*60*24; 1598 $actionToken = $this->createActionToken($email, $action, $expire); 1599 $locationArguments = "/action:$action/email:$email/expire:$expire/language:$userLanguage/actiontoken:$actionToken/"; 1600 $url = "$scheme://$address$base".$this->yellow->lookup->normaliseArguments($locationArguments, false); 1601 } 1602 $prefix = "edit".ucfirst($action); 1603 $message = $this->yellow->language->getText("{$prefix}Message", $userLanguage); 1604 $message = str_replace("\\n", "\r\n", $message); 1605 $message = preg_replace("/@useraccount/i", $email, $message); 1606 $message = preg_replace("/@usershort/i", strtok($userName, " "), $message); 1607 $message = preg_replace("/@username/i", $userName, $message); 1608 $message = preg_replace("/@userlanguage/i", $userLanguage, $message); 1609 $sitename = $this->yellow->system->get("sitename"); 1610 $siteEmail = $this->yellow->system->get("from"); 1611 $subject = $this->yellow->language->getText("{$prefix}Subject", $userLanguage); 1612 $footer = $this->yellow->language->getText("editMailFooter", $userLanguage); 1613 $footer = str_replace("\\n", "\r\n", $footer); 1614 $footer = preg_replace("/@sitename/i", $sitename, $footer); 1615 $mailHeaders = array( 1616 "To" => $this->yellow->lookup->normaliseAddress("$userName <$userEmail>"), 1617 "From" => $this->yellow->lookup->normaliseAddress("$sitename <$siteEmail>"), 1618 "Subject" => $subject, 1619 "Date" => date(DATE_RFC2822), 1620 "Mime-Version" => "1.0", 1621 "Content-Type" => "text/plain; charset=utf-8", 1622 "X-Request-Url" => "$scheme://$address$base"); 1623 $mailMessage = "$message\r\n\r\n$url\r\n-- \r\n$footer"; 1624 return $this->yellow->toolbox->mail($action, $mailHeaders, $mailMessage); 1625 } 1626 1627 // Create browser cookies 1628 public function createCookies($scheme, $address, $base, $email) { 1629 $expire = time() + $this->yellow->system->get("editLoginSessionTimeout"); 1630 $authToken = $this->createAuthToken($email, $expire); 1631 $csrfToken = $this->createCsrfToken(); 1632 setcookie("yellowauthtoken", $authToken, $expire, "$base/", "", $scheme=="https", true); 1633 setcookie("yellowcsrftoken", $csrfToken, $expire, "$base/", "", $scheme=="https", false); 1634 } 1635 1636 // Destroy browser cookies 1637 public function destroyCookies($scheme, $address, $base) { 1638 setcookie("yellowauthtoken", "", 1, "$base/"); 1639 setcookie("yellowcsrftoken", "", 1, "$base/"); 1640 } 1641 1642 // Create authentication token 1643 public function createAuthToken($email, $expire) { 1644 $hash = $this->yellow->user->getUser("hash", $email); 1645 $signature = $this->yellow->toolbox->createHash($hash."auth".$expire, "sha256"); 1646 if (is_string_empty($signature)) $signature = "padd"."error-hash-algorithm-sha256"; 1647 return substrb($signature, 4).$this->yellow->user->getUser("stamp", $email).dechex($expire); 1648 } 1649 1650 // Create action token 1651 public function createActionToken($email, $action, $expire) { 1652 $hash = $this->yellow->user->getUser("hash", $email); 1653 $signature = $this->yellow->toolbox->createHash($hash.$action.$expire, "sha256"); 1654 if (is_string_empty($signature)) $signature = "padd"."error-hash-algorithm-sha256"; 1655 return substrb($signature, 4); 1656 } 1657 1658 // Create CSRF token 1659 public function createCsrfToken() { 1660 return $this->yellow->toolbox->createSalt(64); 1661 } 1662 1663 // Create password hash 1664 public function createHash($password) { 1665 $algorithm = $this->yellow->system->get("editUserHashAlgorithm"); 1666 $cost = $this->yellow->system->get("editUserHashCost"); 1667 $hash = $this->yellow->toolbox->createHash($password, $algorithm, $cost); 1668 if (is_string_empty($hash)) $hash = "error-hash-algorithm-$algorithm"; 1669 return $hash; 1670 } 1671 1672 // Create user stamp 1673 public function createStamp() { 1674 $stamp = $this->yellow->toolbox->createSalt(20); 1675 while ($this->getAuthEmail("none", $stamp)) { 1676 $stamp = $this->yellow->toolbox->createSalt(20); 1677 } 1678 return $stamp; 1679 } 1680 1681 // Check user authentication from email and password 1682 public function checkAuthLogin($email, $password) { 1683 $algorithm = $this->yellow->system->get("editUserHashAlgorithm"); 1684 $hash = $this->yellow->user->getUser("hash", $email); 1685 return $this->yellow->user->getUser("status", $email)=="active" && 1686 $this->yellow->toolbox->verifyHash($password, $algorithm, $hash); 1687 } 1688 1689 // Check user authentication from tokens 1690 public function checkAuthToken($authToken, $csrfTokenExpected, $csrfTokenReceived, $csrfTokenIrrelevant) { 1691 $signature = "$5y$".substrb($authToken, 0, 96); 1692 $email = $this->getAuthEmail($authToken); 1693 $expire = $this->getAuthExpire($authToken); 1694 $hash = $this->yellow->user->getUser("hash", $email); 1695 return $expire>time() && $this->yellow->user->getUser("status", $email)=="active" && 1696 $this->yellow->toolbox->verifyHash($hash."auth".$expire, "sha256", $signature) && 1697 ($this->yellow->toolbox->verifyToken($csrfTokenExpected, $csrfTokenReceived) || $csrfTokenIrrelevant); 1698 } 1699 1700 // Check action token 1701 public function checkActionToken($actionToken, $email, $action, $expire) { 1702 $signature = "$5y$".$actionToken; 1703 $hash = $this->yellow->user->getUser("hash", $email); 1704 return $expire>time() && $this->yellow->user->isExisting($email) && 1705 $this->yellow->toolbox->verifyHash($hash.$action.$expire, "sha256", $signature); 1706 } 1707 1708 // Return user email from authentication, timing attack safe email lookup 1709 public function getAuthEmail($authToken, $stamp = "") { 1710 $email = ""; 1711 if (is_string_empty($stamp)) $stamp = substrb($authToken, 96, 20); 1712 foreach ($this->yellow->user->settings as $key=>$value) { 1713 if ($this->yellow->toolbox->verifyToken($value["stamp"], $stamp)) $email = $key; 1714 } 1715 return $email; 1716 } 1717 1718 // Return expiration time from authentication 1719 public function getAuthExpire($authToken) { 1720 return hexdec(substrb($authToken, 96+20)); 1721 } 1722 1723 // Change content file 1724 public function editContentFile($page, $action, $email) { 1725 if (!$page->isError()) { 1726 foreach ($this->yellow->extension->data as $key=>$value) { 1727 if (method_exists($value["object"], "onEditContentFile")) $value["object"]->onEditContentFile($page, $action, $email); 1728 } 1729 } 1730 } 1731 1732 // Change media file 1733 public function editMediaFile($file, $action, $email) { 1734 if (!$file->isError()) { 1735 foreach ($this->yellow->extension->data as $key=>$value) { 1736 if (method_exists($value["object"], "onEditMediaFile")) $value["object"]->onEditMediaFile($file, $action, $email); 1737 } 1738 } 1739 } 1740 1741 // Change system file 1742 public function editSystemFile($file, $action, $email) { 1743 if (!$file->isError()) { 1744 foreach ($this->yellow->extension->data as $key=>$value) { 1745 if (method_exists($value["object"], "onEditSystemFile")) $value["object"]->onEditSystemFile($file, $action, $email); 1746 } 1747 } 1748 } 1749 1750 // Delete file 1751 public function deleteFileLocation($location, $fileName) { 1752 $rawData = $this->yellow->toolbox->readFile($fileName); 1753 $rawData = $this->yellow->toolbox->setMetaData($rawData, "pageOriginalLocation", $location); 1754 $rawData = $this->yellow->toolbox->setMetaData($rawData, "pageOriginalFileName", $fileName); 1755 return $this->yellow->toolbox->writeFile($fileName, $rawData) && 1756 $this->yellow->toolbox->deleteFile($fileName, $this->yellow->system->get("coreTrashDirectory")); 1757 } 1758 1759 // Delete directory 1760 public function deleteDirectoryLocation($location, $fileName) { 1761 $rawData = $this->yellow->toolbox->readFile($fileName); 1762 $rawData = $this->yellow->toolbox->setMetaData($rawData, "pageOriginalLocation", $location); 1763 $rawData = $this->yellow->toolbox->setMetaData($rawData, "pageOriginalFileName", $fileName); 1764 return $this->yellow->toolbox->writeFile($fileName, $rawData) && 1765 $this->yellow->toolbox->deleteDirectory(dirname($fileName), $this->yellow->system->get("coreTrashDirectory")); 1766 } 1767 1768 // Restore deleted file from trash 1769 public function restoreFileLocation($location) { 1770 $fileNameDeleted = $fileNameRestored = ""; 1771 $deleted = 0; 1772 $pathTrash = $this->yellow->system->get("coreTrashDirectory"); 1773 $regex = "/^.*\\".$this->yellow->system->get("coreContentExtension")."$/"; 1774 foreach ($this->yellow->toolbox->getDirectoryEntries($pathTrash, $regex, false, false) as $entry) { 1775 $rawDataOriginal = $this->yellow->toolbox->readFile($entry); 1776 $locationOriginal = $this->yellow->toolbox->getMetaData($rawDataOriginal, "pageOriginalLocation"); 1777 $fileNameOriginal = $this->yellow->toolbox->getMetaData($rawDataOriginal, "pageOriginalFileName"); 1778 $deletedOriginal = $this->yellow->toolbox->getFileDeleted($entry); 1779 if ($location==$locationOriginal && $deleted<=$deletedOriginal) { 1780 $fileNameDeleted = $entry; 1781 $fileNameRestored = $fileNameOriginal; 1782 $rawDataRestored = $rawDataOriginal; 1783 $rawDataRestored = $this->yellow->toolbox->unsetMetaData($rawDataRestored, "pageOriginalLocation"); 1784 $rawDataRestored = $this->yellow->toolbox->unsetMetaData($rawDataRestored, "pageOriginalFileName"); 1785 $deleted = $deletedOriginal; 1786 } 1787 } 1788 return !is_string_empty($fileNameDeleted) && $this->yellow->lookup->isContentFile($fileNameRestored) && 1789 $this->yellow->toolbox->renameFile($fileNameDeleted, $fileNameRestored, true) && 1790 $this->yellow->toolbox->writeFile($fileNameRestored, $rawDataRestored); 1791 } 1792 1793 // Restore deleted directory from trash 1794 public function restoreDirectoryLocation($location) { 1795 $pathDeleted = $fileNameRestored = ""; 1796 $deleted = 0; 1797 $pathTrash = $this->yellow->system->get("coreTrashDirectory"); 1798 foreach ($this->yellow->toolbox->getDirectoryEntries($pathTrash, "/.*/", false, true) as $entry) { 1799 $fileName = $entry."/".$this->yellow->system->get("coreContentDefaultFile"); 1800 if (!is_file($fileName)) continue; 1801 $rawDataOriginal = $this->yellow->toolbox->readFile($fileName); 1802 $locationOriginal = $this->yellow->toolbox->getMetaData($rawDataOriginal, "pageOriginalLocation"); 1803 $fileNameOriginal = $this->yellow->toolbox->getMetaData($rawDataOriginal, "pageOriginalFileName"); 1804 $deletedOriginal = $this->yellow->toolbox->getFileDeleted($entry); 1805 if ($location==$locationOriginal && $deleted<=$deletedOriginal) { 1806 $pathDeleted = $entry; 1807 $fileNameRestored = $fileNameOriginal; 1808 $rawDataRestored = $rawDataOriginal; 1809 $rawDataRestored = $this->yellow->toolbox->unsetMetaData($rawDataRestored, "pageOriginalLocation"); 1810 $rawDataRestored = $this->yellow->toolbox->unsetMetaData($rawDataRestored, "pageOriginalFileName"); 1811 $deleted = $deletedOriginal; 1812 } 1813 } 1814 return !is_string_empty($pathDeleted) && $this->yellow->lookup->isContentFile($fileNameRestored) && 1815 $this->yellow->toolbox->renameDirectory($pathDeleted, dirname($fileNameRestored), true) && 1816 $this->yellow->toolbox->writeFile($fileNameRestored, $rawDataRestored); 1817 } 1818 1819 // Check if location can be created 1820 public function isCreateLocation($location) { 1821 return !is_string_empty($this->yellow->lookup->findFileFromContentLocation($location)); 1822 } 1823 1824 // Check if location has been deleted 1825 public function isDeletedLocation($location) { 1826 $found = false; 1827 $pathTrash = $this->yellow->system->get("coreTrashDirectory"); 1828 $regex = "/^.*\\".$this->yellow->system->get("coreContentExtension")."$/"; 1829 $fileNames = $this->yellow->toolbox->getDirectoryEntries($pathTrash, $regex, false, false); 1830 foreach ($this->yellow->toolbox->getDirectoryEntries($pathTrash, "/.*/", false, true) as $entry) { 1831 $fileName = $entry."/".$this->yellow->system->get("coreContentDefaultFile"); 1832 if (is_file($fileName)) array_push($fileNames, $fileName); 1833 } 1834 foreach ($fileNames as $fileName) { 1835 $rawDataOriginal = $this->yellow->toolbox->readFile($fileName, 4096); 1836 $locationOriginal = $this->yellow->toolbox->getMetaData($rawDataOriginal, "pageOriginalLocation"); 1837 if ($location==$locationOriginal) { 1838 $found = true; 1839 break; 1840 } 1841 } 1842 return $found; 1843 } 1844 1845 // Check if meta data has been modified 1846 public function isMetaModified($pageSource, $pageOther) { 1847 return substrb($pageSource->rawData, 0, $pageSource->metaDataOffsetBytes) != 1848 substrb($pageOther->rawData, 0, $pageOther->metaDataOffsetBytes); 1849 } 1850 1851 // Check if login with restriction 1852 public function isLoginRestriction() { 1853 return $this->yellow->system->get("editLoginRestriction"); 1854 } 1855 1856 // Check if user is logged in 1857 public function isUser() { 1858 return !is_string_empty($this->userEmail); 1859 } 1860 1861 // Check if user with access 1862 public function isUserAccess($action, $location = "") { 1863 $userHome = $this->yellow->user->getUser("home", $this->userEmail); 1864 $tokens = preg_split("/\s*,\s*/", $this->yellow->user->getUser("access", $this->userEmail)); 1865 return in_array($action, $tokens) && (is_string_empty($location) || substru($location, 0, strlenu($userHome))==$userHome); 1866 } 1867 } 1868 1869 class YellowEditMerge { 1870 public $yellow; // access to API 1871 const ADD = "+"; // merge types 1872 const MODIFY = "*"; 1873 const REMOVE = "-"; 1874 const SAME = " "; 1875 1876 public function __construct($yellow) { 1877 $this->yellow = $yellow; 1878 } 1879 1880 // Merge text, null if not possible 1881 public function merge($textSource, $textMine, $textYours, $showDiff = false) { 1882 if ($textMine!=$textYours) { 1883 $diffMine = $this->buildDiff($textSource, $textMine); 1884 $diffYours = $this->buildDiff($textSource, $textYours); 1885 $diff = $this->mergeDiff($diffMine, $diffYours); 1886 $output = $this->getOutput($diff, $showDiff); 1887 } else { 1888 $output = $textMine; 1889 } 1890 return $output; 1891 } 1892 1893 // Build differences to common source 1894 public function buildDiff($textSource, $textOther) { 1895 $diff = array(); 1896 $lastRemove = -1; 1897 $textStart = 0; 1898 $textSource = $this->yellow->toolbox->getTextLines($textSource); 1899 $textOther = $this->yellow->toolbox->getTextLines($textOther); 1900 $sourceEnd = $sourceSize = count($textSource); 1901 $otherEnd = $otherSize = count($textOther); 1902 while ($textStart<$sourceEnd && $textStart<$otherEnd && $textSource[$textStart]==$textOther[$textStart]) { 1903 ++$textStart; 1904 } 1905 while ($textStart<$sourceEnd && $textStart<$otherEnd && $textSource[$sourceEnd-1]==$textOther[$otherEnd-1]) { 1906 --$sourceEnd; 1907 --$otherEnd; 1908 } 1909 for ($pos=0; $pos<$textStart; ++$pos) { 1910 array_push($diff, array(YellowEditMerge::SAME, $textSource[$pos], false)); 1911 } 1912 $lcs = $this->buildDiffLCS($textSource, $textOther, $textStart, $sourceEnd-$textStart, $otherEnd-$textStart); 1913 for ($x=0,$y=0,$xEnd=$otherEnd-$textStart,$yEnd=$sourceEnd-$textStart; $x<$xEnd || $y<$yEnd;) { 1914 $max = $lcs[$y][$x]; 1915 if ($y<$yEnd && $lcs[$y+1][$x]==$max) { 1916 array_push($diff, array(YellowEditMerge::REMOVE, $textSource[$textStart+$y], false)); 1917 if ($lastRemove==-1) $lastRemove = count($diff)-1; 1918 ++$y; 1919 continue; 1920 } 1921 if ($x<$xEnd && $lcs[$y][$x+1]==$max) { 1922 if ($lastRemove==-1 || $diff[$lastRemove][0]!=YellowEditMerge::REMOVE) { 1923 array_push($diff, array(YellowEditMerge::ADD, $textOther[$textStart+$x], false)); 1924 $lastRemove = -1; 1925 } else { 1926 $diff[$lastRemove] = array(YellowEditMerge::MODIFY, $textOther[$textStart+$x], false); 1927 ++$lastRemove; 1928 if (count($diff)==$lastRemove) $lastRemove = -1; 1929 } 1930 ++$x; 1931 continue; 1932 } 1933 array_push($diff, array(YellowEditMerge::SAME, $textSource[$textStart+$y], false)); 1934 $lastRemove = -1; 1935 ++$x; 1936 ++$y; 1937 } 1938 for ($pos=$sourceEnd;$pos<$sourceSize; ++$pos) { 1939 array_push($diff, array(YellowEditMerge::SAME, $textSource[$pos], false)); 1940 } 1941 return $diff; 1942 } 1943 1944 // Build longest common subsequence 1945 public function buildDiffLCS($textSource, $textOther, $textStart, $yEnd, $xEnd) { 1946 $lcs = array_fill(0, $yEnd+1, array_fill(0, $xEnd+1, 0)); 1947 for ($y=$yEnd-1; $y>=0; --$y) { 1948 for ($x=$xEnd-1; $x>=0; --$x) { 1949 if ($textSource[$textStart+$y]==$textOther[$textStart+$x]) { 1950 $lcs[$y][$x] = $lcs[$y+1][$x+1]+1; 1951 } else { 1952 $lcs[$y][$x] = max($lcs[$y][$x+1], $lcs[$y+1][$x]); 1953 } 1954 } 1955 } 1956 return $lcs; 1957 } 1958 1959 // Merge differences 1960 public function mergeDiff($diffMine, $diffYours) { 1961 $diff = array(); 1962 $posMine = $posYours = 0; 1963 while ($posMine<count($diffMine) && $posYours<count($diffYours)) { 1964 $typeMine = $diffMine[$posMine][0]; 1965 $typeYours = $diffYours[$posYours][0]; 1966 if ($typeMine==YellowEditMerge::SAME) { 1967 array_push($diff, $diffYours[$posYours]); 1968 } elseif ($typeYours==YellowEditMerge::SAME) { 1969 array_push($diff, $diffMine[$posMine]); 1970 } elseif ($typeMine==YellowEditMerge::ADD && $typeYours==YellowEditMerge::ADD) { 1971 $this->mergeConflict($diff, $diffMine[$posMine], $diffYours[$posYours], false); 1972 } elseif ($typeMine==YellowEditMerge::MODIFY && $typeYours==YellowEditMerge::MODIFY) { 1973 $this->mergeConflict($diff, $diffMine[$posMine], $diffYours[$posYours], false); 1974 } elseif ($typeMine==YellowEditMerge::REMOVE && $typeYours==YellowEditMerge::REMOVE) { 1975 array_push($diff, $diffMine[$posMine]); 1976 } elseif ($typeMine==YellowEditMerge::ADD) { 1977 array_push($diff, $diffMine[$posMine]); 1978 } elseif ($typeYours==YellowEditMerge::ADD) { 1979 array_push($diff, $diffYours[$posYours]); 1980 } else { 1981 $this->mergeConflict($diff, $diffMine[$posMine], $diffYours[$posYours], true); 1982 } 1983 if ($typeMine==YellowEditMerge::ADD || $typeYours==YellowEditMerge::ADD) { 1984 if ($typeMine==YellowEditMerge::ADD) ++$posMine; 1985 if ($typeYours==YellowEditMerge::ADD) ++$posYours; 1986 } else { 1987 ++$posMine; 1988 ++$posYours; 1989 } 1990 } 1991 for (;$posMine<count($diffMine); ++$posMine) { 1992 array_push($diff, $diffMine[$posMine]); 1993 $typeMine = $diffMine[$posMine][0]; 1994 $typeYours = " "; 1995 } 1996 for (;$posYours<count($diffYours); ++$posYours) { 1997 array_push($diff, $diffYours[$posYours]); 1998 $typeYours = $diffYours[$posYours][0]; 1999 $typeMine = " "; 2000 } 2001 return $diff; 2002 } 2003 2004 // Merge potential conflict 2005 public function mergeConflict(&$diff, $diffMine, $diffYours, $conflict) { 2006 if (!$conflict && $diffMine[1]==$diffYours[1]) { 2007 array_push($diff, $diffMine); 2008 } else { 2009 array_push($diff, array($diffMine[0], $diffMine[1], true)); 2010 array_push($diff, array($diffYours[0], $diffYours[1], true)); 2011 } 2012 } 2013 2014 // Return merged text, null if not possible 2015 public function getOutput($diff, $showDiff = false) { 2016 $output = ""; 2017 $conflict = false; 2018 if (!$showDiff) { 2019 for ($i=0; $i<count($diff); ++$i) { 2020 if ($diff[$i][0]!=YellowEditMerge::REMOVE) $output .= $diff[$i][1]; 2021 $conflict |= $diff[$i][2]; 2022 } 2023 } else { 2024 for ($i=0; $i<count($diff); ++$i) { 2025 $output .= $diff[$i][2] ? "! " : $diff[$i][0]." "; 2026 $output .= $diff[$i][1]; 2027 } 2028 } 2029 return !$conflict ? $output : null; 2030 } 2031 }