diff --git a/resources/sql/patches/043.pastebin.sql b/resources/sql/patches/043.pastebin.sql new file mode 100644 index 0000000000..208ade90a1 --- /dev/null +++ b/resources/sql/patches/043.pastebin.sql @@ -0,0 +1,17 @@ +CREATE DATABASE phabricator_pastebin; + +CREATE TABLE phabricator_pastebin.pastebin_paste ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + title VARCHAR(255) NOT NULL, + phid VARCHAR(64) BINARY NOT NULL, + authorPHID VARCHAR(64) BINARY NOT NULL, + filePHID VARCHAR(64) BINARY NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL +); + +INSERT INTO phabricator_directory.directory_item + (name, description, href, categoryID, sequence, dateCreated, dateModified) +VALUES + ("Paste", "Mmm... tasty, delicious paste.", "/paste/", 5, 150, + UNIX_TIMESTAMP(), UNIX_TIMESTAMP()); \ No newline at end of file diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2bcb47d2e3..344b963863 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -398,6 +398,12 @@ phutil_register_library_map(array( 'PhabricatorPHIDDAO' => 'applications/phid/storage/base', 'PhabricatorPHIDListController' => 'applications/phid/controller/list', 'PhabricatorPHIDLookupController' => 'applications/phid/controller/lookup', + 'PhabricatorPaste' => 'applications/paste/storage/paste', + 'PhabricatorPasteController' => 'applications/paste/controller/base', + 'PhabricatorPasteCreateController' => 'applications/paste/controller/create', + 'PhabricatorPasteDAO' => 'applications/paste/storage/base', + 'PhabricatorPasteHomeController' => 'applications/paste/controller/home', + 'PhabricatorPasteViewController' => 'applications/paste/controller/view', 'PhabricatorPeopleController' => 'applications/people/controller/base', 'PhabricatorPeopleEditController' => 'applications/people/controller/edit', 'PhabricatorPeopleListController' => 'applications/people/controller/list', @@ -840,6 +846,12 @@ phutil_register_library_map(array( 'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO', 'PhabricatorPHIDListController' => 'PhabricatorPHIDController', 'PhabricatorPHIDLookupController' => 'PhabricatorPHIDController', + 'PhabricatorPaste' => 'PhabricatorPasteDAO', + 'PhabricatorPasteController' => 'PhabricatorController', + 'PhabricatorPasteCreateController' => 'PhabricatorPasteController', + 'PhabricatorPasteDAO' => 'PhabricatorLiskDAO', + 'PhabricatorPasteHomeController' => 'PhabricatorPasteController', + 'PhabricatorPasteViewController' => 'PhabricatorPasteController', 'PhabricatorPeopleController' => 'PhabricatorController', 'PhabricatorPeopleEditController' => 'PhabricatorPeopleController', 'PhabricatorPeopleListController' => 'PhabricatorPeopleController', diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index 519aed0c70..a7a28111a9 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -305,6 +305,13 @@ class AphrontDefaultApplicationConfiguration '/status/$' => 'PhabricatorStatusController', + '/paste/' => array( + '$' => 'PhabricatorPasteHomeController', + 'create/' => 'PhabricatorPasteCreateController', + ), + + '/P(?P\d+)$' => 'PhabricatorPasteViewController', + '/help/' => array( 'keyboardshortcut/$' => 'PhabricatorHelpKeyboardShortcutController', ), diff --git a/src/applications/paste/controller/base/PhabricatorPasteController.php b/src/applications/paste/controller/base/PhabricatorPasteController.php new file mode 100644 index 0000000000..2ec153c9dd --- /dev/null +++ b/src/applications/paste/controller/base/PhabricatorPasteController.php @@ -0,0 +1,44 @@ +buildStandardPageView(); + + $page->setApplicationName('Paste'); + $page->setBaseURI('/paste/'); + $page->setTitle(idx($data, 'title')); + $page->setGlyph("\xE2\x9C\x8E"); + $page->setTabs( + array( + 'create' => array( + 'href' => '/paste/create', + 'name' => 'Create a Paste', + ), + ), + idx($data, 'tab')); + + $page->appendChild($view); + + $response = new AphrontWebpageResponse(); + return $response->setContent($page->render()); + + } +} diff --git a/src/applications/paste/controller/create/PhabricatorPasteCreateController.php b/src/applications/paste/controller/create/PhabricatorPasteCreateController.php index 199a69a5cd..4b4a5be54c 100644 --- a/src/applications/paste/controller/create/PhabricatorPasteCreateController.php +++ b/src/applications/paste/controller/create/PhabricatorPasteCreateController.php @@ -22,6 +22,43 @@ class PhabricatorPasteCreateController extends PhabricatorPasteController { $request = $this->getRequest(); $user = $request->getUser(); + $paste = new PhabricatorPaste(); + + $error_view = null; + $e_text = true; + + if ($request->isFormPost()) { + $errors = array(); + $title = $request->getStr('title'); + $text = $request->getStr('text'); + + if (!strlen($text)) { + $e_text = 'Required'; + $errors[] = 'The paste may not be blank.'; + } else { + $e_text = null; + } + + $paste->setTitle($title); + + if (!$errors) { + $paste_file = PhabricatorFile::newFromFileData( + $text, + array( + 'name' => $title, + )); + $paste->setFilePHID($paste_file->getPHID()); + $paste->setAuthorPHID($user->getPHID()); + $paste->save(); + + return id(new AphrontRedirectResponse()) + ->setURI('/P'.$paste->getID()); + } else { + $error_view = new AphrontErrorView(); + $error_view->setErrors($errors); + $error_view->setTitle('A problem has occurred!'); + } + } $form = new AphrontFormView(); $form @@ -30,26 +67,31 @@ class PhabricatorPasteCreateController extends PhabricatorPasteController { ->appendChild( id(new AphrontFormTextControl()) ->setLabel('Title') + ->setValue($paste->getTitle()) ->setName('title')) ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel('Text') + ->setError($e_text) ->setName('text')) ->appendChild( - id(new AphrontFormSubmitControl()) - ->addCancelButton('/paste/') - ->setValue('Create Paste')); - + id(new AphrontFormSubmitControl()) + ->addCancelButton('/paste/') + ->setValue('Create Paste')); + $panel = new AphrontPanelView(); - $panel->setWidth(AphrontPanelView::WIDTH_FULL); - $panel->setHeader("Create a Paste"); + $panel->setWidth(AphrontPanelView::WIDTH_FORM); + $panel->setHeader('Create a Paste'); $panel->appendChild($form); return $this->buildStandardPageResponse( - $panel, + array( + $error_view, + $panel, + ), array( 'title' => 'Paste Creation', 'tab' => 'create', )); } -} \ No newline at end of file +} diff --git a/src/applications/paste/controller/create/__init__.php b/src/applications/paste/controller/create/__init__.php index 0e297abd04..f6151ac905 100644 --- a/src/applications/paste/controller/create/__init__.php +++ b/src/applications/paste/controller/create/__init__.php @@ -6,7 +6,18 @@ +phutil_require_module('phabricator', 'aphront/response/redirect'); +phutil_require_module('phabricator', 'applications/files/storage/file'); phutil_require_module('phabricator', 'applications/paste/controller/base'); +phutil_require_module('phabricator', 'applications/paste/storage/paste'); +phutil_require_module('phabricator', 'view/form/base'); +phutil_require_module('phabricator', 'view/form/control/submit'); +phutil_require_module('phabricator', 'view/form/control/text'); +phutil_require_module('phabricator', 'view/form/control/textarea'); +phutil_require_module('phabricator', 'view/form/error'); +phutil_require_module('phabricator', 'view/layout/panel'); + +phutil_require_module('phutil', 'utils'); phutil_require_source('PhabricatorPasteCreateController.php'); diff --git a/src/applications/paste/controller/home/PhabricatorPasteHomeController.php b/src/applications/paste/controller/home/PhabricatorPasteHomeController.php index 3a79c8625f..f8dbb261d9 100644 --- a/src/applications/paste/controller/home/PhabricatorPasteHomeController.php +++ b/src/applications/paste/controller/home/PhabricatorPasteHomeController.php @@ -19,11 +19,83 @@ class PhabricatorPasteHomeController extends PhabricatorPasteController { public function processRequest() { + + $request = $this->getRequest(); + + $pager = new AphrontPagerView(); + $pager->setOffset($request->getInt('page')); + + $pastes = id(new PhabricatorPaste())->loadAllWhere( + '1 = 1 ORDER BY id DESC LIMIT %d, %d', + $pager->getOffset(), + $pager->getPageSize() + 1); + + $pastes = $pager->sliceResults($pastes); + $pager->setURI($request->getRequestURI(), 'page'); + + $phids = mpull($pastes, 'getAuthorPHID'); + $handles = array(); + if ($phids) { + $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles(); + } + + $rows = array(); + foreach ($pastes as $paste) { + + $handle = $handles[$paste->getAuthorPHID()]; + + $rows[] = array( + phutil_escape_html($paste->getPHID()), + phutil_escape_html($paste->getTitle()), + + // TODO: Make this filter by user instead of going to their profile. + phutil_render_tag( + 'a', + array( + 'href' => '/p/'.$handle->getName().'/', + ), + phutil_escape_html($handle->getName())), + + phutil_render_tag( + 'a', + array( + 'href' => PhabricatorFileURI::getViewURIForPHID( + $paste->getFilePHID()), + ), + phutil_escape_html($paste->getFilePHID())), + + phutil_render_tag( + 'a', + array( + 'class' => 'small button grey', + 'href' => '/P'.$paste->getID(), + ), + 'View'), + ); + } + + $table = new AphrontTableView($rows); + $table->setHeaders( + array( + 'PHID', + 'Title', + 'Author', + 'File PHID', + 'View' + )); + + $panel = new AphrontPanelView(); + $panel->setWidth(AphrontPanelView::WIDTH_FULL); + $panel->setHeader("Paste"); + $panel->setCreateButton('Paste Something', '/paste/create/'); + $panel->appendChild($table); + $panel->appendChild($pager); + return $this->buildStandardPageResponse( - null, + $panel, array( 'title' => 'Paste', - 'tab' => 'home', - )); + ) + ); } -} \ No newline at end of file +} diff --git a/src/applications/paste/controller/home/__init__.php b/src/applications/paste/controller/home/__init__.php index 65db4d0b96..5962fbc537 100644 --- a/src/applications/paste/controller/home/__init__.php +++ b/src/applications/paste/controller/home/__init__.php @@ -6,7 +6,16 @@ +phutil_require_module('phabricator', 'applications/files/uri'); phutil_require_module('phabricator', 'applications/paste/controller/base'); +phutil_require_module('phabricator', 'applications/paste/storage/paste'); +phutil_require_module('phabricator', 'applications/phid/handle/data'); +phutil_require_module('phabricator', 'view/control/pager'); +phutil_require_module('phabricator', 'view/control/table'); +phutil_require_module('phabricator', 'view/layout/panel'); + +phutil_require_module('phutil', 'markup'); +phutil_require_module('phutil', 'utils'); phutil_require_source('PhabricatorPasteHomeController.php'); diff --git a/src/applications/paste/controller/view/PhabricatorPasteViewController.php b/src/applications/paste/controller/view/PhabricatorPasteViewController.php new file mode 100644 index 0000000000..54ede8fbca --- /dev/null +++ b/src/applications/paste/controller/view/PhabricatorPasteViewController.php @@ -0,0 +1,130 @@ +id = $data['id']; + } + + public function processRequest() { + + $request = $this->getRequest(); + $user = $request->getUser(); + + $paste = id(new PhabricatorPaste())->load($this->id); + if (!$paste) { + return new Aphront404Response(); + } + + $file = id(new PhabricatorFile())->loadOneWhere( + 'phid = %s', + $paste->getFilePHID()); + if (!$file) { + return new Aphront400Response(); + } + + $corpus = $this->buildCorpus($paste, $file); + + /* TODO + $raw_button = phutil_render_tag( + 'a', + array( + 'class' => 'small button grey', + 'href' => '/P'.$paste->getId().'/raw/', + ), + 'Raw Paste'); + */ + + $panel = new AphrontPanelView(); + + if (strlen($paste->getTitle())) { + $panel->setHeader( + 'Viewing Paste '.$paste->getID().' - '. + phutil_escape_html($paste->getTitle())); + } else { + $panel->setHeader('Viewing Paste '.$paste->getID()); + } + + $panel->setWidth(AphrontPanelView::WIDTH_FULL); + $panel->setCreateButton('Paste Something', '/paste/create/'); + $panel->appendChild($corpus); + // $panel->appendChild($raw_button); + + return $this->buildStandardPageResponse( + $panel, + array( + 'title' => 'Viewing Paste '.$this->id, + 'tab' => 'view', + )); + } + + private function buildCorpus($paste, $file) { + // Blantently copied from DiffusionBrowseFileController + + require_celerity_resource('diffusion-source-css'); + require_celerity_resource('syntax-highlighting-css'); + + $highlightEngine = new PhutilDefaultSyntaxHighlighterEngine(); + $highlightEngine->setConfig( + 'pygments.enabled', + PhabricatorEnv::getEnvConfig('pygments.enabled')); + + $text_list = explode( + "\n", $highlightEngine->highlightSource( + $paste->getTitle(), + $file->loadFileData())); + + $rows = $this->buildDisplayRows($text_list); + + $corpus_table = phutil_render_tag( + 'table', + array( + 'class' => "diffusion-source remarkup-code PhabricatorMonospaced", + ), + implode("\n", $rows)); + + $corpus = phutil_render_tag( + 'div', + array( + 'style' => 'padding: 0pt 2em;', + ), + $corpus_table); + + return $corpus; + } + + private function buildDisplayRows($text_list) { + $rows = array(); + $n = 1; + + foreach ($text_list as $k => $line) { + // Pardon the ugly for the time being. + // And eventually this will highlight a line that you click + // like diffusion does. Or maybe allow for line comments + // like differential. Either way it will be better than it is now. + $rows[] = ''.$n.''.$line.''; + ++$n; + } + + return $rows; + } + +} diff --git a/src/applications/paste/controller/view/__init__.php b/src/applications/paste/controller/view/__init__.php new file mode 100644 index 0000000000..6d7b9dc150 --- /dev/null +++ b/src/applications/paste/controller/view/__init__.php @@ -0,0 +1,23 @@ + true, + ) + parent::getConfiguration(); + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID( + PhabricatorPHIDConstants::PHID_TYPE_PSTE); + } + +} diff --git a/src/applications/paste/storage/paste/__init__.php b/src/applications/paste/storage/paste/__init__.php new file mode 100644 index 0000000000..ef2ac38a2f --- /dev/null +++ b/src/applications/paste/storage/paste/__init__.php @@ -0,0 +1,14 @@ +