private function renderCommonProperties(PHUIPropertyListView $properties, PhabricatorCacheSpec $cache) { if ($cache->getName() !== null) { $name = $this->renderYes($cache->getName()); } else { $name = $this->renderNo(pht('None')); } $properties->addProperty(pht('Cache'), $name); if ($cache->getIsEnabled()) { $enabled = $this->renderYes(pht('Enabled')); } else { $enabled = $this->renderNo(pht('Not Enabled')); } $properties->addProperty(pht('Enabled'), $enabled); $version = $cache->getVersion(); if ($version) { $properties->addProperty(pht('Version'), $this->renderInfo($version)); } if ($cache->getName() === null) { return; } $mem_total = $cache->getTotalMemory(); $mem_used = $cache->getUsedMemory(); if ($mem_total) { $percent = 100 * ($mem_used / $mem_total); $properties->addProperty(pht('Memory Usage'), pht('%s of %s', phutil_tag('strong', array(), sprintf('%.1f%%', $percent)), phutil_format_bytes($mem_total))); } $entry_count = $cache->getEntryCount(); if ($entry_count !== null) { $properties->addProperty(pht('Cache Entries'), pht('%s', new PhutilNumber($entry_count))); } }
public function testByteFormatting() { $tests = array(1 => '1 B', 1024 => '1 KB', 1024 * 1024 => '1 MB', 10 * 1024 * 1024 => '10 MB', 100 * 1024 * 1024 => '100 MB', 1024 * 1024 * 1024 => '1 GB', 999 => '999 B'); foreach ($tests as $input => $expect) { $this->assertEqual($expect, phutil_format_bytes($input), 'phutil_format_bytes(' . $input . ')'); } }
/** * @phutil-external-symbol class PhabricatorStartup */ protected function executeChecks() { $upload_limit = PhabricatorEnv::getEnvConfig('storage.upload-size-limit'); if (!$upload_limit) { $message = pht('The Phabricator file upload limit is not configured. You may only ' . 'be able to upload very small files until you configure it, because ' . 'some PHP default limits are very low (as low as 2MB).'); $this->newIssue('config.storage.upload-size-limit')->setShortName(pht('Upload Limit'))->setName(pht('Upload Limit Not Yet Configured'))->setMessage($message)->addPhabricatorConfig('storage.upload-size-limit'); } else { $memory_limit = PhabricatorStartup::getOldMemoryLimit(); if ($memory_limit && (int) $memory_limit > 0) { $memory_limit_bytes = phutil_parse_bytes($memory_limit); $memory_usage_bytes = memory_get_usage(); $upload_limit_bytes = phutil_parse_bytes($upload_limit); $available_bytes = $memory_limit_bytes - $memory_usage_bytes; if ($upload_limit_bytes > $available_bytes) { $summary = pht('Your PHP memory limit is configured in a way that may prevent ' . 'you from uploading large files.'); $message = pht('When you upload a file via drag-and-drop or the API, the entire ' . 'file is buffered into memory before being written to permanent ' . 'storage. Phabricator needs memory available to store these ' . 'files while they are uploaded, but PHP is currently configured ' . 'to limit the available memory.' . "\n\n" . 'Your Phabricator %s is currently set to a larger value (%s) than ' . 'the amount of available memory (%s) that a PHP process has ' . 'available to use, so uploads via drag-and-drop and the API will ' . 'hit the memory limit before they hit other limits.' . "\n\n" . '(Note that the application itself must also fit in available ' . 'memory, so not all of the memory under the memory limit is ' . 'available for buffering file uploads.)' . "\n\n" . "The easiest way to resolve this issue is to set %s to %s in your " . "PHP configuration, to disable the memory limit. There is " . "usually little or no value to using this option to limit " . "Phabricator process memory." . "\n\n" . "You can also increase the limit, or decrease %s, or ignore this " . "issue and accept that these upload mechanisms will be limited " . "in the size of files they can handle.", phutil_tag('tt', array(), 'storage.upload-size-limit'), phutil_format_bytes($upload_limit_bytes), phutil_format_bytes($available_bytes), phutil_tag('tt', array(), 'memory_limit'), phutil_tag('tt', array(), '-1'), phutil_tag('tt', array(), 'storage.upload-size-limit')); $this->newIssue('php.memory_limit.upload')->setName(pht('Memory Limit Restricts File Uploads'))->setSummary($summary)->setMessage($message)->addPHPConfig('memory_limit')->addPHPConfigOriginalValue('memory_limit', $memory_limit)->addPhabricatorConfig('storage.upload-size-limit'); } } } $local_path = PhabricatorEnv::getEnvConfig('storage.local-disk.path'); if (!$local_path) { return; } if (!Filesystem::pathExists($local_path) || !is_readable($local_path) || !is_writable($local_path)) { $message = pht('Configured location for storing uploaded files on disk ("%s") does ' . 'not exist, or is not readable or writable. Verify the directory ' . 'exists and is readable and writable by the webserver.', $local_path); $this->newIssue('config.storage.local-disk.path')->setShortName(pht('Local Disk Storage'))->setName(pht('Local Disk Storage Not Readable/Writable'))->setMessage($message)->addPhabricatorConfig('storage.local-disk.path'); } }
private function renderUploadLimit() { $limit = PhabricatorEnv::getEnvConfig('storage.upload-size-limit'); $limit = phutil_parse_bytes($limit); if ($limit) { $formatted = phutil_format_bytes($limit); return 'Maximum file size: ' . $formatted; } $doc_href = PhabricatorEnv::getDocLink('Configuring File Upload Limits'); $doc_link = phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), 'Configuring File Upload Limits'); return hsprintf('Upload limit is not configured, see %s.', $doc_link); }
/** * @phutil-external-symbol class PhabricatorStartup */ protected function executeChecks() { $engines = PhabricatorFileStorageEngine::loadWritableChunkEngines(); $chunk_engine_active = (bool) $engines; $this->checkS3(); if (!$chunk_engine_active) { $doc_href = PhabricatorEnv::getDocLink('Configuring File Storage'); $message = pht('Large file storage has not been configured, which will limit ' . 'the maximum size of file uploads. See %s for ' . 'instructions on configuring uploads and storage.', phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Configuring File Storage'))); $this->newIssue('large-files')->setShortName(pht('Large Files'))->setName(pht('Large File Storage Not Configured'))->setMessage($message); } $post_max_size = ini_get('post_max_size'); if ($post_max_size && (int) $post_max_size > 0) { $post_max_bytes = phutil_parse_bytes($post_max_size); $post_max_need = 32 * 1024 * 1024; if ($post_max_need > $post_max_bytes) { $summary = pht('Set %s in your PHP configuration to at least 32MB ' . 'to support large file uploads.', phutil_tag('tt', array(), 'post_max_size')); $message = pht('Adjust %s in your PHP configuration to at least 32MB. When ' . 'set to smaller value, large file uploads may not work properly.', phutil_tag('tt', array(), 'post_max_size')); $this->newIssue('php.post_max_size')->setName(pht('PHP post_max_size Not Configured'))->setSummary($summary)->setMessage($message)->setGroup(self::GROUP_PHP)->addPHPConfig('post_max_size'); } } // This is somewhat arbitrary, but make sure we have enough headroom to // upload a default file at the chunk threshold (8MB), which may be // base64 encoded, then JSON encoded in the request, and may need to be // held in memory in the raw and as a query string. $need_bytes = 64 * 1024 * 1024; $memory_limit = PhabricatorStartup::getOldMemoryLimit(); if ($memory_limit && (int) $memory_limit > 0) { $memory_limit_bytes = phutil_parse_bytes($memory_limit); $memory_usage_bytes = memory_get_usage(); $available_bytes = $memory_limit_bytes - $memory_usage_bytes; if ($need_bytes > $available_bytes) { $summary = pht('Your PHP memory limit is configured in a way that may prevent ' . 'you from uploading large files or handling large requests.'); $message = pht('When you upload a file via drag-and-drop or the API, chunks must ' . 'be buffered into memory before being written to permanent ' . 'storage. Phabricator needs memory available to store these ' . 'chunks while they are uploaded, but PHP is currently configured ' . 'to severly limit the available memory.' . "\n\n" . 'PHP processes currently have very little free memory available ' . '(%s). To work well, processes should have at least %s.' . "\n\n" . '(Note that the application itself must also fit in available ' . 'memory, so not all of the memory under the memory limit is ' . 'available for running workloads.)' . "\n\n" . "The easiest way to resolve this issue is to set %s to %s in your " . "PHP configuration, to disable the memory limit. There is " . "usually little or no value to using this option to limit " . "Phabricator process memory." . "\n\n" . "You can also increase the limit or ignore this issue and accept " . "that you may encounter problems uploading large files and " . "processing large requests.", phutil_format_bytes($available_bytes), phutil_format_bytes($need_bytes), phutil_tag('tt', array(), 'memory_limit'), phutil_tag('tt', array(), '-1')); $this->newIssue('php.memory_limit.upload')->setName(pht('Memory Limit Restricts File Uploads'))->setSummary($summary)->setMessage($message)->setGroup(self::GROUP_PHP)->addPHPConfig('memory_limit')->addPHPConfigOriginalValue('memory_limit', $memory_limit); } } $local_path = PhabricatorEnv::getEnvConfig('storage.local-disk.path'); if (!$local_path) { return; } if (!Filesystem::pathExists($local_path) || !is_readable($local_path) || !is_writable($local_path)) { $message = pht('Configured location for storing uploaded files on disk ("%s") does ' . 'not exist, or is not readable or writable. Verify the directory ' . 'exists and is readable and writable by the webserver.', $local_path); $this->newIssue('config.storage.local-disk.path')->setShortName(pht('Local Disk Storage'))->setName(pht('Local Disk Storage Not Readable/Writable'))->setMessage($message)->addPhabricatorConfig('storage.local-disk.path'); } }
private function loadRawPatchText(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit) { $drequest = DiffusionRequest::newFromDictionary(array('user' => PhabricatorUser::getOmnipotentUser(), 'repository' => $repository, 'commit' => $commit->getCommitIdentifier())); $raw_query = DiffusionRawDiffQuery::newFromDiffusionRequest($drequest); $raw_query->setLinesOfContext(3); $time_key = 'metamta.diffusion.time-limit'; $byte_key = 'metamta.diffusion.byte-limit'; $time_limit = PhabricatorEnv::getEnvConfig($time_key); $byte_limit = PhabricatorEnv::getEnvConfig($byte_key); if ($time_limit) { $raw_query->setTimeout($time_limit); } $raw_diff = $raw_query->loadRawDiff(); $size = strlen($raw_diff); if ($byte_limit && $size > $byte_limit) { $pretty_size = phutil_format_bytes($size); $pretty_limit = phutil_format_bytes($byte_limit); throw new Exception(pht('Patch size of %s exceeds configured byte size limit (%s) of %s.', $pretty_size, $byte_key, $pretty_limit)); } return $raw_diff; }
public function buildConfigurationPagePanel() { $viewer = $this->getViewer(); $application = $this->getApplication(); $engines = PhabricatorFileStorageEngine::loadAllEngines(); $writable_engines = PhabricatorFileStorageEngine::loadWritableEngines(); $chunk_engines = PhabricatorFileStorageEngine::loadWritableChunkEngines(); $yes = pht('Yes'); $no = pht('No'); $rows = array(); $rowc = array(); foreach ($engines as $key => $engine) { $limited = $no; $limit = null; if ($engine->hasFilesizeLimit()) { $limited = $yes; $limit = phutil_format_bytes($engine->getFilesizeLimit()); } if ($engine->canWriteFiles()) { $writable = $yes; } else { $writable = $no; } if ($engine->isTestEngine()) { $test = $yes; } else { $test = $no; } if (isset($writable_engines[$key]) || isset($chunk_engines[$key])) { $rowc[] = 'highlighted'; } else { $rowc[] = null; } $rows[] = array($key, get_class($engine), $test, $writable, $limited, $limit); } $table = id(new AphrontTableView($rows))->setNoDataString(pht('No storage engines available.'))->setHeaders(array(pht('Key'), pht('Class'), pht('Unit Test'), pht('Writable'), pht('Has Limit'), pht('Limit')))->setRowClasses($rowc)->setColumnClasses(array('', 'wide', '', '', '', 'n')); $box = id(new PHUIObjectBoxView())->setHeaderText(pht('Storage Engines'))->setTable($table); return $box; }
protected function getChangesetProperties($changeset) { $old = $changeset->getOldProperties(); $new = $changeset->getNewProperties(); // When adding files, don't show the uninteresting 644 filemode change. if ($changeset->getChangeType() == DifferentialChangeType::TYPE_ADD && $new == array('unix:filemode' => '100644')) { unset($new['unix:filemode']); } // Likewise when removing files. if ($changeset->getChangeType() == DifferentialChangeType::TYPE_DELETE && $old == array('unix:filemode' => '100644')) { unset($old['unix:filemode']); } if ($this->hasOldFile()) { $file = $this->getOldFile(); if ($file->getImageWidth()) { $dimensions = $file->getImageWidth() . 'x' . $file->getImageHeight(); $old['file:dimensions'] = $dimensions; } $old['file:mimetype'] = $file->getMimeType(); $old['file:size'] = phutil_format_bytes($file->getByteSize()); } if ($this->hasNewFile()) { $file = $this->getNewFile(); if ($file->getImageWidth()) { $dimensions = $file->getImageWidth() . 'x' . $file->getImageHeight(); $new['file:dimensions'] = $dimensions; } $new['file:mimetype'] = $file->getMimeType(); $new['file:size'] = phutil_format_bytes($file->getByteSize()); } return array($old, $new); }
public function execute(PhutilArgumentParser $args) { $target_key = $args->getArg('engine'); if (!$target_key) { throw new PhutilArgumentUsageException(pht('Specify an engine to migrate to with `%s`. ' . 'Use `%s` to get a list of engines.', '--engine', 'files engines')); } $target_engine = PhabricatorFile::buildEngine($target_key); $iterator = $this->buildIterator($args); if (!$iterator) { throw new PhutilArgumentUsageException(pht('Either specify a list of files to migrate, or use `%s` ' . 'to migrate all files.', '--all')); } $is_dry_run = $args->getArg('dry-run'); $min_size = (int) $args->getArg('min-size'); $max_size = (int) $args->getArg('max-size'); $is_copy = $args->getArg('copy'); $failed = array(); $engines = PhabricatorFileStorageEngine::loadAllEngines(); $total_bytes = 0; $total_files = 0; foreach ($iterator as $file) { $monogram = $file->getMonogram(); $engine_key = $file->getStorageEngine(); $engine = idx($engines, $engine_key); if (!$engine) { echo tsprintf("%s\n", pht('%s: Uses unknown storage engine "%s".', $monogram, $engine_key)); $failed[] = $file; continue; } if ($engine->isChunkEngine()) { echo tsprintf("%s\n", pht('%s: Stored as chunks, no data to migrate directly.', $monogram)); continue; } if ($engine_key === $target_key) { echo tsprintf("%s\n", pht('%s: Already stored in engine "%s".', $monogram, $target_key)); continue; } $byte_size = $file->getByteSize(); if ($min_size && $byte_size < $min_size) { echo tsprintf("%s\n", pht('%s: File size (%s) is smaller than minimum size (%s).', $monogram, phutil_format_bytes($byte_size), phutil_format_bytes($min_size))); continue; } if ($max_size && $byte_size > $max_size) { echo tsprintf("%s\n", pht('%s: File size (%s) is larger than maximum size (%s).', $monogram, phutil_format_bytes($byte_size), phutil_format_bytes($max_size))); continue; } if ($is_dry_run) { echo tsprintf("%s\n", pht('%s: (%s) Would migrate from "%s" to "%s" (dry run)...', $monogram, phutil_format_bytes($byte_size), $engine_key, $target_key)); } else { echo tsprintf("%s\n", pht('%s: (%s) Migrating from "%s" to "%s"...', $monogram, phutil_format_bytes($byte_size), $engine_key, $target_key)); } try { if ($is_dry_run) { // Do nothing, this is a dry run. } else { $file->migrateToEngine($target_engine, $is_copy); } $total_files += 1; $total_bytes += $byte_size; echo tsprintf("%s\n", pht('Done.')); } catch (Exception $ex) { echo tsprintf("%s\n", pht('Failed! %s', (string) $ex)); $failed[] = $file; throw $ex; } } echo tsprintf("%s\n", pht('Total Migrated Files: %s', new PhutilNumber($total_files))); echo tsprintf("%s\n", pht('Total Migrated Bytes: %s', phutil_format_bytes($total_bytes))); if ($is_dry_run) { echo tsprintf("%s\n", pht('This was a dry run, so no real migrations were performed.')); } if ($failed) { $monograms = mpull($failed, 'getMonogram'); echo tsprintf("%s\n", pht('Failures: %s.', implode(', ', $monograms))); return 1; } return 0; }
private function buildCorpus($show_blame, $show_color, $file_corpus, $needs_blame, DiffusionRequest $drequest, $path, $data) { $viewer = $this->getViewer(); $blame_timeout = 15; $blame_failed = false; $highlight_limit = DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT; $blame_limit = DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT; $can_highlight = strlen($file_corpus) <= $highlight_limit; $can_blame = strlen($file_corpus) <= $blame_limit; if ($needs_blame && $can_blame) { $blame = $this->loadBlame($path, $drequest->getCommit(), $blame_timeout); list($blame_list, $blame_commits) = $blame; if ($blame_list === null) { $blame_failed = true; $blame_list = array(); } } else { $blame_list = array(); $blame_commits = array(); } if (!$show_color) { $corpus = $this->renderPlaintextCorpus($file_corpus, $blame_list, $blame_commits, $show_blame); } else { if ($can_highlight) { require_celerity_resource('syntax-highlighting-css'); $highlighted = PhabricatorSyntaxHighlighter::highlightWithFilename($path, $file_corpus); $lines = phutil_split_lines($highlighted); } else { $lines = phutil_split_lines($file_corpus); } $rows = $this->buildDisplayRows($lines, $blame_list, $blame_commits, $show_blame, $show_color); $corpus_table = javelin_tag('table', array('class' => 'diffusion-source remarkup-code PhabricatorMonospaced', 'sigil' => 'phabricator-source'), $rows); if ($this->getRequest()->isAjax()) { return $corpus_table; } $id = celerity_generate_unique_node_id(); $repo = $drequest->getRepository(); $symbol_repos = nonempty($repo->getSymbolSources(), array()); $symbol_repos[] = $repo->getPHID(); $lang = last(explode('.', $drequest->getPath())); $repo_languages = $repo->getSymbolLanguages(); $repo_languages = nonempty($repo_languages, array()); $repo_languages = array_fill_keys($repo_languages, true); $needs_symbols = true; if ($repo_languages && $symbol_repos) { $have_symbols = id(new DiffusionSymbolQuery())->existsSymbolsInRepository($repo->getPHID()); if (!$have_symbols) { $needs_symbols = false; } } if ($needs_symbols && $repo_languages) { $needs_symbols = isset($repo_languages[$lang]); } if ($needs_symbols) { Javelin::initBehavior('repository-crossreference', array('container' => $id, 'lang' => $lang, 'repositories' => $symbol_repos)); } $corpus = phutil_tag('div', array('id' => $id), $corpus_table); Javelin::initBehavior('load-blame', array('id' => $id)); } $edit = $this->renderEditButton(); $file = $this->renderFileButton(); $header = id(new PHUIHeaderView())->setHeader(pht('File Contents'))->addActionLink($edit)->addActionLink($file); $corpus = id(new PHUIObjectBoxView())->setHeader($header)->appendChild($corpus)->setCollapsed(true); $messages = array(); if (!$can_highlight) { $messages[] = pht('This file is larger than %s, so syntax highlighting is disabled ' . 'by default.', phutil_format_bytes($highlight_limit)); } if ($show_blame && !$can_blame) { $messages[] = pht('This file is larger than %s, so blame is disabled.', phutil_format_bytes($blame_limit)); } if ($blame_failed) { $messages[] = pht('Failed to load blame information for this file in %s second(s).', new PhutilNumber($blame_timeout)); } if ($messages) { $corpus->setInfoView(id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_WARNING)->setErrors($messages)); } return $corpus; }
private function buildPropertyViews(PHUIObjectBoxView $box, PhabricatorFile $file, PhabricatorActionListView $actions) { $request = $this->getRequest(); $user = $request->getUser(); $properties = id(new PHUIPropertyListView()); $properties->setActionList($actions); $box->addPropertyList($properties, pht('Details')); if ($file->getAuthorPHID()) { $properties->addProperty(pht('Author'), $this->getHandle($file->getAuthorPHID())->renderLink()); } $properties->addProperty(pht('Created'), phabricator_datetime($file->getDateCreated(), $user)); $finfo = id(new PHUIPropertyListView()); $box->addPropertyList($finfo, pht('File Info')); $finfo->addProperty(pht('Size'), phutil_format_bytes($file->getByteSize())); $finfo->addProperty(pht('Mime Type'), $file->getMimeType()); $width = $file->getImageWidth(); if ($width) { $finfo->addProperty(pht('Width'), pht('%s px', new PhutilNumber($width))); } $height = $file->getImageHeight(); if ($height) { $finfo->addProperty(pht('Height'), pht('%s px', new PhutilNumber($height))); } $is_image = $file->isViewableImage(); if ($is_image) { $image_string = pht('Yes'); $cache_string = $file->getCanCDN() ? pht('Yes') : pht('No'); } else { $image_string = pht('No'); $cache_string = pht('Not Applicable'); } $finfo->addProperty(pht('Viewable Image'), $image_string); $finfo->addProperty(pht('Cacheable'), $cache_string); $storage_properties = new PHUIPropertyListView(); $box->addPropertyList($storage_properties, pht('Storage')); $storage_properties->addProperty(pht('Engine'), $file->getStorageEngine()); $storage_properties->addProperty(pht('Format'), $file->getStorageFormat()); $storage_properties->addProperty(pht('Handle'), $file->getStorageHandle()); $phids = $file->getObjectPHIDs(); if ($phids) { $attached = new PHUIPropertyListView(); $box->addPropertyList($attached, pht('Attached')); $attached->addProperty(pht('Attached To'), $this->renderHandlesForPHIDs($phids)); } if ($file->isViewableImage()) { $image = phutil_tag('img', array('src' => $file->getViewURI(), 'class' => 'phui-property-list-image')); $linked_image = phutil_tag('a', array('href' => $file->getViewURI()), $image); $media = id(new PHUIPropertyListView())->addImageContent($linked_image); $box->addPropertyList($media); } else { if ($file->isAudio()) { $audio = phutil_tag('audio', array('controls' => 'controls', 'class' => 'phui-property-list-audio'), phutil_tag('source', array('src' => $file->getViewURI(), 'type' => $file->getMimeType()))); $media = id(new PHUIPropertyListView())->addImageContent($audio); $box->addPropertyList($media); } } }
protected function executeChecks() { $max_allowed_packet = self::loadRawConfigValue('max_allowed_packet'); // This primarily supports setting the filesize limit for MySQL to 8MB, // which may produce a >16MB packet after escaping. $recommended_minimum = 32 * 1024 * 1024; if ($max_allowed_packet < $recommended_minimum) { $message = pht("MySQL is configured with a small '%s' (%d), " . "which may cause some large writes to fail. Strongly consider raising " . "this to at least %d in your MySQL configuration.", 'max_allowed_packet', $max_allowed_packet, $recommended_minimum); $this->newIssue('mysql.max_allowed_packet')->setName(pht('Small MySQL "%s"', 'max_allowed_packet'))->setMessage($message)->addMySQLConfig('max_allowed_packet'); } $modes = self::loadRawConfigValue('sql_mode'); $modes = explode(',', $modes); if (!in_array('STRICT_ALL_TABLES', $modes)) { $summary = pht('MySQL is not in strict mode, but using strict mode is strongly ' . 'encouraged.'); $message = pht("On your MySQL instance, the global %s is not set to %s. " . "It is strongly encouraged that you enable this mode when running " . "Phabricator.\n\n" . "By default MySQL will silently ignore some types of errors, which " . "can cause data loss and raise security concerns. Enabling strict " . "mode makes MySQL raise an explicit error instead, and prevents this " . "entire class of problems from doing any damage.\n\n" . "You can find more information about this mode (and how to configure " . "it) in the MySQL manual. Usually, it is sufficient to add this to " . "your %s file (in the %s section) and then restart %s:\n\n" . "%s\n" . "(Note that if you run other applications against the same database, " . "they may not work in strict mode. Be careful about enabling it in " . "these cases.)", phutil_tag('tt', array(), 'sql_mode'), phutil_tag('tt', array(), 'STRICT_ALL_TABLES'), phutil_tag('tt', array(), 'my.cnf'), phutil_tag('tt', array(), '[mysqld]'), phutil_tag('tt', array(), 'mysqld'), phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES')); $this->newIssue('mysql.mode')->setName(pht('MySQL %s Mode Not Set', 'STRICT_ALL_TABLES'))->setSummary($summary)->setMessage($message)->addMySQLConfig('sql_mode'); } if (in_array('ONLY_FULL_GROUP_BY', $modes)) { $summary = pht('MySQL is in ONLY_FULL_GROUP_BY mode, but using this mode is strongly ' . 'discouraged.'); $message = pht("On your MySQL instance, the global %s is set to %s. " . "It is strongly encouraged that you disable this mode when running " . "Phabricator.\n\n" . "With %s enabled, MySQL rejects queries for which the select list " . "or (as of MySQL 5.0.23) %s list refer to nonaggregated columns " . "that are not named in the %s clause. More importantly, Phabricator " . "does not work properly with this mode enabled.\n\n" . "You can find more information about this mode (and how to configure " . "it) in the MySQL manual. Usually, it is sufficient to change the %s " . "in your %s file (in the %s section) and then restart %s:\n\n" . "%s\n" . "(Note that if you run other applications against the same database, " . "they may not work with %s. Be careful about enabling " . "it in these cases and consider migrating Phabricator to a different " . "database.)", phutil_tag('tt', array(), 'sql_mode'), phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY'), phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY'), phutil_tag('tt', array(), 'HAVING'), phutil_tag('tt', array(), 'GROUP BY'), phutil_tag('tt', array(), 'sql_mode'), phutil_tag('tt', array(), 'my.cnf'), phutil_tag('tt', array(), '[mysqld]'), phutil_tag('tt', array(), 'mysqld'), phutil_tag('pre', array(), 'sql_mode=STRICT_ALL_TABLES'), phutil_tag('tt', array(), 'ONLY_FULL_GROUP_BY')); $this->newIssue('mysql.mode')->setName(pht('MySQL %s Mode Set', 'ONLY_FULL_GROUP_BY'))->setSummary($summary)->setMessage($message)->addMySQLConfig('sql_mode'); } $stopword_file = self::loadRawConfigValue('ft_stopword_file'); if ($this->shouldUseMySQLSearchEngine()) { if ($stopword_file === null) { $summary = pht('Your version of MySQL does not support configuration of a ' . 'stopword file. You will not be able to find search results for ' . 'common words.'); $message = pht("Your MySQL instance does not support the %s option. You will not " . "be able to find search results for common words. You can gain " . "access to this option by upgrading MySQL to a more recent " . "version.\n\n" . "You can ignore this warning if you plan to configure ElasticSearch " . "later, or aren't concerned about searching for common words.", phutil_tag('tt', array(), 'ft_stopword_file')); $this->newIssue('mysql.ft_stopword_file')->setName(pht('MySQL %s Not Supported', 'ft_stopword_file'))->setSummary($summary)->setMessage($message)->addMySQLConfig('ft_stopword_file'); } else { if ($stopword_file == '(built-in)') { $root = dirname(phutil_get_library_root('phabricator')); $stopword_path = $root . '/resources/sql/stopwords.txt'; $stopword_path = Filesystem::resolvePath($stopword_path); $namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace'); $summary = pht('MySQL is using a default stopword file, which will prevent ' . 'searching for many common words.'); $message = pht("Your MySQL instance is using the builtin stopword file for " . "building search indexes. This can make Phabricator's search " . "feature less useful.\n\n" . "Stopwords are common words which are not indexed and thus can not " . "be searched for. The default stopword file has about 500 words, " . "including various words which you are likely to wish to search " . "for, such as 'various', 'likely', 'wish', and 'zero'.\n\n" . "To make search more useful, you can use an alternate stopword " . "file with fewer words. Alternatively, if you aren't concerned " . "about searching for common words, you can ignore this warning. " . "If you later plan to configure ElasticSearch, you can also ignore " . "this warning: this stopword file only affects MySQL fulltext " . "indexes.\n\n" . "To choose a different stopword file, add this to your %s file " . "(in the %s section) and then restart %s:\n\n" . "%s\n" . "(You can also use a different file if you prefer. The file " . "suggested above has about 50 of the most common English words.)\n\n" . "Finally, run this command to rebuild indexes using the new " . "rules:\n\n" . "%s", phutil_tag('tt', array(), 'my.cnf'), phutil_tag('tt', array(), '[mysqld]'), phutil_tag('tt', array(), 'mysqld'), phutil_tag('pre', array(), 'ft_stopword_file=' . $stopword_path), phutil_tag('pre', array(), "mysql> REPAIR TABLE {$namespace}_search.search_documentfield;")); $this->newIssue('mysql.ft_stopword_file')->setName(pht('MySQL is Using Default Stopword File'))->setSummary($summary)->setMessage($message)->addMySQLConfig('ft_stopword_file'); } } } $min_len = self::loadRawConfigValue('ft_min_word_len'); if ($min_len >= 4) { if ($this->shouldUseMySQLSearchEngine()) { $namespace = PhabricatorEnv::getEnvConfig('storage.default-namespace'); $summary = pht('MySQL is configured to only index words with at least %d ' . 'characters.', $min_len); $message = pht("Your MySQL instance is configured to use the default minimum word " . "length when building search indexes, which is 4. This means words " . "which are only 3 characters long will not be indexed and can not " . "be searched for.\n\n" . "For example, you will not be able to find search results for words " . "like 'SMS', 'web', or 'DOS'.\n\n" . "You can change this setting to 3 to allow these words to be " . "indexed. Alternatively, you can ignore this warning if you are " . "not concerned about searching for 3-letter words. If you later " . "plan to configure ElasticSearch, you can also ignore this warning: " . "only MySQL fulltext search is affected.\n\n" . "To reduce the minimum word length to 3, add this to your %s file " . "(in the %s section) and then restart %s:\n\n" . "%s\n" . "Finally, run this command to rebuild indexes using the new " . "rules:\n\n" . "%s", phutil_tag('tt', array(), 'my.cnf'), phutil_tag('tt', array(), '[mysqld]'), phutil_tag('tt', array(), 'mysqld'), phutil_tag('pre', array(), 'ft_min_word_len=3'), phutil_tag('pre', array(), "mysql> REPAIR TABLE {$namespace}_search.search_documentfield;")); $this->newIssue('mysql.ft_min_word_len')->setName(pht('MySQL is Using Default Minimum Word Length'))->setSummary($summary)->setMessage($message)->addMySQLConfig('ft_min_word_len'); } } $bool_syntax = self::loadRawConfigValue('ft_boolean_syntax'); if ($bool_syntax != ' |-><()~*:""&^') { if ($this->shouldUseMySQLSearchEngine()) { $summary = pht('MySQL is configured to search on fulltext indexes using "OR" by ' . 'default. Using "AND" is usually the desired behaviour.'); $message = pht("Your MySQL instance is configured to use the default Boolean " . "search syntax when using fulltext indexes. This means searching " . "for 'search words' will yield the query 'search OR words' " . "instead of the desired 'search AND words'.\n\n" . "This might produce unexpected search results. \n\n" . "You can change this setting to a more sensible default. " . "Alternatively, you can ignore this warning if " . "using 'OR' is the desired behaviour. If you later plan " . "to configure ElasticSearch, you can also ignore this warning: " . "only MySQL fulltext search is affected.\n\n" . "To change this setting, add this to your %s file " . "(in the %s section) and then restart %s:\n\n" . "%s\n", phutil_tag('tt', array(), 'my.cnf'), phutil_tag('tt', array(), '[mysqld]'), phutil_tag('tt', array(), 'mysqld'), phutil_tag('pre', array(), 'ft_boolean_syntax=\' |-><()~*:""&^\'')); $this->newIssue('mysql.ft_boolean_syntax')->setName(pht('MySQL is Using the Default Boolean Syntax'))->setSummary($summary)->setMessage($message)->addMySQLConfig('ft_boolean_syntax'); } } $innodb_pool = self::loadRawConfigValue('innodb_buffer_pool_size'); $innodb_bytes = phutil_parse_bytes($innodb_pool); $innodb_readable = phutil_format_bytes($innodb_bytes); // This is arbitrary and just trying to detect values that the user // probably didn't set themselves. The Mac OS X default is 128MB and // 40% of an AWS EC2 Micro instance is 245MB, so keeping it somewhere // between those two values seems like a reasonable approximation. $minimum_readable = '225MB'; $minimum_bytes = phutil_parse_bytes($minimum_readable); if ($innodb_bytes < $minimum_bytes) { $summary = pht('MySQL is configured with a very small innodb_buffer_pool_size, ' . 'which may impact performance.'); $message = pht("Your MySQL instance is configured with a very small %s (%s). " . "This may cause poor database performance and lock exhaustion.\n\n" . "There are no hard-and-fast rules to setting an appropriate value, " . "but a reasonable starting point for a standard install is something " . "like 40%% of the total memory on the machine. For example, if you " . "have 4GB of RAM on the machine you have installed Phabricator on, " . "you might set this value to %s.\n\n" . "You can read more about this option in the MySQL documentation to " . "help you make a decision about how to configure it for your use " . "case. There are no concerns specific to Phabricator which make it " . "different from normal workloads with respect to this setting.\n\n" . "To adjust the setting, add something like this to your %s file (in " . "the %s section), replacing %s with an appropriate value for your " . "host and use case. Then restart %s:\n\n" . "%s\n" . "If you're satisfied with the current setting, you can safely " . "ignore this setup warning.", phutil_tag('tt', array(), 'innodb_buffer_pool_size'), phutil_tag('tt', array(), $innodb_readable), phutil_tag('tt', array(), '1600M'), phutil_tag('tt', array(), 'my.cnf'), phutil_tag('tt', array(), '[mysqld]'), phutil_tag('tt', array(), '1600M'), phutil_tag('tt', array(), 'mysqld'), phutil_tag('pre', array(), 'innodb_buffer_pool_size=1600M')); $this->newIssue('mysql.innodb_buffer_pool_size')->setName(pht('MySQL May Run Slowly'))->setSummary($summary)->setMessage($message)->addMySQLConfig('innodb_buffer_pool_size'); } $conn_w = id(new PhabricatorUser())->establishConnection('w'); $ok = PhabricatorStorageManagementAPI::isCharacterSetAvailableOnConnection('utf8mb4', $conn_w); if (!$ok) { $summary = pht('You are using an old version of MySQL, and should upgrade.'); $message = pht('You are using an old version of MySQL which has poor unicode ' . 'support (it does not support the "utf8mb4" collation set). You will ' . 'encounter limitations when working with some unicode data.' . "\n\n" . 'We strongly recommend you upgrade to MySQL 5.5 or newer.'); $this->newIssue('mysql.utf8mb4')->setName(pht('Old MySQL Version'))->setSummary($summary)->setMessage($message); } $info = queryfx_one($conn_w, 'SELECT UNIX_TIMESTAMP() epoch'); $epoch = (int) $info['epoch']; $local = PhabricatorTime::getNow(); $delta = (int) abs($local - $epoch); if ($delta > 60) { $this->newIssue('mysql.clock')->setName(pht('Major Web/Database Clock Skew'))->setSummary(pht('This host is set to a very different time than the database.'))->setMessage(pht('The database host and this host ("%s") disagree on the current ' . 'time by more than 60 seconds (absolute skew is %s seconds). ' . 'Check that the current time is set correctly everywhere.', php_uname('n'), new PhutilNumber($delta))); } }
private function buildGitLFSCorpus(PhabricatorRepositoryGitLFSRef $ref) { // TODO: We should probably test if we can load the file PHID here and // show the user an error if we can't, rather than making them click // through to hit an error. $header = id(new PHUIHeaderView())->setHeader(basename($this->getDiffusionRequest()->getPath()))->setHeaderIcon('fa-archive'); $severity = PHUIInfoView::SEVERITY_NOTICE; $messages = array(); $messages[] = pht('This %s file is stored in Git Large File Storage.', phutil_format_bytes($ref->getByteSize())); try { $file = $this->loadGitLFSFile($ref); $data = $this->renderGitLFSButton(); $header->addActionLink($data); } catch (Exception $ex) { $severity = PHUIInfoView::SEVERITY_ERROR; $messages[] = pht('The data for this file could not be loaded.'); } $raw = $this->renderFileButton(null, pht('View Raw LFS Pointer')); $header->addActionLink($raw); $corpus = id(new PHUIObjectBoxView())->setHeader($header)->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)->setCollapsed(true); if ($messages) { $corpus->setInfoView(id(new PHUIInfoView())->setSeverity($severity)->setErrors($messages)); } return $corpus; }
private function buildPropertyViews(PHUIObjectBoxView $box, PhabricatorFile $file, PhabricatorActionListView $actions) { $request = $this->getRequest(); $viewer = $request->getUser(); $properties = id(new PHUIPropertyListView()); $properties->setActionList($actions); $box->addPropertyList($properties, pht('Details')); if ($file->getAuthorPHID()) { $properties->addProperty(pht('Author'), $viewer->renderHandle($file->getAuthorPHID())); } $properties->addProperty(pht('Created'), phabricator_datetime($file->getDateCreated(), $viewer)); $finfo = id(new PHUIPropertyListView()); $box->addPropertyList($finfo, pht('File Info')); $finfo->addProperty(pht('Size'), phutil_format_bytes($file->getByteSize())); $finfo->addProperty(pht('Mime Type'), $file->getMimeType()); $width = $file->getImageWidth(); if ($width) { $finfo->addProperty(pht('Width'), pht('%s px', new PhutilNumber($width))); } $height = $file->getImageHeight(); if ($height) { $finfo->addProperty(pht('Height'), pht('%s px', new PhutilNumber($height))); } $is_image = $file->isViewableImage(); if ($is_image) { $image_string = pht('Yes'); $cache_string = $file->getCanCDN() ? pht('Yes') : pht('No'); } else { $image_string = pht('No'); $cache_string = pht('Not Applicable'); } $finfo->addProperty(pht('Viewable Image'), $image_string); $finfo->addProperty(pht('Cacheable'), $cache_string); $builtin = $file->getBuiltinName(); if ($builtin === null) { $builtin_string = pht('No'); } else { $builtin_string = $builtin; } $finfo->addProperty(pht('Builtin'), $builtin_string); $is_profile = $file->getIsProfileImage() ? pht('Yes') : pht('No'); $finfo->addProperty(pht('Profile'), $is_profile); $storage_properties = new PHUIPropertyListView(); $box->addPropertyList($storage_properties, pht('Storage')); $storage_properties->addProperty(pht('Engine'), $file->getStorageEngine()); $storage_properties->addProperty(pht('Format'), $file->getStorageFormat()); $storage_properties->addProperty(pht('Handle'), $file->getStorageHandle()); $phids = $file->getObjectPHIDs(); if ($phids) { $attached = new PHUIPropertyListView(); $box->addPropertyList($attached, pht('Attached')); $attached->addProperty(pht('Attached To'), $viewer->renderHandleList($phids)); } if ($file->isViewableImage()) { $image = phutil_tag('img', array('src' => $file->getViewURI(), 'class' => 'phui-property-list-image')); $linked_image = phutil_tag('a', array('href' => $file->getViewURI()), $image); $media = id(new PHUIPropertyListView())->addImageContent($linked_image); $box->addPropertyList($media); } else { if ($file->isAudio()) { $audio = phutil_tag('audio', array('controls' => 'controls', 'class' => 'phui-property-list-audio'), phutil_tag('source', array('src' => $file->getViewURI(), 'type' => $file->getMimeType()))); $media = id(new PHUIPropertyListView())->addImageContent($audio); $box->addPropertyList($media); } } $engine = null; try { $engine = $file->instantiateStorageEngine(); } catch (Exception $ex) { // Don't bother raising this anywhere for now. } if ($engine) { if ($engine->isChunkEngine()) { $chunkinfo = new PHUIPropertyListView(); $box->addPropertyList($chunkinfo, pht('Chunks')); $chunks = id(new PhabricatorFileChunkQuery())->setViewer($viewer)->withChunkHandles(array($file->getStorageHandle()))->execute(); $chunks = msort($chunks, 'getByteStart'); $rows = array(); $completed = array(); foreach ($chunks as $chunk) { $is_complete = $chunk->getDataFilePHID(); $rows[] = array($chunk->getByteStart(), $chunk->getByteEnd(), $is_complete ? pht('Yes') : pht('No')); if ($is_complete) { $completed[] = $chunk; } } $table = id(new AphrontTableView($rows))->setHeaders(array(pht('Offset'), pht('End'), pht('Complete')))->setColumnClasses(array('', '', 'wide')); $chunkinfo->addProperty(pht('Total Chunks'), count($chunks)); $chunkinfo->addProperty(pht('Completed Chunks'), count($completed)); $chunkinfo->addRawContent($table); } } }
public function getDisplayDescription(PhabricatorUser $viewer, PhabricatorCalendarImportLog $log) { $size = $log->getParameter('data.size'); $limit = $log->getParameter('data.limit'); return pht('Queued for background import: data size (%s) exceeds limit for ' . 'immediate processing (%s).', phutil_format_bytes($size), phutil_format_bytes($limit)); }
private function buildPropertyViews(PHUIObjectBoxView $box, PhabricatorFile $file) { $request = $this->getRequest(); $viewer = $request->getUser(); $tab_group = id(new PHUITabGroupView()); $box->addTabGroup($tab_group); $properties = id(new PHUIPropertyListView()); $tab_group->addTab(id(new PHUITabView())->setName(pht('Details'))->setKey('details')->appendChild($properties)); if ($file->getAuthorPHID()) { $properties->addProperty(pht('Author'), $viewer->renderHandle($file->getAuthorPHID())); } $properties->addProperty(pht('Created'), phabricator_datetime($file->getDateCreated(), $viewer)); $finfo = id(new PHUIPropertyListView()); $tab_group->addTab(id(new PHUITabView())->setName(pht('File Info'))->setKey('info')->appendChild($finfo)); $finfo->addProperty(pht('Size'), phutil_format_bytes($file->getByteSize())); $finfo->addProperty(pht('Mime Type'), $file->getMimeType()); $width = $file->getImageWidth(); if ($width) { $finfo->addProperty(pht('Width'), pht('%s px', new PhutilNumber($width))); } $height = $file->getImageHeight(); if ($height) { $finfo->addProperty(pht('Height'), pht('%s px', new PhutilNumber($height))); } $is_image = $file->isViewableImage(); if ($is_image) { $image_string = pht('Yes'); $cache_string = $file->getCanCDN() ? pht('Yes') : pht('No'); } else { $image_string = pht('No'); $cache_string = pht('Not Applicable'); } $types = array(); if ($file->isViewableImage()) { $types[] = pht('Image'); } if ($file->isVideo()) { $types[] = pht('Video'); } if ($file->isAudio()) { $types[] = pht('Audio'); } if ($file->getCanCDN()) { $types[] = pht('Can CDN'); } $builtin = $file->getBuiltinName(); if ($builtin !== null) { $types[] = pht('Builtin ("%s")', $builtin); } if ($file->getIsProfileImage()) { $types[] = pht('Profile'); } if ($types) { $types = implode(', ', $types); $finfo->addProperty(pht('Attributes'), $types); } $storage_properties = new PHUIPropertyListView(); $tab_group->addTab(id(new PHUITabView())->setName(pht('Storage'))->setKey('storage')->appendChild($storage_properties)); $storage_properties->addProperty(pht('Engine'), $file->getStorageEngine()); $engine = $this->loadStorageEngine($file); if ($engine && $engine->isChunkEngine()) { $format_name = pht('Chunks'); } else { $format_key = $file->getStorageFormat(); $format = PhabricatorFileStorageFormat::getFormat($format_key); if ($format) { $format_name = $format->getStorageFormatName(); } else { $format_name = pht('Unknown ("%s")', $format_key); } } $storage_properties->addProperty(pht('Format'), $format_name); $storage_properties->addProperty(pht('Handle'), $file->getStorageHandle()); $phids = $file->getObjectPHIDs(); if ($phids) { $attached = new PHUIPropertyListView(); $tab_group->addTab(id(new PHUITabView())->setName(pht('Attached'))->setKey('attached')->appendChild($attached)); $attached->addProperty(pht('Attached To'), $viewer->renderHandleList($phids)); } if ($file->isViewableImage()) { $image = phutil_tag('img', array('src' => $file->getViewURI(), 'class' => 'phui-property-list-image')); $linked_image = phutil_tag('a', array('href' => $file->getViewURI()), $image); $media = id(new PHUIPropertyListView())->addImageContent($linked_image); $box->addPropertyList($media); } else { if ($file->isVideo()) { $video = phutil_tag('video', array('controls' => 'controls', 'class' => 'phui-property-list-video'), phutil_tag('source', array('src' => $file->getViewURI(), 'type' => $file->getMimeType()))); $media = id(new PHUIPropertyListView())->addImageContent($video); $box->addPropertyList($media); } else { if ($file->isAudio()) { $audio = phutil_tag('audio', array('controls' => 'controls', 'class' => 'phui-property-list-audio'), phutil_tag('source', array('src' => $file->getViewURI(), 'type' => $file->getMimeType()))); $media = id(new PHUIPropertyListView())->addImageContent($audio); $box->addPropertyList($media); } } } $engine = $this->loadStorageEngine($file); if ($engine) { if ($engine->isChunkEngine()) { $chunkinfo = new PHUIPropertyListView(); $tab_group->addTab(id(new PHUITabView())->setName(pht('Chunks'))->setKey('chunks')->appendChild($chunkinfo)); $chunks = id(new PhabricatorFileChunkQuery())->setViewer($viewer)->withChunkHandles(array($file->getStorageHandle()))->execute(); $chunks = msort($chunks, 'getByteStart'); $rows = array(); $completed = array(); foreach ($chunks as $chunk) { $is_complete = $chunk->getDataFilePHID(); $rows[] = array($chunk->getByteStart(), $chunk->getByteEnd(), $is_complete ? pht('Yes') : pht('No')); if ($is_complete) { $completed[] = $chunk; } } $table = id(new AphrontTableView($rows))->setHeaders(array(pht('Offset'), pht('End'), pht('Complete')))->setColumnClasses(array('', '', 'wide')); $chunkinfo->addProperty(pht('Total Chunks'), count($chunks)); $chunkinfo->addProperty(pht('Completed Chunks'), count($completed)); $chunkinfo->addRawContent($table); } } }
protected function renderResultList(array $files, PhabricatorSavedQuery $query, array $handles) { assert_instances_of($files, 'PhabricatorFile'); $request = $this->getRequest(); if ($request) { $highlighted_ids = $request->getStrList('h'); } else { $highlighted_ids = array(); } $viewer = $this->requireViewer(); $highlighted_ids = array_fill_keys($highlighted_ids, true); $list_view = id(new PHUIObjectItemListView())->setUser($viewer); foreach ($files as $file) { $id = $file->getID(); $phid = $file->getPHID(); $name = $file->getName(); $file_uri = $this->getApplicationURI("/info/{$phid}/"); $date_created = phabricator_date($file->getDateCreated(), $viewer); $author_phid = $file->getAuthorPHID(); if ($author_phid) { $author_link = $handles[$author_phid]->renderLink(); $uploaded = pht('Uploaded by %s on %s', $author_link, $date_created); } else { $uploaded = pht('Uploaded on %s', $date_created); } $item = id(new PHUIObjectItemView())->setObject($file)->setObjectName("F{$id}")->setHeader($name)->setHref($file_uri)->addAttribute($uploaded)->addIcon('none', phutil_format_bytes($file->getByteSize())); $ttl = $file->getTTL(); if ($ttl !== null) { $item->addIcon('blame', pht('Temporary')); } if (isset($highlighted_ids[$id])) { $item->setEffect('highlighted'); } $list_view->addItem($item); } $list_view->appendChild(id(new PhabricatorGlobalUploadTargetView())->setUser($viewer)); return $list_view; }
protected function renderUndershieldHeader() { $messages = array(); $changeset = $this->getChangeset(); $file = $changeset->getFileType(); // If this is a text file with at least one hunk, we may have converted // the text encoding. In this case, show a note. $show_encoding = $file == DifferentialChangeType::FILE_TEXT && $changeset->getHunks(); if ($show_encoding) { $encoding = $this->getOriginalCharacterEncoding(); if ($encoding != 'utf8') { if ($encoding) { $messages[] = pht('This file was converted from %s for display.', phutil_tag('strong', array(), $encoding)); } else { $messages[] = pht('This file uses an unknown character encoding.'); } } } if ($this->getHighlightingDisabled()) { $messages[] = pht('This file is larger than %s, so syntax highlighting is ' . 'disabled by default.', phutil_format_bytes(DifferentialChangesetParser::HIGHLIGHT_BYTE_LIMIT)); } return $this->formatHeaderMessages($messages); }