protected final function executeQuery() { $future = $this->newQueryFuture(); $drequest = $this->getRequest(); $name = basename($drequest->getPath()); $ttl = PhabricatorTime::getNow() + phutil_units('48 hours in seconds'); try { $threshold = PhabricatorFileStorageEngine::getChunkThreshold(); $future->setReadBufferSize($threshold); $source = id(new PhabricatorExecFutureFileUploadSource())->setName($name)->setTTL($ttl)->setViewPolicy(PhabricatorPolicies::POLICY_NOONE)->setExecFuture($future); $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); $file = $source->uploadFile(); unset($unguarded); } catch (CommandException $ex) { if (!$future->getWasKilledByTimeout()) { throw $ex; } $this->didHitTimeLimit = true; $file = null; } $byte_limit = $this->getByteLimit(); if ($byte_limit && $file->getByteSize() > $byte_limit) { $this->didHitByteLimit = true; $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); id(new PhabricatorDestructionEngine())->destroyObject($file); unset($unguarded); $file = null; } return $file; }
public function render() { $viewer = $this->getViewer(); if (!$viewer->isLoggedIn()) { return null; } $instructions_id = 'phabricator-global-drag-and-drop-upload-instructions'; require_celerity_resource('global-drag-and-drop-css'); $hint_text = $this->getHintText(); if (!strlen($hint_text)) { $hint_text = "⇪ " . pht('Drop Files to Upload'); } // Use the configured default view policy. Drag and drop uploads use // a more restrictive view policy if we don't specify a policy explicitly, // as the more restrictive policy is correct for most drop targets (like // Pholio uploads and Remarkup text areas). $view_policy = $this->getViewPolicy(); if ($view_policy === null) { $view_policy = PhabricatorFile::initializeNewFile()->getViewPolicy(); } $submit_uri = $this->getSubmitURI(); $done_uri = '/file/query/authored/'; Javelin::initBehavior('global-drag-and-drop', array('ifSupported' => $this->showIfSupportedID, 'instructions' => $instructions_id, 'uploadURI' => '/file/dropupload/', 'submitURI' => $submit_uri, 'browseURI' => $done_uri, 'viewPolicy' => $view_policy, 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold())); return phutil_tag('div', array('id' => $instructions_id, 'class' => 'phabricator-global-upload-instructions', 'style' => 'display: none;'), $hint_text); }
private function shouldChunkFile() { if ($this->shouldChunk !== null) { return $this->shouldChunk; } $threshold = PhabricatorFileStorageEngine::getChunkThreshold(); if ($threshold === null) { // If there are no chunk engines available, we clearly can't chunk the // file. $this->shouldChunk = false; } else { // If we don't know how large the file is, we're going to read some data // from it until we know whether it's a small file or not. This will give // us enough information to make a decision about chunking. $length = $this->getDataLength(); if ($length === null) { $rope = $this->getRope(); while ($this->readFileData()) { $length = $rope->getByteLength(); if ($length > $threshold) { break; } } } $this->shouldChunk = $length > $threshold; } return $this->shouldChunk; }
protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $hash = $request->getValue('contentHash'); $name = $request->getValue('name'); $view_policy = $request->getValue('viewPolicy'); $length = $request->getValue('contentLength'); $properties = array('name' => $name, 'authorPHID' => $viewer->getPHID(), 'viewPolicy' => $view_policy, 'isExplicitUpload' => true); $ttl = $request->getValue('deleteAfterEpoch'); if ($ttl) { $properties['ttl'] = $ttl; } $file = null; if ($hash) { $file = PhabricatorFile::newFileFromContentHash($hash, $properties); } if ($hash && !$file) { $chunked_hash = PhabricatorChunkedFileStorageEngine::getChunkedHash($viewer, $hash); $file = id(new PhabricatorFileQuery())->setViewer($viewer)->withContentHashes(array($chunked_hash))->executeOne(); } if (strlen($name) && !$hash && !$file) { if ($length > PhabricatorFileStorageEngine::getChunkThreshold()) { // If we don't have a hash, but this file is large enough to store in // chunks and thus may be resumable, try to find a partially uploaded // file by the same author with the same name and same length. This // allows us to resume uploads in Javascript where we can't efficiently // compute file hashes. $file = id(new PhabricatorFileQuery())->setViewer($viewer)->withAuthorPHIDs(array($viewer->getPHID()))->withNames(array($name))->withLengthBetween($length, $length)->withIsPartial(true)->setLimit(1)->executeOne(); } } if ($file) { return array('upload' => (bool) $file->getIsPartial(), 'filePHID' => $file->getPHID()); } // If there are any non-chunk engines which this file can fit into, // just tell the client to upload the file. $engines = PhabricatorFileStorageEngine::loadStorageEngines($length); if ($engines) { return array('upload' => true, 'filePHID' => null); } // Otherwise, this is a large file and we want to perform a chunked // upload if we have a chunk engine available. $chunk_engines = PhabricatorFileStorageEngine::loadWritableChunkEngines(); if ($chunk_engines) { $chunk_properties = $properties; if ($hash) { $chunk_properties += array('chunkedHash' => $chunked_hash); } $chunk_engine = head($chunk_engines); $file = $chunk_engine->allocateChunks($length, $chunk_properties); return array('upload' => true, 'filePHID' => $file->getPHID()); } // None of the storage engines can accept this file. if (PhabricatorFileStorageEngine::loadWritableEngines()) { $error = pht('Unable to upload file: this file is too large for any ' . 'configured storage engine.'); } else { $error = pht('Unable to upload file: the server is not configured with any ' . 'writable storage engines.'); } return array('upload' => false, 'filePHID' => null, 'error' => $error); }
protected function renderInput() { $id = $this->getID(); if (!$id) { $id = celerity_generate_unique_node_id(); $this->setID($id); } $viewer = $this->getUser(); if (!$viewer) { throw new PhutilInvalidStateException('setUser'); } // We need to have this if previews render images, since Ajax can not // currently ship JS or CSS. require_celerity_resource('lightbox-attachment-css'); if (!$this->getDisabled()) { Javelin::initBehavior('aphront-drag-and-drop-textarea', array('target' => $id, 'activatedClass' => 'aphront-textarea-drag-and-drop', 'uri' => '/file/dropupload/', 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold())); } $root_id = celerity_generate_unique_node_id(); $user_datasource = new PhabricatorPeopleDatasource(); $proj_datasource = id(new PhabricatorProjectDatasource())->setParameters(array('autocomplete' => 1)); Javelin::initBehavior('phabricator-remarkup-assist', array('pht' => array('bold text' => pht('bold text'), 'italic text' => pht('italic text'), 'monospaced text' => pht('monospaced text'), 'List Item' => pht('List Item'), 'Quoted Text' => pht('Quoted Text'), 'data' => pht('data'), 'name' => pht('name'), 'URL' => pht('URL')), 'disabled' => $this->getDisabled(), 'rootID' => $root_id, 'autocompleteMap' => (object) array(64 => array('datasourceURI' => $user_datasource->getDatasourceURI(), 'headerIcon' => 'fa-user', 'headerText' => pht('Find User:'******'hintText' => $user_datasource->getPlaceholderText()), 35 => array('datasourceURI' => $proj_datasource->getDatasourceURI(), 'headerIcon' => 'fa-briefcase', 'headerText' => pht('Find Project:'), 'hintText' => $proj_datasource->getPlaceholderText())))); Javelin::initBehavior('phabricator-tooltips', array()); $actions = array('fa-bold' => array('tip' => pht('Bold'), 'nodevice' => true), 'fa-italic' => array('tip' => pht('Italics'), 'nodevice' => true), 'fa-text-width' => array('tip' => pht('Monospaced'), 'nodevice' => true), 'fa-link' => array('tip' => pht('Link'), 'nodevice' => true), array('spacer' => true, 'nodevice' => true), 'fa-list-ul' => array('tip' => pht('Bulleted List'), 'nodevice' => true), 'fa-list-ol' => array('tip' => pht('Numbered List'), 'nodevice' => true), 'fa-code' => array('tip' => pht('Code Block'), 'nodevice' => true), 'fa-quote-right' => array('tip' => pht('Quote'), 'nodevice' => true), 'fa-table' => array('tip' => pht('Table'), 'nodevice' => true), 'fa-cloud-upload' => array('tip' => pht('Upload File'))); $can_use_macros = !$this->disableMacro && function_exists('imagettftext'); if ($can_use_macros) { $can_use_macros = PhabricatorApplication::isClassInstalledForViewer('PhabricatorMacroApplication', $viewer); } if ($can_use_macros) { $actions[] = array('spacer' => true); $actions['fa-meh-o'] = array('tip' => pht('Meme')); } $actions['fa-eye'] = array('tip' => pht('Preview'), 'align' => 'right'); $actions[] = array('spacer' => true, 'align' => 'right'); $actions['fa-life-bouy'] = array('tip' => pht('Help'), 'align' => 'right', 'href' => PhabricatorEnv::getDoclink('Remarkup Reference')); if (!$this->disableFullScreen) { $actions[] = array('spacer' => true, 'align' => 'right'); $actions['fa-arrows-alt'] = array('tip' => pht('Fullscreen Mode'), 'align' => 'right'); } $buttons = array(); foreach ($actions as $action => $spec) { $classes = array(); if (idx($spec, 'align') == 'right') { $classes[] = 'remarkup-assist-right'; } if (idx($spec, 'nodevice')) { $classes[] = 'remarkup-assist-nodevice'; } if (idx($spec, 'spacer')) { $classes[] = 'remarkup-assist-separator'; $buttons[] = phutil_tag('span', array('class' => implode(' ', $classes)), ''); continue; } else { $classes[] = 'remarkup-assist-button'; } $href = idx($spec, 'href', '#'); if ($href == '#') { $meta = array('action' => $action); $mustcapture = true; $target = null; } else { $meta = array(); $mustcapture = null; $target = '_blank'; } $content = null; $tip = idx($spec, 'tip'); if ($tip) { $meta['tip'] = $tip; $content = javelin_tag('span', array('aural' => true), $tip); } $sigils = array(); $sigils[] = 'remarkup-assist'; if (!$this->getDisabled()) { $sigils[] = 'has-tooltip'; } $buttons[] = javelin_tag('a', array('class' => implode(' ', $classes), 'href' => $href, 'sigil' => implode(' ', $sigils), 'meta' => $meta, 'mustcapture' => $mustcapture, 'target' => $target, 'tabindex' => -1), phutil_tag('div', array('class' => 'remarkup-assist phui-icon-view phui-font-fa bluegrey ' . $action), $content)); } $buttons = phutil_tag('div', array('class' => 'remarkup-assist-bar'), $buttons); $use_monospaced = $viewer->compareUserSetting(PhabricatorMonospacedTextareasSetting::SETTINGKEY, PhabricatorMonospacedTextareasSetting::VALUE_TEXT_MONOSPACED); if ($use_monospaced) { $monospaced_textareas_class = 'PhabricatorMonospaced'; } else { $monospaced_textareas_class = null; } $this->setCustomClass('remarkup-assist-textarea ' . $monospaced_textareas_class); return javelin_tag('div', array('sigil' => 'remarkup-assist-control', 'class' => $this->getDisabled() ? 'disabled-control' : null, 'id' => $root_id), array($buttons, parent::renderInput())); }
private function browseFile() { $viewer = $this->getViewer(); $request = $this->getRequest(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $before = $request->getStr('before'); if ($before) { return $this->buildBeforeResponse($before); } $path = $drequest->getPath(); $preferences = $viewer->loadPreferences(); $show_blame = $request->getBool('blame', $preferences->getPreference(PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME, false)); $show_color = $request->getBool('color', $preferences->getPreference(PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR, true)); $view = $request->getStr('view'); if ($request->isFormPost() && $view != 'raw' && $viewer->isLoggedIn()) { $preferences->setPreference(PhabricatorUserPreferences::PREFERENCE_DIFFUSION_BLAME, $show_blame); $preferences->setPreference(PhabricatorUserPreferences::PREFERENCE_DIFFUSION_COLOR, $show_color); $preferences->save(); $uri = $request->getRequestURI()->alter('blame', null)->alter('color', null); return id(new AphrontRedirectResponse())->setURI($uri); } // We need the blame information if blame is on and we're building plain // text, or blame is on and this is an Ajax request. If blame is on and // this is a colorized request, we don't show blame at first (we ajax it // in afterward) so we don't need to query for it. $needs_blame = $show_blame && !$show_color || $show_blame && $request->isAjax(); $params = array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath()); $byte_limit = null; if ($view !== 'raw') { $byte_limit = PhabricatorFileStorageEngine::getChunkThreshold(); $time_limit = 10; $params += array('timeout' => $time_limit, 'byteLimit' => $byte_limit); } $response = $this->callConduitWithDiffusionRequest('diffusion.filecontentquery', $params); $hit_byte_limit = $response['tooHuge']; $hit_time_limit = $response['tooSlow']; $file_phid = $response['filePHID']; if ($hit_byte_limit) { $corpus = $this->buildErrorCorpus(pht('This file is larger than %s byte(s), and too large to display ' . 'in the web UI.', phutil_format_bytes($byte_limit))); } else { if ($hit_time_limit) { $corpus = $this->buildErrorCorpus(pht('This file took too long to load from the repository (more than ' . '%s second(s)).', new PhutilNumber($time_limit))); } else { $file = id(new PhabricatorFileQuery())->setViewer($viewer)->withPHIDs(array($file_phid))->executeOne(); if (!$file) { throw new Exception(pht('Failed to load content file!')); } if ($view === 'raw') { return $file->getRedirectResponse(); } $data = $file->loadFileData(); if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) { $file_uri = $file->getBestURI(); if ($file->isViewableImage()) { $corpus = $this->buildImageCorpus($file_uri); } else { $corpus = $this->buildBinaryCorpus($file_uri, $data); } } else { $this->loadLintMessages(); $this->coverage = $drequest->loadCoverage(); // Build the content of the file. $corpus = $this->buildCorpus($show_blame, $show_color, $data, $needs_blame, $drequest, $path, $data); } } } if ($request->isAjax()) { return id(new AphrontAjaxResponse())->setContent($corpus); } require_celerity_resource('diffusion-source-css'); // Render the page. $view = $this->buildActionView($drequest); $action_list = $this->enrichActionView($view, $drequest, $show_blame, $show_color); $properties = $this->buildPropertyView($drequest, $action_list); $object_box = id(new PHUIObjectBoxView())->setHeader($this->buildHeaderView($drequest))->addPropertyList($properties); $content = array(); $content[] = $object_box; $follow = $request->getStr('follow'); if ($follow) { $notice = new PHUIInfoView(); $notice->setSeverity(PHUIInfoView::SEVERITY_WARNING); $notice->setTitle(pht('Unable to Continue')); switch ($follow) { case 'first': $notice->appendChild(pht('Unable to continue tracing the history of this file because ' . 'this commit is the first commit in the repository.')); break; case 'created': $notice->appendChild(pht('Unable to continue tracing the history of this file because ' . 'this commit created the file.')); break; } $content[] = $notice; } $renamed = $request->getStr('renamed'); if ($renamed) { $notice = new PHUIInfoView(); $notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE); $notice->setTitle(pht('File Renamed')); $notice->appendChild(pht('File history passes through a rename from "%s" to "%s".', $drequest->getPath(), $renamed)); $content[] = $notice; } $content[] = $corpus; $content[] = $this->buildOpenRevisions(); $crumbs = $this->buildCrumbs(array('branch' => true, 'path' => true, 'view' => 'browse')); $basename = basename($this->getDiffusionRequest()->getPath()); return $this->newPage()->setTitle(array($basename, $repository->getDisplayName()))->setCrumbs($crumbs)->appendChild($content); }
protected function renderInput() { $file_id = $this->getID(); Javelin::initBehavior('phui-file-upload', array('fileInputID' => $file_id, 'inputName' => $this->getName(), 'uploadURI' => '/file/dropupload/', 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold())); return phutil_tag('input', array('type' => 'file', 'multiple' => $this->getAllowMultiple() ? 'multiple' : null, 'name' => $this->getName() . '.raw', 'id' => $file_id, 'disabled' => $this->getDisabled() ? 'disabled' : null)); }
public function handleRequest(AphrontRequest $request) { $viewer = $request->getUser(); $response = $this->loadProject(); if ($response) { return $response; } $project = $this->getProject(); $this->readRequestState(); $board_uri = $this->getApplicationURI('board/' . $project->getID() . '/'); $search_engine = id(new ManiphestTaskSearchEngine())->setViewer($viewer)->setBaseURI($board_uri)->setIsBoardView(true); if ($request->isFormPost() && !$request->getBool('initialize')) { $saved = $search_engine->buildSavedQueryFromRequest($request); $search_engine->saveQuery($saved); $filter_form = id(new AphrontFormView())->setUser($viewer); $search_engine->buildSearchForm($filter_form, $saved); if ($search_engine->getErrors()) { return $this->newDialog()->setWidth(AphrontDialogView::WIDTH_FULL)->setTitle(pht('Advanced Filter'))->appendChild($filter_form->buildLayoutView())->setErrors($search_engine->getErrors())->setSubmitURI($board_uri)->addSubmitButton(pht('Apply Filter'))->addCancelButton($board_uri); } return id(new AphrontRedirectResponse())->setURI($this->getURIWithState($search_engine->getQueryResultsPageURI($saved->getQueryKey()))); } $query_key = $request->getURIData('queryKey'); if (!$query_key) { $query_key = 'open'; } $this->queryKey = $query_key; $custom_query = null; if ($search_engine->isBuiltinQuery($query_key)) { $saved = $search_engine->buildSavedQueryFromBuiltin($query_key); } else { $saved = id(new PhabricatorSavedQueryQuery())->setViewer($viewer)->withQueryKeys(array($query_key))->executeOne(); if (!$saved) { return new Aphront404Response(); } $custom_query = $saved; } if ($request->getURIData('filter')) { $filter_form = id(new AphrontFormView())->setUser($viewer); $search_engine->buildSearchForm($filter_form, $saved); return $this->newDialog()->setWidth(AphrontDialogView::WIDTH_FULL)->setTitle(pht('Advanced Filter'))->appendChild($filter_form->buildLayoutView())->setSubmitURI($board_uri)->addSubmitButton(pht('Apply Filter'))->addCancelButton($board_uri); } $task_query = $search_engine->buildQueryFromSavedQuery($saved); $select_phids = array($project->getPHID()); if ($project->getHasSubprojects() || $project->getHasMilestones()) { $descendants = id(new PhabricatorProjectQuery())->setViewer($viewer)->withAncestorProjectPHIDs($select_phids)->execute(); foreach ($descendants as $descendant) { $select_phids[] = $descendant->getPHID(); } } $tasks = $task_query->withEdgeLogicPHIDs(PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, PhabricatorQueryConstraint::OPERATOR_ANCESTOR, array($select_phids))->setOrder(ManiphestTaskQuery::ORDER_PRIORITY)->setViewer($viewer)->execute(); $tasks = mpull($tasks, null, 'getPHID'); $board_phid = $project->getPHID(); $layout_engine = id(new PhabricatorBoardLayoutEngine())->setViewer($viewer)->setBoardPHIDs(array($board_phid))->setObjectPHIDs(array_keys($tasks))->setFetchAllBoards(true)->executeLayout(); $columns = $layout_engine->getColumns($board_phid); if (!$columns || !$project->getHasWorkboard()) { $has_normal_columns = false; foreach ($columns as $column) { if (!$column->getProxyPHID()) { $has_normal_columns = true; break; } } $can_edit = PhabricatorPolicyFilter::hasCapability($viewer, $project, PhabricatorPolicyCapability::CAN_EDIT); if (!$has_normal_columns) { if (!$can_edit) { $content = $this->buildNoAccessContent($project); } else { $content = $this->buildInitializeContent($project); } } else { if (!$can_edit) { $content = $this->buildDisabledContent($project); } else { $content = $this->buildEnableContent($project); } } if ($content instanceof AphrontResponse) { return $content; } $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorProject::PANEL_WORKBOARD); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Workboard')); return $this->newPage()->setTitle(array($project->getDisplayName(), pht('Workboard')))->setNavigation($nav)->setCrumbs($crumbs)->appendChild($content); } $task_can_edit_map = id(new PhabricatorPolicyFilter())->setViewer($viewer)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_EDIT))->apply($tasks); // If this is a batch edit, select the editable tasks in the chosen column // and ship the user into the batch editor. $batch_edit = $request->getStr('batch'); if ($batch_edit) { if ($batch_edit !== self::BATCH_EDIT_ALL) { $column_id_map = mpull($columns, null, 'getID'); $batch_column = idx($column_id_map, $batch_edit); if (!$batch_column) { return new Aphront404Response(); } $batch_task_phids = $layout_engine->getColumnObjectPHIDs($board_phid, $batch_column->getPHID()); foreach ($batch_task_phids as $key => $batch_task_phid) { if (empty($task_can_edit_map[$batch_task_phid])) { unset($batch_task_phids[$key]); } } $batch_tasks = array_select_keys($tasks, $batch_task_phids); } else { $batch_tasks = $task_can_edit_map; } if (!$batch_tasks) { $cancel_uri = $this->getURIWithState($board_uri); return $this->newDialog()->setTitle(pht('No Editable Tasks'))->appendParagraph(pht('The selected column contains no visible tasks which you ' . 'have permission to edit.'))->addCancelButton($board_uri); } $batch_ids = mpull($batch_tasks, 'getID'); $batch_ids = implode(',', $batch_ids); $batch_uri = new PhutilURI('/maniphest/batch/'); $batch_uri->setQueryParam('board', $this->id); $batch_uri->setQueryParam('batch', $batch_ids); return id(new AphrontRedirectResponse())->setURI($batch_uri); } $board_id = celerity_generate_unique_node_id(); $board = id(new PHUIWorkboardView())->setUser($viewer)->setID($board_id)->addSigil('jx-workboard')->setMetadata(array('boardPHID' => $project->getPHID())); $visible_columns = array(); $column_phids = array(); $visible_phids = array(); foreach ($columns as $column) { if (!$this->showHidden) { if ($column->isHidden()) { continue; } } $proxy = $column->getProxy(); if ($proxy && !$proxy->isMilestone()) { // TODO: For now, don't show subproject columns because we can't // handle tasks with multiple positions yet. continue; } $task_phids = $layout_engine->getColumnObjectPHIDs($board_phid, $column->getPHID()); $column_tasks = array_select_keys($tasks, $task_phids); // If we aren't using "natural" order, reorder the column by the original // query order. if ($this->sortKey != PhabricatorProjectColumn::ORDER_NATURAL) { $column_tasks = array_select_keys($column_tasks, array_keys($tasks)); } $column_phid = $column->getPHID(); $visible_columns[$column_phid] = $column; $column_phids[$column_phid] = $column_tasks; foreach ($column_tasks as $phid => $task) { $visible_phids[$phid] = $phid; } } $rendering_engine = id(new PhabricatorBoardRenderingEngine())->setViewer($viewer)->setObjects(array_select_keys($tasks, $visible_phids))->setEditMap($task_can_edit_map)->setExcludedProjectPHIDs($select_phids); $templates = array(); $column_maps = array(); $all_tasks = array(); foreach ($visible_columns as $column_phid => $column) { $column_tasks = $column_phids[$column_phid]; $panel = id(new PHUIWorkpanelView())->setHeader($column->getDisplayName())->setSubHeader($column->getDisplayType())->addSigil('workpanel'); $proxy = $column->getProxy(); if ($proxy) { $proxy_id = $proxy->getID(); $href = $this->getApplicationURI("view/{$proxy_id}/"); $panel->setHref($href); } $header_icon = $column->getHeaderIcon(); if ($header_icon) { $panel->setHeaderIcon($header_icon); } $display_class = $column->getDisplayClass(); if ($display_class) { $panel->addClass($display_class); } if ($column->isHidden()) { $panel->addClass('project-panel-hidden'); } $column_menu = $this->buildColumnMenu($project, $column); $panel->addHeaderAction($column_menu); $count_tag = id(new PHUITagView())->setType(PHUITagView::TYPE_SHADE)->setShade(PHUITagView::COLOR_BLUE)->addSigil('column-points')->setName(javelin_tag('span', array('sigil' => 'column-points-content'), pht('-')))->setStyle('display: none'); $panel->setHeaderTag($count_tag); $cards = id(new PHUIObjectItemListView())->setUser($viewer)->setFlush(true)->setAllowEmptyList(true)->addSigil('project-column')->setItemClass('phui-workcard')->setMetadata(array('columnPHID' => $column->getPHID(), 'pointLimit' => $column->getPointLimit())); foreach ($column_tasks as $task) { $object_phid = $task->getPHID(); $card = $rendering_engine->renderCard($object_phid); $templates[$object_phid] = hsprintf('%s', $card->getItem()); $column_maps[$column_phid][] = $object_phid; $all_tasks[$object_phid] = $task; } $panel->setCards($cards); $board->addPanel($panel); } $behavior_config = array('moveURI' => $this->getApplicationURI('move/' . $project->getID() . '/'), 'createURI' => $this->getCreateURI(), 'uploadURI' => '/file/dropupload/', 'coverURI' => $this->getApplicationURI('cover/'), 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold(), 'pointsEnabled' => ManiphestTaskPoints::getIsEnabled(), 'boardPHID' => $project->getPHID(), 'order' => $this->sortKey, 'templateMap' => $templates, 'columnMaps' => $column_maps, 'orderMaps' => mpull($all_tasks, 'getWorkboardOrderVectors'), 'propertyMaps' => mpull($all_tasks, 'getWorkboardProperties'), 'boardID' => $board_id, 'projectPHID' => $project->getPHID()); $this->initBehavior('project-boards', $behavior_config); $sort_menu = $this->buildSortMenu($viewer, $this->sortKey); $filter_menu = $this->buildFilterMenu($viewer, $custom_query, $search_engine, $query_key); $manage_menu = $this->buildManageMenu($project, $this->showHidden); $header_link = phutil_tag('a', array('href' => $this->getApplicationURI('profile/' . $project->getID() . '/')), $project->getName()); $board_box = id(new PHUIBoxView())->appendChild($board)->addClass('project-board-wrapper'); $nav = $this->getProfileMenu(); $divider = id(new PHUIListItemView())->setType(PHUIListItemView::TYPE_DIVIDER); $fullscreen = $this->buildFullscreenMenu(); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Workboard')); $crumbs->setBorder(true); $crumbs->addAction($sort_menu); $crumbs->addAction($filter_menu); $crumbs->addAction($divider); $crumbs->addAction($manage_menu); $crumbs->addAction($fullscreen); return $this->newPage()->setTitle(array($project->getDisplayName(), pht('Workboard')))->setPageObjectPHIDs(array($project->getPHID()))->setShowFooter(false)->setNavigation($nav)->setCrumbs($crumbs)->addQuicksandConfig(array('boardConfig' => $behavior_config))->appendChild(array($board_box)); }
private function browseFile() { $viewer = $this->getViewer(); $request = $this->getRequest(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $before = $request->getStr('before'); if ($before) { return $this->buildBeforeResponse($before); } $path = $drequest->getPath(); $blame_key = PhabricatorDiffusionBlameSetting::SETTINGKEY; $color_key = PhabricatorDiffusionColorSetting::SETTINGKEY; $show_blame = $request->getBool('blame', $viewer->getUserSetting($blame_key)); $show_color = $request->getBool('color', $viewer->getUserSetting($color_key)); $view = $request->getStr('view'); if ($request->isFormPost() && $view != 'raw' && $viewer->isLoggedIn()) { $preferences = PhabricatorUserPreferences::loadUserPreferences($viewer); $editor = id(new PhabricatorUserPreferencesEditor())->setActor($viewer)->setContentSourceFromRequest($request)->setContinueOnNoEffect(true)->setContinueOnMissingFields(true); $xactions = array(); $xactions[] = $preferences->newTransaction($blame_key, $show_blame); $xactions[] = $preferences->newTransaction($color_key, $show_color); $editor->applyTransactions($preferences, $xactions); $uri = $request->getRequestURI()->alter('blame', null)->alter('color', null); return id(new AphrontRedirectResponse())->setURI($uri); } // We need the blame information if blame is on and we're building plain // text, or blame is on and this is an Ajax request. If blame is on and // this is a colorized request, we don't show blame at first (we ajax it // in afterward) so we don't need to query for it. $needs_blame = $show_blame && !$show_color || $show_blame && $request->isAjax(); $params = array('commit' => $drequest->getCommit(), 'path' => $drequest->getPath()); $byte_limit = null; if ($view !== 'raw') { $byte_limit = PhabricatorFileStorageEngine::getChunkThreshold(); $time_limit = 10; $params += array('timeout' => $time_limit, 'byteLimit' => $byte_limit); } $response = $this->callConduitWithDiffusionRequest('diffusion.filecontentquery', $params); $hit_byte_limit = $response['tooHuge']; $hit_time_limit = $response['tooSlow']; $file_phid = $response['filePHID']; if ($hit_byte_limit) { $corpus = $this->buildErrorCorpus(pht('This file is larger than %s byte(s), and too large to display ' . 'in the web UI.', phutil_format_bytes($byte_limit))); } else { if ($hit_time_limit) { $corpus = $this->buildErrorCorpus(pht('This file took too long to load from the repository (more than ' . '%s second(s)).', new PhutilNumber($time_limit))); } else { $file = id(new PhabricatorFileQuery())->setViewer($viewer)->withPHIDs(array($file_phid))->executeOne(); if (!$file) { throw new Exception(pht('Failed to load content file!')); } if ($view === 'raw') { return $file->getRedirectResponse(); } $data = $file->loadFileData(); $ref = $this->getGitLFSRef($repository, $data); if ($ref) { if ($view == 'git-lfs') { $file = $this->loadGitLFSFile($ref); // Rename the file locally so we generate a better vanity URI for // it. In storage, it just has a name like "lfs-13f9a94c0923...", // since we don't get any hints about possible human-readable names // at upload time. $basename = basename($drequest->getPath()); $file->makeEphemeral(); $file->setName($basename); return $file->getRedirectResponse(); } else { $corpus = $this->buildGitLFSCorpus($ref); } } else { if (ArcanistDiffUtils::isHeuristicBinaryFile($data)) { $file_uri = $file->getBestURI(); if ($file->isViewableImage()) { $corpus = $this->buildImageCorpus($file_uri); } else { $corpus = $this->buildBinaryCorpus($file_uri, $data); } } else { $this->loadLintMessages(); $this->coverage = $drequest->loadCoverage(); // Build the content of the file. $corpus = $this->buildCorpus($show_blame, $show_color, $data, $needs_blame, $drequest, $path, $data); } } } } if ($request->isAjax()) { return id(new AphrontAjaxResponse())->setContent($corpus); } require_celerity_resource('diffusion-source-css'); // Render the page. $view = $this->buildCurtain($drequest); $curtain = $this->enrichCurtain($view, $drequest, $show_blame, $show_color); $properties = $this->buildPropertyView($drequest); $header = $this->buildHeaderView($drequest); $header->setHeaderIcon('fa-file-code-o'); $content = array(); $follow = $request->getStr('follow'); if ($follow) { $notice = new PHUIInfoView(); $notice->setSeverity(PHUIInfoView::SEVERITY_WARNING); $notice->setTitle(pht('Unable to Continue')); switch ($follow) { case 'first': $notice->appendChild(pht('Unable to continue tracing the history of this file because ' . 'this commit is the first commit in the repository.')); break; case 'created': $notice->appendChild(pht('Unable to continue tracing the history of this file because ' . 'this commit created the file.')); break; } $content[] = $notice; } $renamed = $request->getStr('renamed'); if ($renamed) { $notice = new PHUIInfoView(); $notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE); $notice->setTitle(pht('File Renamed')); $notice->appendChild(pht('File history passes through a rename from "%s" to "%s".', $drequest->getPath(), $renamed)); $content[] = $notice; } $content[] = $corpus; $content[] = $this->buildOpenRevisions(); $crumbs = $this->buildCrumbs(array('branch' => true, 'path' => true, 'view' => 'browse')); $crumbs->setBorder(true); $basename = basename($this->getDiffusionRequest()->getPath()); $view = id(new PHUITwoColumnView())->setHeader($header)->setCurtain($curtain)->setMainColumn(array($content)); if ($properties) { $view->addPropertySection(pht('Details'), $properties); } $title = array($basename, $repository->getDisplayName()); return $this->newPage()->setTitle($title)->setCrumbs($crumbs)->appendChild(array($view)); }
protected function renderInput() { $id = $this->getID(); if (!$id) { $id = celerity_generate_unique_node_id(); $this->setID($id); } $viewer = $this->getUser(); if (!$viewer) { throw new PhutilInvalidStateException('setUser'); } // We need to have this if previews render images, since Ajax can not // currently ship JS or CSS. require_celerity_resource('lightbox-attachment-css'); Javelin::initBehavior('aphront-drag-and-drop-textarea', array('target' => $id, 'activatedClass' => 'aphront-textarea-drag-and-drop', 'uri' => '/file/dropupload/', 'chunkThreshold' => PhabricatorFileStorageEngine::getChunkThreshold())); Javelin::initBehavior('phabricator-remarkup-assist', array('pht' => array('bold text' => pht('bold text'), 'italic text' => pht('italic text'), 'monospaced text' => pht('monospaced text'), 'List Item' => pht('List Item'), 'Quoted Text' => pht('Quoted Text'), 'data' => pht('data'), 'name' => pht('name'), 'URL' => pht('URL')))); Javelin::initBehavior('phabricator-tooltips', array()); $actions = array('fa-bold' => array('tip' => pht('Bold')), 'fa-italic' => array('tip' => pht('Italics')), 'fa-text-width' => array('tip' => pht('Monospaced')), 'fa-link' => array('tip' => pht('Link')), array('spacer' => true), 'fa-list-ul' => array('tip' => pht('Bulleted List')), 'fa-list-ol' => array('tip' => pht('Numbered List')), 'fa-code' => array('tip' => pht('Code Block')), 'fa-quote-right' => array('tip' => pht('Quote')), 'fa-table' => array('tip' => pht('Table')), 'fa-cloud-upload' => array('tip' => pht('Upload File'))); $can_use_macros = !$this->disableMacro && function_exists('imagettftext'); if ($can_use_macros) { $can_use_macros = PhabricatorApplication::isClassInstalledForViewer('PhabricatorMacroApplication', $viewer); } if ($can_use_macros) { $actions[] = array('spacer' => true); $actions['fa-meh-o'] = array('tip' => pht('Meme')); } $actions['fa-life-bouy'] = array('tip' => pht('Help'), 'align' => 'right', 'href' => PhrictionDocument::getSlugURI('usage/formatting_reference')); if (!$this->disableFullScreen) { $actions[] = array('spacer' => true, 'align' => 'right'); $actions['fa-arrows-alt'] = array('tip' => pht('Fullscreen Mode'), 'align' => 'right'); } $buttons = array(); foreach ($actions as $action => $spec) { $classes = array(); if (idx($spec, 'align') == 'right') { $classes[] = 'remarkup-assist-right'; } if (idx($spec, 'spacer')) { $classes[] = 'remarkup-assist-separator'; $buttons[] = phutil_tag('span', array('class' => implode(' ', $classes)), ''); continue; } else { $classes[] = 'remarkup-assist-button'; } $href = idx($spec, 'href', '#'); if ($href == '#') { $meta = array('action' => $action); $mustcapture = true; $target = null; } else { $meta = array(); $mustcapture = null; $target = '_blank'; } $content = null; $tip = idx($spec, 'tip'); if ($tip) { $meta['tip'] = $tip; $content = javelin_tag('span', array('aural' => true), $tip); } $buttons[] = javelin_tag('a', array('class' => implode(' ', $classes), 'href' => $href, 'sigil' => 'remarkup-assist has-tooltip', 'meta' => $meta, 'mustcapture' => $mustcapture, 'target' => $target, 'tabindex' => -1), phutil_tag('div', array('class' => 'remarkup-assist phui-icon-view phui-font-fa bluegrey ' . $action), $content)); } $buttons = phutil_tag('div', array('class' => 'remarkup-assist-bar'), $buttons); $monospaced_textareas = null; $monospaced_textareas_class = null; $monospaced_textareas = $viewer->loadPreferences()->getPreference(PhabricatorUserPreferences::PREFERENCE_MONOSPACED_TEXTAREAS); if ($monospaced_textareas == 'enabled') { $monospaced_textareas_class = 'PhabricatorMonospaced'; } $this->setCustomClass('remarkup-assist-textarea ' . $monospaced_textareas_class); return javelin_tag('div', array('sigil' => 'remarkup-assist-control'), array($buttons, parent::renderInput())); }