/** * Send an external email message * Caution! gmail may rewrite the "From" header unless you have added the address to your account. * * @param Tree $tree * @param string $to_email * @param string $to_name * @param string $replyto_email * @param string $replyto_name * @param string $subject * @param string $message * * @return bool */ public static function send(Tree $tree, $to_email, $to_name, $replyto_email, $replyto_name, $subject, $message) { try { $mail = new Zend_Mail('UTF-8'); $mail->setSubject($subject)->setBodyHtml($message)->setBodyText(Filter::unescapeHtml($message))->setFrom(Site::getPreference('SMTP_FROM_NAME'), $tree->getPreference('title'))->addTo($to_email, $to_name)->setReplyTo($replyto_email, $replyto_name)->send(self::transport()); } catch (\Exception $ex) { Log::addErrorLog('Mail: ' . $ex->getMessage()); return false; } return true; }
/** * Send an external email message * Caution! gmail may rewrite the "From" header unless you have added the address to your account. * * @param Tree $tree * @param string $to_email * @param string $to_name * @param string $replyto_email * @param string $replyto_name * @param string $subject * @param string $message * * @return bool */ public static function send(Tree $tree, $to_email, $to_name, $replyto_email, $replyto_name, $subject, $message) { try { $mail = Swift_Message::newInstance()->setSubject($subject)->setFrom(Site::getPreference('SMTP_FROM_NAME'), $tree->getPreference('title'))->setTo($to_email, $to_name)->setReplyTo($replyto_email, $replyto_name)->setBody($message, 'text/html')->addPart(Filter::unescapeHtml($message), 'text/plain'); Swift_Mailer::newInstance(self::transport())->send($mail); } catch (Exception $ex) { Log::addErrorLog('Mail: ' . $ex->getMessage()); return false; } return true; }
/** * Get a list of all active (enabled) modules. * * @return AbstractModule[] */ private static function getActiveModules() { /** @var AbstractModule[] - Only query the database once. */ static $modules; if ($modules === null) { $module_names = Database::prepare("SELECT SQL_CACHE module_name FROM `##module` WHERE status = 'enabled'")->fetchOneColumn(); $modules = array(); foreach ($module_names as $module_name) { try { $module = (include WT_ROOT . WT_MODULES_DIR . $module_name . '/module.php'); if ($module instanceof AbstractModule) { $modules[$module->getName()] = $module; } else { throw new \Exception(); } } catch (\Exception $ex) { // The module has been deleted or is broken? Disable it. Log::addConfigurationLog("Module {$module_name} is missing or broken - disabling it"); Database::prepare("UPDATE `##module` SET status = 'disabled' WHERE module_name = :module_name")->execute(array('module_name' => $module_name)); } } } return $modules; }
/** * Determine whether there is enough memory to load a particular image. * * @param string $serverFilename * * @return bool */ public static function hasMemoryForImage($serverFilename) { // find out how much total memory this script can access $memoryAvailable = self::sizeToBytes(ini_get('memory_limit')); // if memory is unlimited, it will return -1 and we don’t need to worry about it if ($memoryAvailable == -1) { return true; } // find out how much memory we are already using $memoryUsed = memory_get_usage(); try { $imgsize = getimagesize($serverFilename); } catch (\ErrorException $ex) { // Not an image, or not a valid image? $imgsize = false; } // find out how much memory this image needs for processing, probably only works for jpegs // from comments on http://www.php.net/imagecreatefromjpeg if ($imgsize && isset($imgsize['bits']) && isset($imgsize['channels'])) { $memoryNeeded = round(($imgsize[0] * $imgsize[1] * $imgsize['bits'] * $imgsize['channels'] / 8 + Pow(2, 16)) * 1.65); $memorySpare = $memoryAvailable - $memoryUsed - $memoryNeeded; if ($memorySpare > 0) { // we have enough memory to load this file return true; } else { // not enough memory to load this file $image_info = sprintf('%.2fKB, %d × %d %d bits %d channels', filesize($serverFilename) / 1024, $imgsize[0], $imgsize[1], $imgsize['bits'], $imgsize['channels']); Log::addMediaLog('Cannot create thumbnail ' . $serverFilename . ' (' . $image_info . ') memory avail: ' . $memoryAvailable . ' used: ' . $memoryUsed . ' needed: ' . $memoryNeeded . ' spare: ' . $memorySpare); return false; } } else { // assume there is enough memory // TODO find out how to check memory needs for gif and png return true; } }
/** * {@inheritDoc} * @see \MyArtJaub\Webtrees\Module\AdminTasks\Model\ConfigurableTaskInterface::saveConfig() */ public function saveConfig() { try { foreach (Tree::getAll() as $tree) { if (Auth::isManager($tree)) { $tree_enabled = Filter::postInteger('HEALTHCHECK_ENABLED_' . $tree->getTreeId(), 0, 1); $tree->setPreference('MAJ_AT_' . $this->getName() . '_ENABLED', $tree_enabled); } } return true; } catch (\Exception $ex) { Log::addErrorLog(sprintf('Error while updating the Admin Task "%s". Exception: %s', $this->getName(), $ex->getMessage())); return false; } }
/** * Accept all pending changes for a specified record. * * @param string $xref * @param int $ged_id */ public static function acceptAllChanges($xref, $ged_id) { $changes = Database::prepare("SELECT change_id, gedcom_name, old_gedcom, new_gedcom" . " FROM `##change` c" . " JOIN `##gedcom` g USING (gedcom_id)" . " WHERE c.status='pending' AND xref=? AND gedcom_id=?" . " ORDER BY change_id")->execute(array($xref, $ged_id))->fetchAll(); foreach ($changes as $change) { if (empty($change->new_gedcom)) { // delete self::updateRecord($change->old_gedcom, $ged_id, true); } else { // add/update self::updateRecord($change->new_gedcom, $ged_id, false); } Database::prepare("UPDATE `##change`" . " SET status='accepted'" . " WHERE status='pending' AND xref=? AND gedcom_id=?")->execute(array($xref, $ged_id)); Log::addEditLog("Accepted change {$change->change_id} for {$xref} / {$change->gedcom_name} into database"); } }
/** * imagettftext is the function that is most likely to throw an error * use this custom error handler to catch and log it * * @param int $errno * @param string $errstr * * @return bool */ function imagettftextErrorHandler($errno, $errstr) { global $useTTF, $serverFilename; // log the error Log::addErrorLog('Media Firewall error: >' . $errno . '/' . $errstr . '< while processing file >' . $serverFilename . '<'); // change value of useTTF to false so the fallback watermarking can be used. $useTTF = false; return true; }
} else { ?> <?php echo I18N::translate('Nothing found to cleanup'); ?> <?php } ?> </p> </form> <?php break; case 'cleanup2': foreach (User::all() as $user) { if (Filter::post('del_' . $user->getUserId()) == '1') { Log::addAuthenticationLog('Deleted user: '******'The user %s has been deleted.', Filter::escapeHtml($user->getUserName())); } } header('Location: ' . WT_BASE_URL . WT_SCRIPT_NAME); break; default: $controller->setPageTitle(I18N::translate('User administration'))->addExternalJavascript(WT_JQUERY_DATATABLES_JS_URL)->addExternalJavascript(WT_DATATABLES_BOOTSTRAP_JS_URL)->addInlineJavascript(' jQuery(".table-user-list").dataTable({ ' . I18N::datatablesI18N() . ', stateSave: true, stateDuration: 300, processing: true, serverSide: true, ajax: {
/** * get image properties * * @param string $which specify either 'main' or 'thumb' * @param int $addWidth amount to add to width * @param int $addHeight amount to add to height * * @return array */ public function getImageAttributes($which = 'main', $addWidth = 0, $addHeight = 0) { $THUMBNAIL_WIDTH = $this->tree->getPreference('THUMBNAIL_WIDTH'); $var = $which . 'imagesize'; if (!empty($this->{$var})) { return $this->{$var}; } $imgsize = array(); if ($this->fileExists($which)) { try { $imgsize = getimagesize($this->getServerFilename($which)); if (is_array($imgsize) && !empty($imgsize['0'])) { // this is an image $imgsize[0] = $imgsize[0] + 0; $imgsize[1] = $imgsize[1] + 0; $imgsize['adjW'] = $imgsize[0] + $addWidth; // adjusted width $imgsize['adjH'] = $imgsize[1] + $addHeight; // adjusted height $imageTypes = array('', 'GIF', 'JPG', 'PNG', 'SWF', 'PSD', 'BMP', 'TIFF', 'TIFF', 'JPC', 'JP2', 'JPX', 'JB2', 'SWC', 'IFF', 'WBMP', 'XBM'); $imgsize['ext'] = $imageTypes[0 + $imgsize[2]]; // this is for display purposes, always show non-adjusted info $imgsize['WxH'] = I18N::translate('%1$s × %2$s pixels', I18N::number($imgsize['0']), I18N::number($imgsize['1'])); $imgsize['imgWH'] = ' width="' . $imgsize['adjW'] . '" height="' . $imgsize['adjH'] . '" '; if ($which == 'thumb' && $imgsize['0'] > $THUMBNAIL_WIDTH) { // don’t let large images break the dislay $imgsize['imgWH'] = ' width="' . $THUMBNAIL_WIDTH . '" '; } } } catch (\ErrorException $ex) { // Not an image, or not a valid image? $imgsize = false; } } if (!is_array($imgsize) || empty($imgsize['0'])) { // this is not an image, OR the file doesn’t exist OR it is a url $imgsize[0] = 0; $imgsize[1] = 0; $imgsize['adjW'] = 0; $imgsize['adjH'] = 0; $imgsize['ext'] = ''; $imgsize['mime'] = ''; $imgsize['WxH'] = ''; $imgsize['imgWH'] = ''; if ($this->isExternal()) { // don’t let large external images break the dislay $imgsize['imgWH'] = ' width="' . $THUMBNAIL_WIDTH . '" '; } } if (empty($imgsize['mime'])) { // this is not an image, OR the file doesn’t exist OR it is a url // set file type equal to the file extension - can’t use parse_url because this may not be a full url $exp = explode('?', $this->file); $imgsize['ext'] = strtoupper(pathinfo($exp[0], PATHINFO_EXTENSION)); // all mimetypes we wish to serve with the media firewall must be added to this array. $mime = array('DOC' => 'application/msword', 'MOV' => 'video/quicktime', 'MP3' => 'audio/mpeg', 'PDF' => 'application/pdf', 'PPT' => 'application/vnd.ms-powerpoint', 'RTF' => 'text/rtf', 'SID' => 'image/x-mrsid', 'TXT' => 'text/plain', 'XLS' => 'application/vnd.ms-excel', 'WMV' => 'video/x-ms-wmv'); if (empty($mime[$imgsize['ext']])) { // if we don’t know what the mimetype is, use something ambiguous $imgsize['mime'] = 'application/octet-stream'; if ($this->fileExists($which)) { // alert the admin if we cannot determine the mime type of an existing file // as the media firewall will be unable to serve this file properly Log::addMediaLog('Media Firewall error: >Unknown Mimetype< for file >' . $this->file . '<'); } } else { $imgsize['mime'] = $mime[$imgsize['ext']]; } } $this->{$var} = $imgsize; return $this->{$var}; }
/** * Execute the task, default skeleton * */ public function execute() { if ($this->last_updated->add(new \DateInterval('PT' . self::TASK_TIME_OUT . 'S')) < new \DateTime()) { $this->is_running = false; } if (!$this->is_running) { $this->last_result = false; $this->is_running = true; $this->save(); Log::addDebugLog('Start execution of Admin task: ' . $this->getTitle()); $this->last_result = $this->executeSteps(); if ($this->last_result) { $this->last_updated = new \DateTime(); if ($this->nb_occurrences > 0) { $this->nb_occurrences--; if ($this->nb_occurrences == 0) { $this->is_enabled = false; } } } $this->is_running = false; $this->save(); Log::addDebugLog('Execution completed for Admin task: ' . $this->getTitle() . ' - ' . ($this->last_result ? 'Success' : 'Failure')); } }
$long_message .= '#' . $level . ' ' . $frame['file'] . ':' . $frame['line'] . ' '; $short_message .= '#' . $level . ' ' . $frame['file'] . ':' . $frame['line'] . ' '; if ($level) { $long_message .= $frame['function'] . '(' . implode(', ', $frame['args']) . ')' . PHP_EOL; $short_message .= $frame['function'] . "()<br>"; } else { $long_message .= get_class($ex) . '("' . $ex->getMessage() . '")' . PHP_EOL; $short_message .= get_class($ex) . '("' . $ex->getMessage() . '")<br>'; } } if (WT_DEBUG) { echo $long_message; } else { echo $short_message; } Log::addErrorLog($long_message); }); // Load our configuration file, so we can connect to the database if (file_exists(WT_ROOT . 'data/config.ini.php')) { $dbconfig = parse_ini_file(WT_ROOT . 'data/config.ini.php'); // Invalid/unreadable config file? if (!is_array($dbconfig)) { header('Location: ' . WT_BASE_URL . 'site-unavailable.php'); exit; } // Down for maintenance? if (file_exists(WT_ROOT . 'data/offline.txt')) { header('Location: ' . WT_BASE_URL . 'site-offline.php'); exit; } } else {
/** * {inhericDoc} * @see \MyArtJaub\Webtrees\Module\AdminTasks\Model\TaskProviderInterface::deleteTask() */ public function deleteTask($task_name) { try { Database::beginTransaction(); Database::prepare('DELETE FROM `##maj_admintasks` WHERE majat_name= :task_name')->execute(array('task_name' => $task_name)); Database::prepare('DELETE FROM `##gedcom_setting` WHERE setting_name LIKE :setting_name')->execute(array('setting_name' => 'MAJ_AT_' . $task_name . '%')); Database::commit(); Log::addConfigurationLog('Admin Task ' . $task_name . ' has been deleted from disk - deleting it from DB'); return true; } catch (\Exception $ex) { Database::rollback(); Log::addErrorLog('An error occurred while deleting Admin Task ' . $task_name . '. Exception: ' . $ex->getMessage()); return false; } }
/** {@inheritdoc} */ public function modAction($mod_action) { Database::updateSchema(self::SCHEMA_MIGRATION_PREFIX, self::SCHEMA_SETTING_NAME, self::SCHEMA_TARGET_VERSION); switch ($mod_action) { case 'admin_config': $template = new AdminTemplate(); return $template->pageContent(); case 'admin_search': // new settings $surname = Filter::post('SURNAME'); $pid = Filter::post('PID'); if ($surname) { $soundex_std = Filter::postBool('soundex_std'); $soundex_dm = Filter::postBool('soundex_dm'); $indis = $this->module()->indisArray($surname, $soundex_std, $soundex_dm); usort($indis, 'Fisharebest\\Webtrees\\Individual::compareBirthDate'); if (isset($indis) && count($indis) > 0) { $pid = $indis[0]->getXref(); } else { $result['error'] = I18N::translate('Error: The surname you entered doesn’t exist in this tree.'); } } if (isset($pid)) { $FTV_SETTINGS = unserialize($this->getSetting('FTV_SETTINGS')); if ($this->module()->searchArray($this->module()->searchArray($FTV_SETTINGS, 'TREE', Filter::getInteger('tree')), 'PID', $pid)) { if ($surname) { $result['error'] = I18N::translate('Error: The root person belonging to this surname already exists'); } else { $result['error'] = I18N::translate('Error: A root person with ID %s already exists', $pid); } } else { $record = Individual::getInstance($pid, $this->tree); if ($record) { $root = $record->getFullName() . ' (' . $record->getLifeSpan() . ')'; $title = $this->module()->getPageLink($pid); $result = array('access_level' => '2', 'pid' => $pid, 'root' => $root, 'sort' => count($this->module()->searchArray($FTV_SETTINGS, 'TREE', Filter::getInteger('tree'))) + 1, 'surname' => $this->module()->getSurname($pid), 'title' => $title, 'tree' => Filter::getInteger('tree')); } else { if (empty($result['error'])) { $result['error'] = I18N::translate('Error: A person with ID %s does not exist in this tree', $pid); } } } } echo json_encode($result); break; case 'admin_add': $FTV_SETTINGS = unserialize($this->getSetting('FTV_SETTINGS')); $NEW_FTV_SETTINGS = $FTV_SETTINGS; $NEW_FTV_SETTINGS[] = array('TREE' => Filter::getInteger('tree'), 'SURNAME' => Filter::post('surname'), 'PID' => Filter::post('pid'), 'ACCESS_LEVEL' => Filter::postInteger('access_level'), 'SORT' => Filter::postInteger('sort')); $this->setSetting('FTV_SETTINGS', serialize(array_values($NEW_FTV_SETTINGS))); Log::addConfigurationLog($this->getTitle() . ' config updated'); break; case 'admin_update': $FTV_SETTINGS = unserialize($this->getSetting('FTV_SETTINGS')); $new_surname = Filter::postArray('surname'); $new_access_level = Filter::postArray('access_level'); $new_sort = Filter::postArray('sort'); foreach ($new_surname as $key => $new_surname) { $FTV_SETTINGS[$key]['SURNAME'] = $new_surname; } foreach ($new_access_level as $key => $new_access_level) { $FTV_SETTINGS[$key]['ACCESS_LEVEL'] = $new_access_level; } foreach ($new_sort as $key => $new_sort) { $FTV_SETTINGS[$key]['SORT'] = $new_sort; } $NEW_FTV_SETTINGS = $this->module()->sortArray($FTV_SETTINGS, 'SORT'); $this->setSetting('FTV_SETTINGS', serialize($NEW_FTV_SETTINGS)); break; case 'admin_save': $FTV_OPTIONS = unserialize($this->getSetting('FTV_OPTIONS')); $FTV_OPTIONS[Filter::getInteger('tree')] = Filter::postArray('NEW_FTV_OPTIONS'); $this->setSetting('FTV_OPTIONS', serialize($FTV_OPTIONS)); Log::addConfigurationLog($this->getTitle() . ' config updated'); // the cache has to be recreated because the image options could have been changed $this->module()->emptyCache(); break; case 'admin_reset': $FTV_OPTIONS = unserialize($this->getSetting('FTV_OPTIONS')); unset($FTV_OPTIONS[Filter::getInteger('tree')]); $this->setSetting('FTV_OPTIONS', serialize($FTV_OPTIONS)); Log::addConfigurationLog($this->getTitle() . ' options set to default'); break; case 'admin_delete': $FTV_SETTINGS = unserialize($this->getSetting('FTV_SETTINGS')); unset($FTV_SETTINGS[Filter::getInteger('key')]); $this->setSetting('FTV_SETTINGS', serialize($FTV_SETTINGS)); Log::addConfigurationLog($this->getTitle() . ' item deleted'); break; case 'page': $template = new PageTemplate(); return $template->pageContent(); // See mediafirewall.php // See mediafirewall.php case 'thumbnail': $mid = Filter::get('mid', WT_REGEX_XREF); $media = Media::getInstance($mid, $this->tree); $mimetype = $media->mimeType(); $cache_filename = $this->module()->cacheFileName($media); $filetime = filemtime($cache_filename); $filetimeHeader = gmdate('D, d M Y H:i:s', $filetime) . ' GMT'; $expireOffset = 3600 * 24 * 7; // tell browser to cache this image for 7 days $expireHeader = gmdate('D, d M Y H:i:s', WT_TIMESTAMP + $expireOffset) . ' GMT'; $etag = $media->getEtag(); $filesize = filesize($cache_filename); // parse IF_MODIFIED_SINCE header from client $if_modified_since = 'x'; if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { $if_modified_since = preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE']); } // parse IF_NONE_MATCH header from client $if_none_match = 'x'; if (isset($_SERVER['HTTP_IF_NONE_MATCH'])) { $if_none_match = str_replace('"', '', $_SERVER['HTTP_IF_NONE_MATCH']); } // add caching headers. allow browser to cache file, but not proxy header('Last-Modified: ' . $filetimeHeader); header('ETag: "' . $etag . '"'); header('Expires: ' . $expireHeader); header('Cache-Control: max-age=' . $expireOffset . ', s-maxage=0, proxy-revalidate'); // if this file is already in the user’s cache, don’t resend it // first check if the if_modified_since param matches if ($if_modified_since === $filetimeHeader) { // then check if the etag matches if ($if_none_match === $etag) { http_response_code(304); return; } } // send headers for the image header('Content-Type: ' . $mimetype); header('Content-Disposition: filename="' . basename($cache_filename) . '"'); header('Content-Length: ' . $filesize); // Some servers disable fpassthru() and readfile() if (function_exists('readfile')) { readfile($cache_filename); } else { $fp = fopen($cache_filename, 'rb'); if (function_exists('fpassthru')) { fpassthru($fp); } else { while (!feof($fp)) { echo fread($fp, 65536); } } fclose($fp); } break; case 'show_pdf': $template = new PdfTemplate(); return $template->pageBody(); case 'pdf_data': $template = new PdfTemplate(); return $template->pageData(); case 'pdf_thumb_data': $xref = Filter::get('mid'); $mediaobject = Media::getInstance($xref, $this->tree); $thumb = Filter::get('thumb'); if ($thumb === '2') { // Fancy thumb echo $this->module()->cacheFileName($mediaobject); } else { echo $mediaobject->getServerFilename('thumb'); } break; default: http_response_code(404); break; } }
/** * Check that the POST request contains the CSRF token generated above. * * @return bool */ public static function checkCsrf() { if (self::post('csrf') !== self::getCsrfToken()) { // Oops. Something is not quite right Log::addAuthenticationLog('CSRF mismatch - session expired or malicious attack'); FlashMessages::addMessage(I18N::translate('This form has expired. Try again.'), 'error'); return false; } return true; }
/** * {@inhericDoc} * @see \Fisharebest\Webtrees\GedcomRecord::getInstance() */ public static function getInstance($xref, Tree $tree, $gedcom = null, CertificateProviderInterface $provider = null) { try { $certfile = Functions::decryptFromSafeBase64($xref); //NEED TO CHECK THAT !!! if (Functions::isValidPath($certfile, true)) { return new Certificate($certfile, $tree, $provider); } } catch (\Exception $ex) { Log::addErrorLog('Certificate module error : > ' . $ex->getMessage() . ' < with data > ' . $xref . ' <'); } return null; }
/** * GeoAnalysis@delete */ public function delete() { global $WT_TREE; $controller = new JsonController(); $ga_id = Filter::getInteger('ga_id'); $ga = $this->provider->getGeoAnalysis($ga_id, false); $controller->restrictAccess(true && Auth::isManager($WT_TREE) && $ga); $res = array('geoanalysis' => $ga->getId(), 'error' => null); try { $this->provider->deleteGeoAnalysis($ga); Log::addConfigurationLog('Module ' . $this->module->getName() . ' : Geo Analysis ID "' . $ga->getId() . '" has been deleted.'); } catch (\Exception $ex) { $res['error'] = $ex->getMessage(); Log::addErrorLog('Module ' . $this->module->getName() . ' : Geo Analysis ID "' . $ga->getId() . '" could not be deleted. Error: ' . $ex->getMessage()); } $controller->pageHeader(); if ($res['error']) { http_response_code(500); } $controller->encode($res); }
$message['from_name'] = $from_name; $message['from_email'] = $from_email; } $message['subject'] = $subject; $message['body'] = nl2br($body, false); $message['created'] = WT_TIMESTAMP; $message['method'] = $method; $message['url'] = $url; if ($i > 0) { $message['no_from'] = true; } if (addMessage($message)) { FlashMessages::addMessage(I18N::translate('Message successfully sent to %s', Filter::escapeHtml($to))); } else { FlashMessages::addMessage(I18N::translate('Message was not sent')); Log::addErrorLog('Unable to send a message. FROM:' . $from . ' TO:' . $to . ' (failed to send)'); } $i++; } $controller->pageHeader()->addInlineJavascript('window.opener.location.reload(); window.close();'); break; } /** * Add a message to a user's inbox * * @param string[] $message * * @return bool */ function addMessage($message) {
break; case 'undoall': Database::prepare("UPDATE `##change`" . " SET status='rejected'" . " WHERE status='pending' AND gedcom_id=?")->execute(array($WT_TREE->getTreeId())); break; case 'acceptall': $changes = Database::prepare("SELECT change_id, gedcom_id, gedcom_name, xref, old_gedcom, new_gedcom" . " FROM `##change` c" . " JOIN `##gedcom` g USING (gedcom_id)" . " WHERE c.status='pending' AND gedcom_id=?" . " ORDER BY change_id")->execute(array($WT_TREE->getTreeId()))->fetchAll(); foreach ($changes as $change) { if (empty($change->new_gedcom)) { // delete FunctionsImport::updateRecord($change->old_gedcom, $change->gedcom_id, true); } else { // add/update FunctionsImport::updateRecord($change->new_gedcom, $change->gedcom_id, false); } Database::prepare("UPDATE `##change` SET status='accepted' WHERE change_id=?")->execute(array($change->change_id)); Log::addEditLog("Accepted change {$change->change_id} for {$change->xref} / {$change->gedcom_name} into database"); } break; } $changed_gedcoms = Database::prepare("SELECT g.gedcom_name" . " FROM `##change` c" . " JOIN `##gedcom` g USING (gedcom_id)" . " WHERE c.status='pending'" . " GROUP BY g.gedcom_name")->fetchOneColumn(); if ($changed_gedcoms) { $changes = Database::prepare("SELECT c.*, u.user_name, u.real_name, g.gedcom_name, new_gedcom, old_gedcom" . " FROM `##change` c" . " JOIN `##user` u USING (user_id)" . " JOIN `##gedcom` g USING (gedcom_id)" . " WHERE c.status='pending'" . " ORDER BY gedcom_id, c.xref, c.change_id")->fetchAll(); $output = '<br><br><table class="list_table">'; $prev_xref = null; $prev_gedcom_id = null; foreach ($changes as $change) { $tree = Tree::findById($change->gedcom_id); preg_match('/^0 @' . WT_REGEX_XREF . '@ (' . WT_REGEX_TAG . ')/', $change->old_gedcom . $change->new_gedcom, $match); switch ($match[1]) { case 'INDI': $record = new Individual($change->xref, $change->old_gedcom, $change->new_gedcom, $tree);
/** * Delete this record */ public function deleteRecord() { // Create a pending change if (!$this->isPendingDeletion()) { Database::prepare("INSERT INTO `##change` (gedcom_id, xref, old_gedcom, new_gedcom, user_id) VALUES (?, ?, ?, '', ?)")->execute(array($this->tree->getTreeId(), $this->xref, $this->getGedcom(), Auth::id())); } // Auto-accept this pending change if (Auth::user()->getPreference('auto_accept')) { FunctionsImport::acceptAllChanges($this->xref, $this->tree->getTreeId()); } // Clear the cache self::$gedcom_record_cache = null; self::$pending_record_cache = null; Log::addEditLog('Delete: ' . static::RECORD_TYPE . ' ' . $this->xref); }
$tag = array_merge(array('FILE'), $tag); $islink = array_merge(array(0), $islink); $text = array_merge(array($newFilename), $text); $record = GedcomRecord::getInstance($pid, $WT_TREE); $newrec = "0 @{$pid}@ OBJE\n"; $newrec = FunctionsEdit::handleUpdates($newrec); $record->updateRecord($newrec, $update_CHAN); if ($move_file) { // We've moved a file. Therefore we must approve the change, as rejecting // the change will create broken references. FunctionsImport::acceptAllChanges($record->getXref(), $record->getTree()->getTreeId()); } if ($pid && $linktoid) { $record = GedcomRecord::getInstance($linktoid, $WT_TREE); $record->createFact('1 OBJE @' . $pid . '@', true); Log::addEditLog('Media ID ' . $pid . " successfully added to {$linktoid}."); } $controller->pageHeader(); if ($messages) { echo '<button onclick="closePopupAndReloadParent();">', I18N::translate('close'), '</button>'; } else { $controller->addInlineJavascript('closePopupAndReloadParent();'); } return; case 'showmediaform': $controller->setPageTitle(I18N::translate('Create a new media object')); $action = 'create'; break; case 'editmedia': $controller->setPageTitle(I18N::translate('Edit media object')); $action = 'update';
I18N::init($webmaster->getPreference('language')); $user = User::findByUserName($user_name); $edit_user_url = WT_BASE_URL . "admin_users.php?action=edit&user_id=" . $user->getUserId(); $mail1_body = I18N::translate('Hello administrator…') . Mail::EOL . Mail::EOL . I18N::translate('A new user (%1$s) has requested an account (%2$s) and verified an email address (%3$s).', $user->getRealNameHtml(), Filter::escapeHtml($user->getUserName()), Filter::escapeHtml($user->getEmail())) . Mail::EOL . Mail::EOL . I18N::translate('You need to review the account details.') . Mail::EOL . Mail::EOL . '<a href="' . $edit_user_url . '">' . $edit_user_url . '</a>' . Mail::EOL . Mail::EOL . I18N::translate('Set the status to “approved”.') . Mail::EOL . I18N::translate('Set the access level for each tree.') . Mail::EOL . I18N::translate('Link the user account to an individual.'); $mail1_subject = I18N::translate('New user at %s', WT_BASE_URL . ' ' . $WT_TREE->getTitle()); // Change to the new user’s language I18N::init($user->getPreference('language')); $controller->setPageTitle(I18N::translate('User verification')); $controller->pageHeader(); echo '<div id="login-register-page">'; echo '<h2>' . I18N::translate('User verification') . '</h2>'; echo '<div id="user-verify">'; if ($user && $user->checkPassword($user_password) && $user->getPreference('reg_hashcode') === $user_hashcode) { Mail::send($WT_TREE, $webmaster->getEmail(), $webmaster->getRealName(), $WT_TREE->getPreference('WEBTREES_EMAIL'), $WT_TREE->getPreference('WEBTREES_EMAIL'), $mail1_subject, $mail1_body); $mail1_method = $webmaster->getPreference('CONTACT_METHOD'); if ($mail1_method != 'messaging3' && $mail1_method != 'mailto' && $mail1_method != 'none') { Database::prepare("INSERT INTO `##message` (sender, ip_address, user_id, subject, body) VALUES (? ,? ,? ,? ,?)")->execute(array($user_name, WT_CLIENT_IP, $webmaster->getUserId(), $mail1_subject, Filter::unescapeHtml($mail1_body))); } $user->setPreference('verified', '1')->setPreference('reg_timestamp', date('U'))->deletePreference('reg_hashcode'); Log::addAuthenticationLog('User ' . $user_name . ' verified their email address'); echo '<p>', I18N::translate('You have confirmed your request to become a registered user.'), '</p>'; echo '<p>', I18N::translate('The administrator has been informed. As soon as they give you permission to login, you can login with your user name and password.'), '</p>'; } else { echo '<p class="warning">'; echo I18N::translate('Could not verify the information you entered. Please try again or contact the site administrator for more information.'); echo '</p>'; } echo '</div>'; echo '</div>'; break; }
$arg = '[' . gettype($arg) . ']'; break; } }); $frame['file'] = str_replace(dirname(__DIR__), '', $frame['file']); $message .= '#' . $level . ' ' . $frame['file'] . ':' . $frame['line'] . ' '; if ($level) { $message .= $frame['function'] . '(' . implode(', ', $frame['args']) . ')' . PHP_EOL; } else { $message .= get_class($ex) . '("' . $ex->getMessage() . '")' . PHP_EOL; } } if (true || error_reporting() & $ex->getCode()) { echo $message; } Log::addErrorLog($message); }); // Load our configuration file, so we can connect to the database if (file_exists(WT_ROOT . 'data/config.ini.php')) { $dbconfig = parse_ini_file(WT_ROOT . 'data/config.ini.php'); // Invalid/unreadable config file? if (!is_array($dbconfig)) { header('Location: ' . WT_BASE_URL . 'site-unavailable.php'); exit; } // Down for maintenance? if (file_exists(WT_ROOT . 'data/offline.txt')) { header('Location: ' . WT_BASE_URL . 'site-offline.php'); exit; } } else {
/** * Create a new record from GEDCOM data. * * @param string $gedcom * * @throws \Exception * * @return GedcomRecord */ public function createRecord($gedcom) { if (preg_match('/^0 @(' . WT_REGEX_XREF . ')@ (' . WT_REGEX_TAG . ')/', $gedcom, $match)) { $xref = $match[1]; $type = $match[2]; } else { throw new \Exception('Invalid argument to GedcomRecord::createRecord(' . $gedcom . ')'); } if (strpos("\r", $gedcom) !== false) { // MSDOS line endings will break things in horrible ways throw new \Exception('Evil line endings found in GedcomRecord::createRecord(' . $gedcom . ')'); } // webtrees creates XREFs containing digits. Anything else (e.g. “new”) is just a placeholder. if (!preg_match('/\\d/', $xref)) { $xref = $this->getNewXref($type); $gedcom = preg_replace('/^0 @(' . WT_REGEX_XREF . ')@/', '0 @' . $xref . '@', $gedcom); } // Create a change record, if not already present if (!preg_match('/\\n1 CHAN/', $gedcom)) { $gedcom .= "\n1 CHAN\n2 DATE " . date('d M Y') . "\n3 TIME " . date('H:i:s') . "\n2 _WT_USER " . Auth::user()->getUserName(); } // Create a pending change Database::prepare("INSERT INTO `##change` (gedcom_id, xref, old_gedcom, new_gedcom, user_id) VALUES (?, ?, '', ?, ?)")->execute(array($this->tree_id, $xref, $gedcom, Auth::id())); Log::addEditLog('Create: ' . $type . ' ' . $xref); // Accept this pending change if (Auth::user()->getPreference('auto_accept')) { FunctionsImport::acceptAllChanges($xref, $this->tree_id); } // Return the newly created record. Note that since GedcomRecord // has a cache of pending changes, we cannot use it to create a // record with a newly created pending change. return GedcomRecord::getInstance($xref, $this, $gedcom); }
/** * recursively find all of the csv files on the server * * @param string $path * * @return string[] */ private function findFiles($path) { $placefiles = array(); try { $di = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS); $it = new \RecursiveIteratorIterator($di); foreach ($it as $file) { if ($file->getExtension() == "csv") { $placefiles[] = '/' . $file->getFilename(); } } } catch (\Exception $e) { Log::addErrorLog(basename($e->getFile()) . ' - line: ' . $e->getLine() . ' - ' . $e->getMessage()); } return $placefiles; }
// Change the current language $language = Filter::post('language'); try { I18N::init($language); Session::put('locale', $language); // Remember our selection Auth::user()->setPreference('language', $language); } catch (\Exception $ex) { // Request for a non-existant language. http_response_code(406); } break; case 'masquerade': $user = User::find(Filter::postInteger('user_id')); if ($user && Auth::isAdmin() && Auth::user() !== $user) { Log::addAuthenticationLog('Masquerade as user: '******'unlink-media': // Remove links from an individual and their spouse-family records to a media object. // Used by the "unlink" option on the album (lightbox) tab. $source = Individual::getInstance(Filter::post('source', WT_REGEX_XREF), $WT_TREE); $target = Filter::post('target', WT_REGEX_XREF); if ($source && $source->canShow() && $source->canEdit() && $target) { // Consider the individual and their spouse-family records $sources = $source->getSpouseFamilies(); $sources[] = $source; foreach ($sources as $source) {
public function getSidebarContent() { // code based on similar in function_print_list.php global $controller; // load the module stylesheet $html = $this->includeCss(WT_MODULES_DIR . $this->getName() . '/css/style.css') . '<script src="' . WT_STATIC_URL . $this->directory . '/js/sidebar.js" defer="defer"></script>'; $controller->addInlineJavascript(' jQuery("#' . $this->getName() . ' a").text("' . $this->getSidebarTitle() . '"); jQuery("#' . $this->getName() . '_content").on("click", ".frl-area-title", function(e){ e.preventDefault(); jQuery(this).next(".frl-list").slideToggle() jQuery(this).parent().siblings().find(".frl-list").slideUp(); }); '); try { $FRL_PLUGINS = unserialize($this->getSetting('FRL_PLUGINS')); $html .= '<ul id="fancy_research_links_content">'; $i = 0; $total_enabled_plugins = 0; foreach ($this->module()->getPluginList() as $area => $plugins) { $enabled_plugins = $this->module()->countEnabledPlugins($plugins, $FRL_PLUGINS); $total_enabled_plugins = $total_enabled_plugins + $enabled_plugins; if ($enabled_plugins > 0) { $html .= '<li class="frl-area"><span class="ui-accordion-header-icon ui-icon ui-icon-triangle-1-e"></span><a href="#" class="frl-area-title">' . $area . ' (' . $enabled_plugins . ')' . '</a>' . '<ul class="frl-list">'; $i++; foreach ($plugins as $label => $plugin) { if (is_array($FRL_PLUGINS) && array_key_exists($label, $FRL_PLUGINS)) { $enabled = $FRL_PLUGINS[$label]; } else { $enabled = '1'; } if ($enabled) { foreach ($controller->record->getFacts() as $fact) { $tag = $fact->getTag(); if ($tag == "NAME") { $this->primary = $this->module()->getPrimaryName($fact); break; // only use the first fact with a NAME tag found. } } if ($this->primary) { if ($plugin->createLinkOnly()) { $link = $plugin->createLinkOnly(); } else { $this->attrs = array('birthyear' => $controller->record->getBirthYear(), 'birthplace' => $controller->record->getBirthPlace(), 'deathyear' => $controller->record->getDeathYear(), 'deathplace' => $controller->record->getDeathPlace()); $link = $plugin->createLink($this->module()->getNames($this->primary, $this->attrs, $plugin->encodePlus())); } $html .= '<li>' . '<a href="' . Filter::escapeHtml($link) . '">' . $plugin->getPluginName() . '</a>' . '</li>'; } } } $html .= '</ul>'; } } $html .= '</ul>'; if ($total_enabled_plugins === 0) { $html = I18N::translate('There are no research links available for this individual.'); } return $html; } catch (\ErrorException $ex) { Log::addErrorLog('Fancy ResearchLinks: ' . $ex->getMessage()); return I18N::translate('There are no research links available for this individual.'); } }
$form_language = Filter::post('form_language'); $form_timezone = Filter::post('form_timezone'); $form_contact_method = Filter::post('form_contact_method'); $form_visible_online = Filter::postBool('form_visible_online'); // Respond to form action if ($form_action && Filter::checkCsrf()) { switch ($form_action) { case 'update': if ($form_username !== Auth::user()->getUserName() && User::findByUserName($form_username)) { FlashMessages::addMessage(I18N::translate('Duplicate user name. A user with that user name already exists. Please choose another user name.')); } elseif ($form_email !== Auth::user()->getEmail() && User::findByEmail($form_email)) { FlashMessages::addMessage(I18N::translate('Duplicate email address. A user with that email already exists.')); } else { // Change username if ($form_username !== Auth::user()->getUserName()) { Log::addAuthenticationLog('User ' . Auth::user()->getUserName() . ' renamed to ' . $form_username); Auth::user()->setUserName($form_username); } // Change password if ($form_pass1 && $form_pass1 === $form_pass2) { Auth::user()->setPassword($form_pass1); } // Change other settings Auth::user()->setRealName($form_realname)->setEmail($form_email)->setPreference('language', $form_language)->setPreference('TIMEZONE', $form_timezone)->setPreference('contactmethod', $form_contact_method)->setPreference('visibleonline', $form_visible_online ? '1' : '0'); if ($form_theme === null) { Auth::user()->deletePreference('theme'); } else { Auth::user()->setPreference('theme', $form_theme); } $WT_TREE->setUserPreference(Auth::user(), 'rootid', $form_rootid); }
/** * Replace variable identifiers with their values. * * @param string $expression An expression such as "$foo == 123" * @param bool $quote Whether to add quotation marks * * @return string */ private function substituteVars($expression, $quote) { $that = $this; // PHP5.3 cannot access $this inside a closure return preg_replace_callback('/\\$(\\w+)/', function ($matches) use($that, $quote) { if (isset($that->vars[$matches[1]]['id'])) { if ($quote) { return "'" . addcslashes($that->vars[$matches[1]]['id'], "'") . "'"; } else { return $that->vars[$matches[1]]['id']; } } else { Log::addErrorLog(sprintf('Undefined variable $%s in report', $matches[1])); return '$' . $matches[1]; } }, $expression); }
/** * Gathers results for a soundex search * * NOTE * ==== * Does not search on the selected gedcoms, searches on all the gedcoms * Does not work on first names, instead of the code, value array is used in the search * Returns all the names even when Names with hit selected * Does not sort results by first name * Does not work on separate double word surnames * Does not work on duplicate code values of the searched text and does not give the correct code * Cohen should give DM codes 556000, 456000, 460000 and 560000, in 4.1 we search only on 560000?? * * The names' Soundex SQL table contains all the soundex values twice * The places table contains only one value */ private function soundexSearch() { if ((!empty($this->lastname) || !empty($this->firstname) || !empty($this->place)) && $this->search_trees) { $logstring = "Type: Soundex\n"; if (!empty($this->lastname)) { $logstring .= "Last name: " . $this->lastname . "\n"; } if (!empty($this->firstname)) { $logstring .= "First name: " . $this->firstname . "\n"; } if (!empty($this->place)) { $logstring .= "Place: " . $this->place . "\n"; } if (!empty($this->year)) { $logstring .= "Year: " . $this->year . "\n"; } Log::addSearchLog($logstring, $this->search_trees); if ($this->search_trees) { $this->myindilist = FunctionsDb::searchIndividualsPhonetic($this->soundex, $this->lastname, $this->firstname, $this->place, $this->search_trees); } else { $this->myindilist = array(); } } // Now we have the final list of individuals to be printed. // We may add the assos at this point. if ($this->showasso == 'on') { foreach ($this->myindilist as $indi) { foreach ($indi->linkedIndividuals('ASSO') as $asso) { $this->myindilist[] = $asso; } foreach ($indi->linkedIndividuals('_ASSO') as $asso) { $this->myindilist[] = $asso; } foreach ($indi->linkedFamilies('ASSO') as $asso) { $this->myfamlist[] = $asso; } foreach ($indi->linkedFamilies('_ASSO') as $asso) { $this->myfamlist[] = $asso; } } } //-- if only 1 item is returned, automatically forward to that item if (count($this->myindilist) == 1 && $this->action != "replace") { $indi = $this->myindilist[0]; header('Location: ' . WT_BASE_URL . $indi->getRawUrl()); exit; } usort($this->myindilist, '\\Fisharebest\\Webtrees\\GedcomRecord::compare'); usort($this->myfamlist, '\\Fisharebest\\Webtrees\\GedcomRecord::compare'); }
/** * imagettftext is the function that is most likely to throw an error * use this custom error handler to catch and log it * * @param int $errno * @param string $errstr * * @return bool */ function imageTtfTextErrorHandler($errno, $errstr) { // log the error Log::addErrorLog('Image Builder error: >' . $errno . '/' . $errstr . '< while processing file >' . $this->media->getServerFilename() . '<'); // change value of useTTF to false so the fallback watermarking can be used. $this->use_ttf = false; return true; }