diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2cc42fbfc..044b3158f 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1,170 +1,191 @@ array( 'Aphront404Response' => 'aphront/response/404', 'AphrontApplicationConfiguration' => 'aphront/applicationconfiguration', 'AphrontController' => 'aphront/controller', 'AphrontDatabaseConnection' => 'storage/connection/base', 'AphrontDefaultApplicationConfiguration' => 'aphront/default/configuration', 'AphrontDefaultApplicationController' => 'aphront/default/controller', 'AphrontDialogResponse' => 'aphront/response/dialog', 'AphrontDialogView' => 'view/dialog', 'AphrontErrorView' => 'view/form/error', 'AphrontFileResponse' => 'aphront/response/file', 'AphrontFormControl' => 'view/form/control/base', 'AphrontFormFileControl' => 'view/form/control/file', 'AphrontFormSelectControl' => 'view/form/control/select', 'AphrontFormStaticControl' => 'view/form/control/static', 'AphrontFormSubmitControl' => 'view/form/control/submit', 'AphrontFormTextAreaControl' => 'view/form/control/textarea', 'AphrontFormTextControl' => 'view/form/control/text', 'AphrontFormView' => 'view/form/base', 'AphrontMySQLDatabaseConnection' => 'storage/connection/mysql', 'AphrontNullView' => 'view/null', 'AphrontPageView' => 'view/page/base', 'AphrontPanelView' => 'view/layout/panel', 'AphrontQueryConnectionException' => 'storage/exception/connection', 'AphrontQueryConnectionLostException' => 'storage/exception/connectionlost', 'AphrontQueryCountException' => 'storage/exception/count', 'AphrontQueryException' => 'storage/exception/base', 'AphrontQueryObjectMissingException' => 'storage/exception/objectmissing', 'AphrontQueryParameterException' => 'storage/exception/parameter', 'AphrontQueryRecoverableException' => 'storage/exception/recoverable', 'AphrontRedirectResponse' => 'aphront/response/redirect', 'AphrontRequest' => 'aphront/request', 'AphrontResponse' => 'aphront/response/base', + 'AphrontSideNavView' => 'view/layout/sidenav', 'AphrontTableView' => 'view/control/table', 'AphrontURIMapper' => 'aphront/mapper', 'AphrontView' => 'view/base', 'AphrontWebpageResponse' => 'aphront/response/webpage', + 'ConduitAPIMethod' => 'applications/conduit/method/base', + 'ConduitAPIRequest' => 'applications/conduit/protocol/request', + 'ConduitAPI_file_upload_Method' => 'applications/conduit/method/file/upload', + 'ConduitException' => 'applications/conduit/protocol/exception', 'DifferentialAction' => 'applications/differential/constants/action', 'DifferentialChangeType' => 'applications/differential/constants/changetype', 'DifferentialLintStatus' => 'applications/differential/constants/lintstatus', 'DifferentialRevisionStatus' => 'applications/differential/constants/revisionstatus', 'DifferentialUnitStatus' => 'applications/differential/constants/unitstatus', 'LiskDAO' => 'storage/lisk/dao', + 'PhabricatorConduitAPIController' => 'applications/conduit/controller/api', + 'PhabricatorConduitConnectionLog' => 'applications/conduit/storage/connectionlog', + 'PhabricatorConduitConsoleController' => 'applications/conduit/controller/console', + 'PhabricatorConduitController' => 'applications/conduit/controller/base', + 'PhabricatorConduitDAO' => 'applications/conduit/storage/base', + 'PhabricatorConduitLogController' => 'applications/conduit/controller/log', + 'PhabricatorConduitMethodCallLog' => 'applications/conduit/storage/methodcalllog', 'PhabricatorController' => 'applications/base/controller/base', 'PhabricatorDirectoryCategory' => 'applications/directory/storage/category', 'PhabricatorDirectoryCategoryDeleteController' => 'applications/directory/controller/categorydelete', 'PhabricatorDirectoryCategoryEditController' => 'applications/directory/controller/categoryedit', 'PhabricatorDirectoryCategoryListController' => 'applications/directory/controller/categorylist', 'PhabricatorDirectoryController' => 'applications/directory/controller/base', 'PhabricatorDirectoryDAO' => 'applications/directory/storage/base', 'PhabricatorDirectoryItem' => 'applications/directory/storage/item', 'PhabricatorDirectoryItemDeleteController' => 'applications/directory/controller/itemdelete', 'PhabricatorDirectoryItemEditController' => 'applications/directory/controller/itemedit', 'PhabricatorDirectoryItemListController' => 'applications/directory/controller/itemlist', 'PhabricatorDirectoryMainController' => 'applications/directory/controller/main', 'PhabricatorFile' => 'applications/files/storage/file', 'PhabricatorFileController' => 'applications/files/controller/base', 'PhabricatorFileDAO' => 'applications/files/storage/base', 'PhabricatorFileListController' => 'applications/files/controller/list', 'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob', 'PhabricatorFileUploadController' => 'applications/files/controller/upload', 'PhabricatorFileViewController' => 'applications/files/controller/view', 'PhabricatorLiskDAO' => 'applications/base/storage/lisk', 'PhabricatorPHID' => 'applications/phid/storage/phid', 'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate', 'PhabricatorPHIDController' => 'applications/phid/controller/base', 'PhabricatorPHIDDAO' => 'applications/phid/storage/base', 'PhabricatorPHIDListController' => 'applications/phid/controller/list', 'PhabricatorPHIDType' => 'applications/phid/storage/type', 'PhabricatorPHIDTypeEditController' => 'applications/phid/controller/typeedit', 'PhabricatorPHIDTypeListController' => 'applications/phid/controller/typelist', 'PhabricatorPeopleController' => 'applications/people/controller/base', 'PhabricatorPeopleEditController' => 'applications/people/controller/edit', 'PhabricatorPeopleListController' => 'applications/people/controller/list', 'PhabricatorPeopleProfileController' => 'applications/people/controller/profile', 'PhabricatorStandardPageView' => 'view/page/standard', 'PhabricatorUser' => 'applications/people/storage/user', 'PhabricatorUserDAO' => 'applications/people/storage/base', ), 'function' => array( '_qsprintf_check_scalar_type' => 'storage/qsprintf', '_qsprintf_check_type' => 'storage/qsprintf', 'qsprintf' => 'storage/qsprintf', 'queryfx' => 'storage/queryfx', 'queryfx_all' => 'storage/queryfx', 'queryfx_one' => 'storage/queryfx', 'vqsprintf' => 'storage/qsprintf', 'vqueryfx' => 'storage/queryfx', 'xsprintf_query' => 'storage/qsprintf', ), 'requires_class' => array( 'Aphront404Response' => 'AphrontResponse', 'AphrontDefaultApplicationConfiguration' => 'AphrontApplicationConfiguration', 'AphrontDefaultApplicationController' => 'AphrontController', 'AphrontDialogResponse' => 'AphrontResponse', 'AphrontDialogView' => 'AphrontView', 'AphrontErrorView' => 'AphrontView', 'AphrontFileResponse' => 'AphrontResponse', 'AphrontFormControl' => 'AphrontView', 'AphrontFormFileControl' => 'AphrontFormControl', 'AphrontFormSelectControl' => 'AphrontFormControl', 'AphrontFormStaticControl' => 'AphrontFormControl', 'AphrontFormSubmitControl' => 'AphrontFormControl', 'AphrontFormTextAreaControl' => 'AphrontFormControl', 'AphrontFormTextControl' => 'AphrontFormControl', 'AphrontFormView' => 'AphrontView', 'AphrontMySQLDatabaseConnection' => 'AphrontDatabaseConnection', 'AphrontNullView' => 'AphrontView', 'AphrontPageView' => 'AphrontView', 'AphrontPanelView' => 'AphrontView', 'AphrontQueryConnectionException' => 'AphrontQueryException', 'AphrontQueryConnectionLostException' => 'AphrontQueryRecoverableException', 'AphrontQueryCountException' => 'AphrontQueryException', 'AphrontQueryObjectMissingException' => 'AphrontQueryException', 'AphrontQueryParameterException' => 'AphrontQueryException', 'AphrontQueryRecoverableException' => 'AphrontQueryException', 'AphrontRedirectResponse' => 'AphrontResponse', + 'AphrontSideNavView' => 'AphrontView', 'AphrontTableView' => 'AphrontView', 'AphrontWebpageResponse' => 'AphrontResponse', + 'ConduitAPI_file_upload_Method' => 'ConduitAPIMethod', + 'PhabricatorConduitAPIController' => 'PhabricatorConduitController', + 'PhabricatorConduitConnectionLog' => 'PhabricatorConduitDAO', + 'PhabricatorConduitConsoleController' => 'PhabricatorConduitController', + 'PhabricatorConduitController' => 'PhabricatorController', + 'PhabricatorConduitDAO' => 'PhabricatorLiskDAO', + 'PhabricatorConduitLogController' => 'PhabricatorConduitController', + 'PhabricatorConduitMethodCallLog' => 'PhabricatorConduitDAO', 'PhabricatorController' => 'AphrontController', 'PhabricatorDirectoryCategory' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryCategoryDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryCategoryListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryController' => 'PhabricatorController', 'PhabricatorDirectoryDAO' => 'PhabricatorLiskDAO', 'PhabricatorDirectoryItem' => 'PhabricatorDirectoryDAO', 'PhabricatorDirectoryItemDeleteController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemEditController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryItemListController' => 'PhabricatorDirectoryController', 'PhabricatorDirectoryMainController' => 'PhabricatorDirectoryController', 'PhabricatorFile' => 'PhabricatorFileDAO', 'PhabricatorFileController' => 'PhabricatorController', 'PhabricatorFileDAO' => 'PhabricatorLiskDAO', 'PhabricatorFileListController' => 'PhabricatorFileController', 'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO', 'PhabricatorFileUploadController' => 'PhabricatorFileController', 'PhabricatorFileViewController' => 'PhabricatorFileController', 'PhabricatorLiskDAO' => 'LiskDAO', 'PhabricatorPHID' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController', 'PhabricatorPHIDController' => 'PhabricatorController', 'PhabricatorPHIDDAO' => 'PhabricatorLiskDAO', 'PhabricatorPHIDListController' => 'PhabricatorPHIDController', 'PhabricatorPHIDType' => 'PhabricatorPHIDDAO', 'PhabricatorPHIDTypeEditController' => 'PhabricatorPHIDController', 'PhabricatorPHIDTypeListController' => 'PhabricatorPHIDController', 'PhabricatorPeopleController' => 'PhabricatorController', 'PhabricatorPeopleEditController' => 'PhabricatorPeopleController', 'PhabricatorPeopleListController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController', 'PhabricatorStandardPageView' => 'AphrontPageView', 'PhabricatorUser' => 'PhabricatorUserDAO', 'PhabricatorUserDAO' => 'PhabricatorLiskDAO', ), 'requires_interface' => array( ), )); diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index da2ab26f6..bed40f15f 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -1,121 +1,127 @@ array( '$' => 'RepositoryListController', 'new/$' => 'RepositoryEditController', 'edit/(?\d+)/$' => 'RepositoryEditController', 'delete/(?\d+)/$' => 'RepositoryDeleteController', ), '/' => array( '$' => 'PhabricatorDirectoryMainController', ), '/directory/' => array( 'item/$' => 'PhabricatorDirectoryItemListController', 'item/edit/(?:(?\d+)/)?$' => 'PhabricatorDirectoryItemEditController', 'item/delete/(?\d+)/' => 'PhabricatorDirectoryItemDeleteController', 'category/$' => 'PhabricatorDirectoryCategoryListController', 'category/edit/(?:(?\d+)/)?$' => 'PhabricatorDirectoryCategoryEditController', 'category/delete/(?\d+)/' => 'PhabricatorDirectoryCategoryDeleteController', ), '/file/' => array( '$' => 'PhabricatorFileListController', 'upload/$' => 'PhabricatorFileUploadController', '(?info)/(?[^/]+)/' => 'PhabricatorFileViewController', '(?view)/(?[^/]+)/' => 'PhabricatorFileViewController', '(?download)/(?[^/]+)/' => 'PhabricatorFileViewController', ), '/phid/' => array( '$' => 'PhabricatorPHIDListController', 'type/$' => 'PhabricatorPHIDTypeListController', 'type/edit/(?:(?\d+)/)?$' => 'PhabricatorPHIDTypeEditController', 'new/$' => 'PhabricatorPHIDAllocateController', ), '/people/' => array( '$' => 'PhabricatorPeopleListController', 'edit/(?:(?\w+)/)?$' => 'PhabricatorPeopleEditController', ), '/p/(?\w+)/$' => 'PhabricatorPeopleProfileController', + '/conduit/' => array( + '$' => 'PhabricatorConduitConsoleController', + 'method/(?[^/]+)$' => 'PhabricatorConduitConsoleController', + 'log/$' => 'PhabricatorConduitLogController', + ), + '/api/(?[^/]+)$' => 'PhabricatorConduitAPIController', '.*' => 'AphrontDefaultApplicationController', ); } public function buildRequest() { $request = new AphrontRequest($this->getHost(), $this->getPath()); $request->setRequestData($_GET + $_POST); return $request; } public function handleException(Exception $ex) { $class = phutil_escape_html(get_class($ex)); $message = phutil_escape_html($ex->getMessage()); $content = '
'. '

Unhandled Exception "'.$class.'": '.$message.'

'. ''.phutil_escape_html((string)$ex).''. '
'; $view = new PhabricatorStandardPageView(); $view->appendChild($content); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); return $response; } public function willSendResponse(AphrontResponse $response) { $request = $this->getRequest(); if ($response instanceof AphrontDialogResponse) { if (!$request->isAjax()) { $view = new PhabricatorStandardPageView(); $view->appendChild( '
'. $response->buildResponseString(). '
'); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); return $response; } } return $response; } } diff --git a/src/applications/conduit/controller/api/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/api/PhabricatorConduitAPIController.php new file mode 100644 index 000000000..c259cf455 --- /dev/null +++ b/src/applications/conduit/controller/api/PhabricatorConduitAPIController.php @@ -0,0 +1,185 @@ +method = $data['method']; + return $this; + } + + public function processRequest() { + $time_start = microtime(true); + $request = $this->getRequest(); + + $method = $this->method; + + $method_class = ConduitAPIMethod::getClassNameFromAPIMethodName($method); + $api_request = null; + + $log = new PhabricatorConduitMethodCallLog(); + $log->setMethod($method); + $metadata = array(); + + try { + + if (!class_exists($method_class)) { + throw new Exception( + "Unable to load the implementation class for method '{$method}'. ". + "You may have misspelled the method, need to define ". + "'{$method_class}', or need to run 'arc build'."); + } + + // Fake out checkModule, the class has already been autoloaded by the + // class_exists() call above. + $method_handler = newv($method_class, array()); + + if (isset($_REQUEST['params']) && is_array($_REQUEST['params'])) { + $params_post = $request->getArr('params'); + foreach ($params_post as $key => $value) { + $params_post[$key] = json_decode($value); + } + $params = $params_post; + } else { + $params_json = $request->getStr('params'); + if (!strlen($params_json)) { + $params = array(); + } else { + $params = json_decode($params_json); + if (!is_array($params)) { + throw new Exception( + "Invalid parameter information was passed to method ". + "'{$method}', could not decode JSON serialization."); + } + } + } + + $metadata = idx($params, '__conduit__', array()); + unset($params['__conduit__']); + + $api_request = new ConduitAPIRequest($params); + + try { + $result = $method_handler->executeMethod($api_request); + $error_code = null; + $error_info = null; + } catch (ConduitException $ex) { + $result = null; + $error_code = $ex->getMessage(); + $error_info = $method_handler->getErrorDescription($error_code); + } + } catch (Exception $ex) { + $result = null; + $error_code = 'ERR-CONDUIT-CORE'; + $error_info = $ex->getMessage(); + } + + $time_end = microtime(true); + + $connection_id = null; + if (idx($metadata, 'connectionID')) { + $connection_id = $metadata['connectionID']; + } else if (($method == 'conduit.connect') && $result) { + $connection_id = idx($result, 'connectionID'); + } + + $log->setConnectionID($connection_id); + $log->setError((string)$error_code); + $log->setDuration(1000000 * ($time_end - $time_start)); + $log->save(); + + $result = array( + 'result' => $result, + 'error_code' => $error_code, + 'error_info' => $error_info, + ); + + switch ($request->getStr('output')) { + case 'human': + return $this->buildHumanReadableResponse( + $method, + $api_request, + $result); + case 'json': + default: + return id(new AphrontFileResponse()) + ->setMimeType('application/json') + ->setContent(json_encode($result)); + } + } + + private function buildHumanReadableResponse( + $method, + ConduitAPIRequest $request = null, + $result = null) { + + $param_rows = array(); + $param_rows[] = array('Method', phutil_escape_html($method)); + if ($request) { + foreach ($request->getAllParameters() as $key => $value) { + $param_rows[] = array( + phutil_escape_html($key), + phutil_escape_html(json_encode($value)), + ); + } + } + + $param_table = new AphrontTableView($param_rows); + $param_table->setColumnClasses( + array( + 'header', + 'wide', + )); + + $result_rows = array(); + foreach ($result as $key => $value) { + $result_rows[] = array( + phutil_escape_html($key), + phutil_escape_html(json_encode($value)), + ); + } + + $result_table = new AphrontTableView($result_rows); + $result_table->setColumnClasses( + array( + 'header', + 'wide', + )); + + $param_panel = new AphrontPanelView(); + $param_panel->setHeader('Method Parameters'); + $param_panel->appendChild($param_table); + + $result_panel = new AphrontPanelView(); + $result_panel->setHeader('Method Result'); + $result_panel->appendChild($result_table); + + return $this->buildStandardPageResponse( + array( + $param_panel, + $result_panel, + ), + array( + 'title' => 'Method Call Result', + )); + } + +} diff --git a/src/applications/conduit/controller/api/__init__.php b/src/applications/conduit/controller/api/__init__.php new file mode 100644 index 000000000..1e6db7a2b --- /dev/null +++ b/src/applications/conduit/controller/api/__init__.php @@ -0,0 +1,21 @@ +setApplicationName('Conduit'); + $page->setBaseURI('/conduit/'); + $page->setTitle(idx($data, 'title')); + $page->setTabs( + array( + 'console' => array( + 'href' => '/conduit/', + 'name' => 'Console', + ), + 'logs' => array( + 'href' => '/conduit/log/', + 'name' => 'Logs', + ), + ), + idx($data, 'tab')); + $page->setGlyph("\xE2\x87\xB5"); + $page->appendChild($view); + + $response = new AphrontWebpageResponse(); + return $response->setContent($page->render()); + } + +} diff --git a/src/applications/conduit/controller/base/__init__.php b/src/applications/conduit/controller/base/__init__.php new file mode 100644 index 000000000..6dd4aa380 --- /dev/null +++ b/src/applications/conduit/controller/base/__init__.php @@ -0,0 +1,16 @@ +method = idx($data, 'method'); + } + + public function processRequest() { + $methods = $this->getAllMethods(); + if (empty($methods[$this->method])) { + $this->method = key($methods); + } + + $method_class = $methods[$this->method]; + PhutilSymbolLoader::loadClass($method_class); + $method_object = newv($method_class, array()); + + + $error_description = array(); + $error_types = $method_object->defineErrorTypes(); + if ($error_types) { + $error_description[] = '
    '; + foreach ($error_types as $error => $meaning) { + $error_description[] = + '
  • '. + ''.phutil_escape_html($error).': '. + phutil_escape_html($meaning). + '
  • '; + } + $error_description[] = '
'; + $error_description = implode("\n", $error_description); + } else { + $error_description = "This method does not raise any specific errors."; + } + + $form = new AphrontFormView(); + $form + ->setAction('/api/'.$this->method) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('Description') + ->setValue( + phutil_escape_html($method_object->getMethodDescription()))) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('Returns') + ->setValue( + phutil_escape_html($method_object->defineReturnType()))) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel('Errors') + ->setValue($error_description)) + ->appendChild( + '

Enter parameters using '. + 'JSON. For instance, to enter a list, type: '. + '["apple", "banana", "cherry"]'); + + $params = $method_object->defineParamTypes(); + foreach ($params as $param => $desc) { + $form->appendChild( + id(new AphrontFormTextControl()) + ->setLabel($param) + ->setName("params[{$param}]") + ->setCaption($desc)); + } + + $form + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel('Output Format') + ->setName('output') + ->setOptions( + array( + 'human' => 'Human Readable', + 'json' => 'JSON', + ))) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue('Call Method')); + + $panel = new AphrontPanelView(); + $panel->setHeader('Conduit API: '.phutil_escape_html($this->method)); + $panel->appendChild($form); + $panel->setWidth(AphrontPanelView::WIDTH_WIDE); + + $view = new AphrontSideNavView(); + foreach ($this->buildNavItems() as $item) { + $view->addNavItem($item); + } + + $view->appendChild($panel); + + return $this->buildStandardPageResponse( + array($view), + array( + 'title' => 'Conduit Console', + 'tab' => 'console', + )); + } + + private function buildNavItems() { + $classes = $this->getAllMethodImplementationClasses(); + $method_names = array(); + foreach ($classes as $method_class) { + $method_name = ConduitAPIMethod::getAPIMethodNameFromClassName( + $method_class); + $method_names[] = array( + 'full_name' => $method_name, + 'group_name' => reset(explode('.', $method_name)), + ); + } + $method_names = igroup($method_names, 'group_name'); + ksort($method_names); + + $items = array(); + foreach ($method_names as $group => $methods) { + $items[] = phutil_render_tag( + 'a', + array( + ), + phutil_escape_html($group)); + foreach ($methods as $method) { + $method_name = $method['full_name']; + $selected = ($method_name == $this->method); + $items[] = phutil_render_tag( + 'a', + array( + 'class' => $selected ? 'aphront-side-nav-selected' : null, + 'href' => '/conduit/method/'.$method_name, + ), + ''. + phutil_escape_html($method_name). + ''); + } + $items[] = '


'; + } + // Pop off the last '
'. + array_pop($items); + + return $items; + } + + private function getAllMethods() { + $classes = $this->getAllMethodImplementationClasses(); + $methods = array(); + foreach ($classes as $class) { + $name = ConduitAPIMethod::getAPIMethodNameFromClassName($class); + $methods[$name] = $class; + } + return $methods; + } + + private function getAllMethodImplementationClasses() { + $classes = id(new PhutilSymbolLoader()) + ->setAncestorClass('ConduitAPIMethod') + ->setType('class') + ->selectSymbolsWithoutLoading(); + return array_values(ipull($classes, 'name')); + } + +} diff --git a/src/applications/conduit/controller/console/__init__.php b/src/applications/conduit/controller/console/__init__.php new file mode 100644 index 000000000..43e8fc4f1 --- /dev/null +++ b/src/applications/conduit/controller/console/__init__.php @@ -0,0 +1,22 @@ +buildStandardPageResponse('stuff', array( + 'title' => 'Conduit Logs', + 'tab' => 'logs', + )); + } +} diff --git a/src/applications/conduit/controller/log/__init__.php b/src/applications/conduit/controller/log/__init__.php new file mode 100644 index 000000000..cbe5f7c47 --- /dev/null +++ b/src/applications/conduit/controller/log/__init__.php @@ -0,0 +1,12 @@ +defineErrorTypes(), $error_code, 'Unknown Error'); + } + + public function executeMethod(ConduitAPIRequest $request) { + return $this->execute($request); + } + + public function getAPIMethodName() { + return self::getAPIMethodNameFromClassName(get_class($this)); + } + + public static function getClassNameFromAPIMethodName($method_name) { + $method_fragment = str_replace('.', '_', $method_name); + return 'ConduitAPI_'.$method_fragment.'_Method'; + } + + public static function getAPIMethodNameFromClassName($class_name) { + $match = null; + $is_valid = preg_match( + '/^ConduitAPI_(.*)_Method$/', + $class_name, + $match); + if (!$is_valid) { + throw new Exception( + "Parameter '{$class_name}' is not a valid Conduit API method class."); + } + $method_fragment = $match[1]; + return str_replace('_', '.', $method_fragment); + } + +} diff --git a/src/applications/conduit/method/base/__init__.php b/src/applications/conduit/method/base/__init__.php new file mode 100644 index 000000000..f7268ea66 --- /dev/null +++ b/src/applications/conduit/method/base/__init__.php @@ -0,0 +1,12 @@ + 'required nonempty base64-bytes', + 'name' => 'optional string', + ); + } + + public function defineReturnType() { + return 'nonempty guid'; + } + + public function defineErrorTypes() { + return array( + ); + } + + protected function execute(ConduitAPIRequest $request) { + $data = $request->getValue('data_base64'); + $name = $request->getValue('name'); + $data = base64_decode($data, $strict = true); + + $file = PhabricatorFile::newFromFileData( + $data, + array( + 'name' => $name + )); + return $file->getPHID(); + } + +} diff --git a/src/applications/conduit/method/file/upload/__init__.php b/src/applications/conduit/method/file/upload/__init__.php new file mode 100644 index 000000000..5b0c10c04 --- /dev/null +++ b/src/applications/conduit/method/file/upload/__init__.php @@ -0,0 +1,13 @@ +params = $params; + } + + public function getValue($key) { + return $this->params[$key]; + } + + public function getAllParameters() { + return $this->params; + } + +} diff --git a/src/applications/conduit/protocol/request/__init__.php b/src/applications/conduit/protocol/request/__init__.php new file mode 100644 index 000000000..5aa52afcf --- /dev/null +++ b/src/applications/conduit/protocol/request/__init__.php @@ -0,0 +1,10 @@ +createButton = phutil_render_tag( 'a', array( 'href' => $href, 'class' => 'create-button button green', ), $create_button); return $this; } public function setHeader($header) { $this->header = $header; return $this; } public function setWidth($width) { $this->width = $width; return $this; } public function render() { if ($this->header !== null) { $header = '

'.$this->header.'

'; } else { $header = null; } if ($this->createButton !== null) { $button = $this->createButton; } else { $button = null; } $table = $this->renderChildren(); $class = array('aphront-panel-view'); if ($this->width) { $class[] = 'aphront-panel-width-'.$this->width; } return '
'. $button. $header. $table. '
'; } } diff --git a/src/view/layout/sidenav/AphrontSideNavView.php b/src/view/layout/sidenav/AphrontSideNavView.php new file mode 100755 index 000000000..c0ababfad --- /dev/null +++ b/src/view/layout/sidenav/AphrontSideNavView.php @@ -0,0 +1,45 @@ +items[] = $item; + return $this; + } + + public function render() { + $view = new AphrontNullView(); + $view->appendChild($this->items); + + return + ''. + ''. + ''. + ''. + ''. + '
'. + $view->render(). + ''. + $this->renderChildren(). + '
'; + } + +} diff --git a/src/view/layout/sidenav/__init__.php b/src/view/layout/sidenav/__init__.php new file mode 100644 index 000000000..b32c493b6 --- /dev/null +++ b/src/view/layout/sidenav/__init__.php @@ -0,0 +1,13 @@ +