Committing changes from Scigap Repo.
Project: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/repo Commit: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/commit/cae5a4dc Tree: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/tree/cae5a4dc Diff: http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/diff/cae5a4dc Branch: refs/heads/master Commit: cae5a4dc6e9a74c969708fd8862a27ea61a9d172 Parents: c644bc8 Author: Nipurn Doshi <[email protected]> Authored: Thu Apr 16 17:07:35 2015 -0400 Committer: Nipurn Doshi <[email protected]> Committed: Thu Apr 16 17:07:35 2015 -0400 ---------------------------------------------------------------------- app/config/app_config.ini | 9 +- app/controllers/AdminController.php | 35 +- app/controllers/ExperimentController.php | 51 +- app/controllers/GatewayprofileController.php | 8 +- app/libraries/ExpUtilities.php | 996 +++++++++++++++++++ app/libraries/utilities.php | 83 +- app/routes.php | 9 +- app/views/admin/manage-credentials.blade.php | 164 +++ app/views/admin/manage-experiments.blade.php | 53 + app/views/admin/manage-gateway.blade.php | 13 +- app/views/admin/manage-roles.blade.php | 2 +- app/views/admin/manage-users.blade.php | 92 +- app/views/experiment/create-complete.blade.php | 80 +- app/views/experiment/create.blade.php | 10 + app/views/experiment/edit.blade.php | 166 +--- app/views/experiment/summary.blade.php | 7 +- app/views/partials/dashboard-block.blade.php | 8 +- app/views/partials/experiment-inputs.blade.php | 102 ++ .../partials/gateway-preferences.blade.php | 11 +- 19 files changed, 1578 insertions(+), 321 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/config/app_config.ini ---------------------------------------------------------------------- diff --git a/app/config/app_config.ini b/app/config/app_config.ini index a990f12..4adabe4 100644 --- a/app/config/app_config.ini +++ b/app/config/app_config.ini @@ -6,13 +6,14 @@ ;Tenant admin's username -;admin-username = "[email protected]" -admin-username = "[email protected]" +admin-username = "[email protected]" +;admin-username = "[email protected]" ;Super Tenant Admin's username ;admin-username = "scigap_admin" ;Tenant admin's password -admin-password = "sp@m!d!g2015" +admin-password = "[email protected]" +;admin-password = "sp@m!d!g2015" ;Super Admin's password ;admin-password = "sci9067@min" @@ -24,7 +25,7 @@ service-url = "https://idp.scigap.org:7443/services/" ;Gateway Domain Name ;gateway-id = "testphprg.scigap.org" -gateway-id = "paramchem" +gateway-id = "default" ;Path the to server certificate file cafile-path = "/resources/security/idp_scigap_org.pem" http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/controllers/AdminController.php ---------------------------------------------------------------------- diff --git a/app/controllers/AdminController.php b/app/controllers/AdminController.php index d0426f8..c55718e 100644 --- a/app/controllers/AdminController.php +++ b/app/controllers/AdminController.php @@ -71,21 +71,11 @@ class AdminController extends BaseController { //check if username exists if( $idStore->username_exists( Input::get("username")) ) { - //first add if this role does not exist - $gatewayName = str_replace(" ", "_", Input::get("gateway_name")); + //add user to admin role $app_config = Utilities::read_config(); - $role = $app_config["gateway-role-prepend"] . $gatewayName . $app_config["gateway-role-admin-append"]; - //var_dump( $role); //exit; - //$role = "gateway_default_b8a153f1-6291_admin"; - if( ! $idStore->isExistingRole( $role) ) - { - $idStore->addRole( $role ); - } + $idStore->updateRoleListOfUser( Input::get("username"), array( "new"=>array( $app_config["admin-role-name"]), "deleted"=>array() ) ); - //add user to gateway_admin role - $idStore->updateRoleListOfUser( Input::get("username"), array( "new"=>array( $role), "deleted"=>array() ) ); - - return Redirect::to("manage/admins")->with("Gateway Admin has been added."); + return Redirect::to("admin/dashboard/users?role=" . $app_config["admin-role-name"])->with("Gateway Admin has been added."); } else @@ -101,6 +91,13 @@ class AdminController extends BaseController { return View::make("admin/manage-roles", array("roles" => $roles)); } + public function experimentsView(){ + $idStore = $this->idStore; + //$roles = $idStore->getExperiments(); + + return View::make("admin/manage-experiments" ); + } + public function addRole(){ $idStore = $this->idStore; @@ -108,6 +105,12 @@ class AdminController extends BaseController { return Redirect::to("admin/dashboard/roles")->with( "message", "Role has been added."); } + public function getRoles(){ + $idStore = $this->idStore; + + return json_encode( (array)$idStore->getRoleListOfUser( Input::get("username") ) ); + } + public function deleteRole(){ $idStore = $this->idStore; @@ -115,4 +118,10 @@ class AdminController extends BaseController { return Redirect::to("admin/dashboard/roles")->with( "message", "Role has been deleted."); } + + public function credentialStoreView(){ + $idStore = $this->idStore; + + return View::make("admin/manage-credentials", array("tokens" => array()) ); + } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/controllers/ExperimentController.php ---------------------------------------------------------------------- diff --git a/app/controllers/ExperimentController.php b/app/controllers/ExperimentController.php index 95caf97..b82a706 100755 --- a/app/controllers/ExperimentController.php +++ b/app/controllers/ExperimentController.php @@ -14,32 +14,34 @@ class ExperimentController extends BaseController { public function createView() { - //var_dump( Session::all()); exit; Session::forget( 'exp_create_continue'); return View::make('experiment/create'); } public function createSubmit() { + $inputs = Input::all(); + if( isset( $_POST['continue'] )) { Session::put( 'exp_create_continue', true); $app_config = Utilities::read_config(); - return View::make( "experiment/create-complete", array( + $experimentInputs = array( "disabled" => ' disabled', "experimentName" => $_POST['experiment-name'], "experimentDescription" => $_POST['experiment-description'] . ' ', "project" => $_POST['project'], "application" => $_POST['application'], "allowedFileSize" => $app_config["server-allowed-file-size"], - // ugly hack until app catalog is in place. - //App catalog is in place. But, I'm not sure what this does so not removing it. "echo" => ($_POST['application'] == 'Echo')? ' selected' : '', - "wrf" => ($_POST['application'] == 'WRF')? ' selected' : '' - - ) - ); + "wrf" => ($_POST['application'] == 'WRF')? ' selected' : '', + "queueName" => $app_config["queue-name"], + "nodeCount" => $app_config["node-count"], + "cpuCount" => $app_config["total-cpu-count"], + "wallTimeLimit" => $app_config["wall-time-limit"] + ); + return View::make( "experiment/create-complete", array( "expInputs" => $experimentInputs) ); } else if (isset($_POST['save']) || isset($_POST['launch'])) @@ -75,6 +77,10 @@ class ExperimentController extends BaseController { if( $expVal["experimentStatusString"] == "FAILED") $expVal["editable"] = false; + $expVal["cancelable"] = false; + if( $expVal["experimentStatusString"] == "LAUNCHED" || $expVal["experimentStatusString"] == "EXECUTING" ) + $expVal["cancelable"] = true; + if( Request::ajax() ) { return json_encode( $experiment); @@ -96,6 +102,13 @@ class ExperimentController extends BaseController { return View::make( "experiment/summary", array("invalidExperimentId" => 1)); } + public function expCancel() + { + Utilities::cancel_experiment( Input::get("expId")); + + return Redirect::to('experiment/summary?expId=' . Input::get("expId")); + } + public function expChange() { //var_dump( Input::all() ); exit; @@ -136,20 +149,24 @@ class ExperimentController extends BaseController { public function editView() { + $app_config = Utilities::read_config(); $experiment = Utilities::get_experiment($_GET['expId']); $project = Utilities::get_project($experiment->projectID); $expVal = Utilities::get_experiment_values( $experiment, $project); - - return View::make("experiment/edit", array( - - 'experiment' => $experiment, - 'project' => $project, - 'expVal' => $expVal - - ) - ); + $experimentInputs = array( + "disabled" => ' ', + "experimentName" => $experiment->name, + "experimentDescription" => $experiment->description, + "application" => $experiment->applicationId, + "allowedFileSize" => $app_config["server-allowed-file-size"], + 'experiment' => $experiment, + 'project' => $project, + 'expVal' => $expVal, + 'cloning' => true + ); + return View::make("experiment/edit", array("expInputs" => $experimentInputs) ); } public function editSubmit() http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/controllers/GatewayprofileController.php ---------------------------------------------------------------------- diff --git a/app/controllers/GatewayprofileController.php b/app/controllers/GatewayprofileController.php index 5d9fbe5..24b8589 100644 --- a/app/controllers/GatewayprofileController.php +++ b/app/controllers/GatewayprofileController.php @@ -42,7 +42,7 @@ class GatewayprofileController extends BaseController { { if( CRUtilities::add_or_update_CRP( Input::all()) ) { - return Redirect::to("gp/browse")->with("message","Compute Resource Preference for the desired Gateway has been set."); + return Redirect::to("admin/dashboard/gateway")->with("message","Compute Resource Preference for the desired Gateway has been set."); } } @@ -53,14 +53,14 @@ class GatewayprofileController extends BaseController { if( Input::has("del-gpId")) // if Gateway has to be deleted { if( CRUtilities::deleteGP( Input::get("del-gpId")) ) - return Redirect::to("gp/browse")->with("message","Gateway Profile has been deleted."); + return Redirect::to("admin/dashboard/gateway")->with("message","Gateway Profile has been deleted."); else $error = true; } else if( Input::has("rem-crId")) // if Compute Resource has to be removed from Gateway { if(CRUtilities::deleteCR( Input::all()) ) - return Redirect::to("gp/browse")->with("message", "The selected Compute Resource has been successfully removed"); + return Redirect::to("admin/dashboard/gateway")->with("message", "The selected Compute Resource has been successfully removed"); else $error = true; } @@ -70,7 +70,7 @@ class GatewayprofileController extends BaseController { if( $error) { - return Redirect::to("gp/browse")->with("message","An error has occurred. Please try again later or report a bug using the link in the Help menu"); + return Redirect::to("admin/dashboard/gateway")->with("message","An error has occurred. Please try again later or report a bug using the link in the Help menu"); } } http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/libraries/ExpUtilities.php ---------------------------------------------------------------------- diff --git a/app/libraries/ExpUtilities.php b/app/libraries/ExpUtilities.php new file mode 100644 index 0000000..40e5d36 --- /dev/null +++ b/app/libraries/ExpUtilities.php @@ -0,0 +1,996 @@ +<?php + +//Thrift classes - loaded from Vendor/Thrift +use Thrift\Transport\TTransport; +use Thrift\Exception\TException; +use Thrift\Exception\TTransportException; +use Thrift\Factory\TStringFuncFactory; +use Thrift\Protocol\TBinaryProtocol; +use Thrift\Transport\TSocket; + +//Airavata classes - loaded from app/libraries/Airavata +use Airavata\API\AiravataClient; +use Airavata\API\Error\InvalidRequestException; +use Airavata\API\Error\AiravataClientException; +use Airavata\API\Error\AiravataSystemException; +use Airavata\API\Error\ExperimentNotFoundException; +use Airavata\Model\Workspace\Experiment\ComputationalResourceScheduling; +use Airavata\Model\AppCatalog\AppInterface\InputDataObjectType; +use Airavata\Model\Workspace\Experiment\UserConfigurationData; +use Airavata\Model\Workspace\Experiment\AdvancedOutputDataHandling; +use Airavata\Model\Workspace\Experiment\Experiment; +use Airavata\Model\Workspace\Experiment\ExperimentState; +use Airavata\Model\AppCatalog\AppInterface\DataType; +use Airavata\Model\Workspace\Experiment\JobState; +use Airavata\Model\AppCatalog\ComputeResource\JobSubmissionInterface; +use Airavata\Model\AppCatalog\ComputeResource\JobSubmissionProtocol; + +class ExpUtilities{ + +/** + * Launch the experiment with the given ID + * @param $expId + */ +public static function launch_experiment($expId) +{ + $airavataclient = Session::get("airavataClient"); + //global $tokenFilePath; + try + { + /* temporarily using hard-coded token + open_tokens_file($tokenFilePath); + + $communityToken = $tokenFile->tokenId; + + + $token = isset($_SESSION['tokenId'])? $_SESSION['tokenId'] : $communityToken; + + $airavataclient->launchExperiment($expId, $token); + + $tokenString = isset($_SESSION['tokenId'])? 'personal' : 'community'; + + Utilities::print_success_message('Experiment launched using ' . $tokenString . ' allocation!'); + */ + + $hardCodedToken = 'bdc612fe-401e-4684-88e9-317f99409c45'; + $airavataclient->launchExperiment($expId, $hardCodedToken); + + /* + Utilities::print_success_message('Experiment launched!'); + Utilities::print_success_message("<p>Experiment launched!</p>" . + '<p>You will be redirected to the summary page shortly, or you can + <a href="experiment_summary.php?expId=' . $expId . '">go directly</a> to the experiment summary page.</p>'); + redirect('experiment_summary.php?expId=' . $expId); + */ + } + catch (InvalidRequestException $ire) + { + Utilities::print_error_message('<p>There was a problem launching the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>InvalidRequestException: ' . $ire->getMessage() . '</p>'); + } + catch (ExperimentNotFoundException $enf) + { + Utilities::print_error_message('<p>There was a problem launching the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>ExperimentNotFoundException: ' . $enf->getMessage() . '</p>'); + } + catch (AiravataClientException $ace) + { + Utilities::print_error_message('<p>There was a problem launching the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataClientException: ' . $ace->getMessage() . '</p>'); + } + catch (AiravataSystemException $ase) + { + Utilities::print_error_message('<p>There was a problem launching the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataSystemException: ' . $ase->getMessage() . '</p>'); + } + catch (Exception $e) + { + Utilities::print_error_message('<p>There was a problem launching the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>Exception: ' . $e->getMessage() . '</p>'); + } +} + + +/** + * List the experiment's input files + * @param $experiment + */ +public static function list_input_files($experiment) +{ + $applicationInputs = Utilities::get_application_inputs($experiment->applicationId); + + $experimentInputs = $experiment->experimentInputs; + + + //showing experiment inputs in the order defined by the admins. + $order = array(); + foreach ($experimentInputs as $index => $input) + { + $order[$index] = $input->inputOrder; + } + array_multisort($order, SORT_ASC, $experimentInputs); + + foreach ($experimentInputs as $input) + { + $matchingAppInput = null; + + foreach($applicationInputs as $applicationInput) + { + if ($input->name == $applicationInput->name) + { + $matchingAppInput = $applicationInput; + } + } + //var_dump($matchingAppInput); + + if ($matchingAppInput->type == DataType::URI) + { + $explode = explode('/', $input->value); + echo '<p><a target="_blank" + href="' . URL::to("/") . "/../../" . Constant::EXPERIMENT_DATA_ROOT . $explode[sizeof($explode)-2] . '/' . $explode[sizeof($explode)-1] . '">' . + $explode[sizeof($explode)-1] . ' + <span class="glyphicon glyphicon-new-window"></span></a></p>'; + } + elseif ($matchingAppInput->type == DataType::STRING) + { + echo '<p>' . $input->name . ': ' . $input->value . '</p>'; + } + } +} + + +/** + * Get a list of the inputs for the application with the given ID + * @param $id + * @return null + */ +public static function get_application_inputs($id) +{ + $airavataclient = Session::get("airavataClient"); + $inputs = null; + + try + { + $inputs = $airavataclient->getApplicationInputs($id); + } + catch (InvalidRequestException $ire) + { + Utilities::print_error_message('<p>There was a problem getting application inputs. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>InvalidRequestException: ' . $ire->getMessage() . '</p>'); + } + catch (AiravataClientException $ace) + { + Utilities::print_error_message('<p>There was a problem getting application inputs. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>Airavata Client Exception: ' . $ace->getMessage() . '</p>'); + } + catch (AiravataSystemException $ase) + { + Utilities::print_error_message('<p>There was a problem getting application inputs. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>Airavata System Exception: ' . $ase->getMessage() . '</p>'); + } + + return $inputs; +} + + +/** + * Get a list of the outputs for the application with the given ID + * @param $id + * @return null + */ +public static function get_application_outputs($id) +{ + $airavataclient = Session::get("airavataClient"); + $outputs = null; + + try + { + $outputs = $airavataclient->getApplicationOutputs($id); + } + catch (InvalidRequestException $ire) + { + Utilities::print_error_message('<p>There was a problem getting application outputs. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>InvalidRequestException: ' . $ire->getMessage() . '</p>'); + } + catch (AiravataClientException $ace) + { + Utilities::print_error_message('<p>There was a problem getting application outputs. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>Airavata Client Exception: ' . $ace->getMessage() . '</p>'); + } + catch (AiravataSystemException $ase) + { + Utilities::print_error_message('<p>There was a problem getting application outputs. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>Airavata System Exception: ' . $ase->getMessage() . '</p>'); + } + + return $outputs; +} + + +/** + * Get the experiment with the given ID + * @param $expId + * @return null + */ +public static function get_experiment($expId) +{ + $airavataclient = Session::get("airavataClient"); + + try + { + return $airavataclient->getExperiment($expId); + } + catch (InvalidRequestException $ire) + { + Utilities::print_error_message('<p>There was a problem getting the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>InvalidRequestException: ' . $ire->getMessage() . '</p>'); + } + catch (ExperimentNotFoundException $enf) + { + Utilities::print_error_message('<p>There was a problem getting the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>ExperimentNotFoundException: ' . $enf->getMessage() . '</p>'); + } + catch (AiravataClientException $ace) + { + Utilities::print_error_message('<p>There was a problem getting the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataClientException: ' . $ace->getMessage() . '</p>'); + } + catch (AiravataSystemException $ase) + { + Utilities::print_error_message('<p>There was a problem getting the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataSystemException: ' . $ase->getMessage() . '</p>'); + } + catch (TTransportException $tte) + { + Utilities::print_error_message('<p>There was a problem getting the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>TTransportException: ' . $tte->getMessage() . '</p>'); + } + catch (Exception $e) + { + Utilities::print_error_message('<p>There was a problem getting the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>Exception: ' . $e->getMessage() . '</p>'); + } + +} + + + +/** + * Create and configure a new Experiment + * @return Experiment + */ +public static function assemble_experiment() +{ + $utility = new Utilities(); + $experimentInputs = array(); + + $scheduling = new ComputationalResourceScheduling(); + $scheduling->totalCPUCount = $_POST['cpu-count']; + $scheduling->nodeCount = $_POST['node-count']; + $scheduling->queueName = $_POST['queue-name']; + //$scheduling->numberOfThreads = $_POST['threads']; + //$scheduling->queueName = 'normal'; + $scheduling->wallTimeLimit = $_POST['wall-time']; + //$scheduling->totalPhysicalMemory = $_POST['memory']; + $scheduling->resourceHostId = $_POST['compute-resource']; + + $userConfigData = new UserConfigurationData(); + $userConfigData->computationalResourceScheduling = $scheduling; + + $applicationInputs = Utilities::get_application_inputs($_POST['application']); + $experimentInputs = Utilities::process_inputs($applicationInputs, $experimentInputs); + //var_dump($experimentInputs); + + if( Utilities::$experimentPath != null){ + $advHandling = new AdvancedOutputDataHandling(); + + $advHandling->outputDataDir = str_replace( base_path() . Constant::EXPERIMENT_DATA_ROOT, Utilities::$pathConstant , Utilities::$experimentPath); + $userConfigData->advanceOutputDataHandling = $advHandling; + } + + //TODO: replace constructor with a call to airvata to get a prepopulated experiment template + $experiment = new Experiment(); + + // required + $experiment->projectID = $_POST['project']; + $experiment->userName = Session::get( 'username'); + $experiment->name = $_POST['experiment-name']; + + // optional + $experiment->description = $_POST['experiment-description']; + $experiment->applicationId = $_POST['application']; + $experiment->userConfigurationData = $userConfigData; + $experiment->experimentInputs = $experimentInputs; + if( isset( $_POST["emailNotification"])) + { + $experiment->emailNotification = intval( $_POST["emailNotification"] ); + $experiment->emailAddresses = array_unique( array_filter( $_POST["emailAddresses"], "trim") ); + } + + // adding default experiment outputs for now till prepoulated experiment template is not implemented. + $experiment->experimentOutputs = Utilities::get_application_outputs( $_POST["application"]); + + if ($experimentInputs) + { + return $experiment; + } +} + +/** + * @param $applicationInputs + * @param $experimentInputs + * @internal param $environmentPath + * @return array + */ +public static function process_inputs($applicationInputs, $experimentInputs) +{ + $utility = new Utilities(); + $experimentAssemblySuccessful = true; + $newExperimentInputs = array(); + + //var_dump($_FILES); + + if (sizeof($_FILES) > 0) + { + if (Utilities::file_upload_successful()) + { + // construct unique path + do + { + Utilities::$experimentPath = base_path() . Constant::EXPERIMENT_DATA_ROOT . str_replace(' ', '', Session::get('username') ) . md5(rand() * time()) . '/'; + } + while (is_dir( Utilities::$experimentPath)); // if dir already exists, try again + + //var_dump( Utilities::$experimentPath ); exit; + // create upload directory + if (!mkdir( Utilities::$experimentPath)) + { + Utilities::print_error_message('<p>Error creating upload directory! + Please try again later or report a bug using the link in the Help menu.</p>'); + $experimentAssemblySuccessful = false; + } + } + else + { + $experimentAssemblySuccessful = false; + } + } + + //sending application inputs in the order defined by the admins. + $order = array(); + foreach ($applicationInputs as $index => $input) + { + $order[$index] = $input->inputOrder; + } + array_multisort($order, SORT_ASC, $applicationInputs); + + foreach ($applicationInputs as $applicationInput) + { + $experimentInput = new InputDataObjectType(); + $experimentInput = $applicationInput; + //$experimentInput->name = $applicationInput->name; + //$experimentInput->metaData = $applicationInput->metaData; + + + //$experimentInput->type = $applicationInput->type; + //$experimentInput->type = DataType::STRING; + + + if(($applicationInput->type == DataType::STRING) || + ($applicationInput->type == DataType::INTEGER) || + ($applicationInput->type == DataType::FLOAT)) + { + if (isset($_POST[$applicationInput->name]) && (trim($_POST[$applicationInput->name]) != '')) + { + $experimentInput->value = $_POST[$applicationInput->name]; + $experimentInput->type = $applicationInput->type; + + } + else // use previous value + { + $index = -1; + for ($i = 0; $i < sizeof($experimentInputs); $i++) + { + if ($experimentInputs[$i]->name == $applicationInput->name) + { + $index = $i; + } + } + + if ($index >= 0) + { + $experimentInput->value = $experimentInputs[$index]->value; + $experimentInput->type = $applicationInput->type; + } + } + } + elseif ($applicationInput->type == DataType::URI) + { + //var_dump($_FILES[$applicationInput->name]->name); + if ($_FILES[$applicationInput->name]['name']) + { + $file = $_FILES[$applicationInput->name]; + + + // + // move file to experiment data directory + // + $filePath = Utilities::$experimentPath . $file['name']; + + // check if file already exists + if (is_file($filePath)) + { + unlink($filePath); + + Utilities::print_warning_message('Uploaded file already exists! Overwriting...'); + } + + $moveFile = move_uploaded_file($file['tmp_name'], $filePath); + + if ($moveFile) + { + Utilities::print_success_message('Upload: ' . $file['name'] . '<br>' . + 'Type: ' . $file['type'] . '<br>' . + 'Size: ' . ($file['size']/1024) . ' kB');//<br>' . + //'Stored in: ' . $experimentPath . $file['name']); + } + else + { + Utilities::print_error_message('<p>Error moving uploaded file ' . $file['name'] . '! + Please try again later or report a bug using the link in the Help menu.</p>'); + $experimentAssemblySuccessful = false; + } + + $experimentInput->value = str_replace(base_path() . Constant::EXPERIMENT_DATA_ROOT, Utilities::$pathConstant , $filePath); + $experimentInput->type = $applicationInput->type; + + } + else + { + $index = -1; + for ($i = 0; $i < sizeof($experimentInputs); $i++) + { + if ($experimentInputs[$i]->name == $applicationInput->name) + { + $index = $i; + } + } + + if ($index >= 0) + { + $experimentInput->value = $experimentInputs[$index]->value; + $experimentInput->type = $applicationInput->type; + } + } + + } + else + { + Utilities::print_error_message('I cannot accept this input type yet!'); + } + + + + + + + + //$experimentInputs[] = $experimentInput; + /* + $index = -1; + for ($i = 0; $i < sizeof($experimentInputs); $i++) + { + if ($experimentInputs[$i]->key == $experimentInput->key) + { + $index = $i; + } + } + + if ($index >= 0) + { + unset($experimentInputs[$index]); + } + */ + //$experimentInputs[] = $experimentInput; + + + + + + $newExperimentInputs[] = $experimentInput; + + + } + + if ($experimentAssemblySuccessful) + { + return $newExperimentInputs; + } + else + { + return false; + } + +} + + +/** + * Check the uploaded files for errors + */ +public static function file_upload_successful() +{ + $uploadSuccessful = true; + + foreach ($_FILES as $file) + { + //var_dump($file); + if($file['name']) + { + if ($file['error'] > 0) + { + $uploadSuccessful = false; + Utilities::print_error_message('<p>Error uploading file ' . $file['name'] . ' ! + Please try again later or report a bug using the link in the Help menu.'); + }/* + elseif ($file['type'] != 'text/plain') + { + $uploadSuccessful = false; + Utilities::print_error_message('Uploaded file ' . $file['name'] . ' type not supported!'); + } + elseif (($file['size'] / 1024) > 20) + { + $uploadSuccessful = false; + Utilities::print_error_message('Uploaded file ' . $file['name'] . ' must be smaller than 10 MB!'); + }*/ + } + + + } + + return $uploadSuccessful; +} + + +/** + * Update the experiment with the given ID + * @param $expId + * @param $updatedExperiment + */ +public static function update_experiment($expId, $updatedExperiment) +{ + $airavataclient = Session::get("airavataClient"); + + try + { + $airavataclient->updateExperiment($expId, $updatedExperiment); + + /* + Utilities::print_success_message("<p>Experiment updated!</p>" . + '<p>Click + <a href="' . URL::to('/') . '/experiment/summary?expId=' . $expId . '">here</a> to visit the experiment summary page.</p>'); + */ + } + catch (InvalidRequestException $ire) + { + Utilities::print_error_message('<p>There was a problem updating the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>InvalidRequestException: ' . $ire->getMessage() . '</p>'); + } + catch (ExperimentNotFoundException $enf) + { + Utilities::print_error_message('<p>There was a problem updating the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>ExperimentNotFoundException: ' . $enf->getMessage() . '</p>'); + } + catch (AiravataClientException $ace) + { + Utilities::print_error_message('<p>There was a problem updating the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataClientException: ' . $ace->getMessage() . '</p>'); + } + catch (AiravataSystemException $ase) + { + Utilities::print_error_message('<p>There was a problem updating the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataSystemException: ' . $ase->getMessage() . '</p>'); + } + +} + + +/** + * Clone the experiment with the given ID + * @param $expId + */ +public static function clone_experiment($expId) +{ + $airavataclient = Session::get("airavataClient"); + + try + { + //create new experiment to receive the clone + $experiment = $airavataclient->getExperiment($expId); + + $cloneId = $airavataclient->cloneExperiment($expId, 'Clone of ' . $experiment->name); + + Utilities::print_success_message("<p>Experiment cloned!</p>" . + '<p>You will be redirected to the edit page shortly, or you can + <a href="edit_experiment.php?expId=' . $cloneId . '">go directly</a> to the edit experiment page.</p>'); + //redirect('edit_experiment.php?expId=' . $cloneId); + return $cloneId; + } + catch (InvalidRequestException $ire) + { + Utilities::print_error_message('<p>There was a problem cloning the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>InvalidRequestException: ' . $ire->getMessage() . '</p>'); + } + catch (ExperimentNotFoundException $enf) + { + Utilities::print_error_message('<p>There was a problem cloning the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>ExperimentNotFoundException: ' . $enf->getMessage() . '</p>'); + } + catch (AiravataClientException $ace) + { + Utilities::print_error_message('<p>There was a problem cloning the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataClientException: ' . $ace->getMessage() . '</p>'); + } + catch (AiravataSystemException $ase) + { + Utilities::print_error_message('<p>There was a problem cloning the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataSystemException: ' . $ase->getMessage() . '</p>'); + } + catch (TTransportException $tte) + { + Utilities::print_error_message('<p>There was a problem cloning the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>TTransportException: ' . $tte->getMessage() . '</p>'); + } +} + +/** + * Cancel the experiment with the given ID + * @param $expId + */ +public static function cancel_experiment($expId) +{ + $airavataclient = Session::get("airavataClient"); + + try + { + $airavataclient->terminateExperiment($expId); + + Utilities::print_success_message("Experiment canceled!"); + } + catch (InvalidRequestException $ire) + { + Utilities::print_error_message('<p>There was a problem canceling the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>InvalidRequestException: ' . $ire->getMessage() . '</p>'); + } + catch (ExperimentNotFoundException $enf) + { + Utilities::print_error_message('<p>There was a problem canceling the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>ExperimentNotFoundException: ' . $enf->getMessage() . '</p>'); + } + catch (AiravataClientException $ace) + { + Utilities::print_error_message('<p>There was a problem canceling the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataClientException: ' . $ace->getMessage() . '</p>'); + } + catch (AiravataSystemException $ase) + { + Utilities::print_error_message('<p>There was a problem canceling the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>AiravataSystemException: ' . $ase->getMessage() . '</p>'); + } + catch (TTransportException $tte) + { + Utilities::print_error_message('<p>There was a problem canceling the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>TTransportException: ' . $tte->getMessage() . '</p>'); + } + catch (Exception $e) + { + Utilities::print_error_message('<p>There was a problem canceling the experiment. + Please try again later or submit a bug report using the link in the Help menu.</p>' . + '<p>Exception: ' . $e->getMessage() . '</p>'); + } +} + + +/** + * Create a new experiment from the values submitted in the form + * @return null + */ +public static function create_experiment() +{ + $airavataclient = Session::get("airavataClient"); + + $experiment = Utilities::assemble_experiment(); + //var_dump($experiment); exit; + $expId = null; + + try + { + if($experiment) + { + $expId = $airavataclient->createExperiment( Session::get("gateway_id"), $experiment); + } + + if ($expId) + { + /* + Utilities::print_success_message("Experiment {$_POST['experiment-name']} created!" . + ' <a href="experiment_summary.php?expId=' . $expId . '">Go to experiment summary page</a>'); + */ + } + else + { + Utilities::print_error_message("Error creating experiment {$_POST['experiment-name']}!"); + } + } + catch (InvalidRequestException $ire) + { + Utilities::print_error_message('InvalidRequestException!<br><br>' . $ire->getMessage()); + } + catch (AiravataClientException $ace) + { + Utilities::print_error_message('AiravataClientException!<br><br>' . $ace->getMessage()); + } + catch (AiravataSystemException $ase) + { + Utilities::print_error_message('AiravataSystemException!<br><br>' . $ase->getMessage()); + } + + return $expId; +} + +/* + * Required in Experiment Sumamry page. + * +*/ + +public static function list_output_files($experiment, $expStatus) +{ + $expStatusVal = array_search($expStatus, ExperimentState::$__names); + + if( $expStatusVal == ExperimentState::COMPLETED ) + { + $utility = new Utilities(); + $experimentOutputs = $experiment->experimentOutputs; + foreach ((array)$experimentOutputs as $output) + { + if ($output->type == DataType::URI || $output->type == DataType::STDOUT || $output->type == DataType::STDERR ) + { + //echo '<p>' . $output->key . ': <a href="' . $output->value . '">' . $output->value . '</a></p>'; + $outputPath = str_replace(Utilities::$experimentDataPathAbsolute, Constant::EXPERIMENT_DATA_ROOT, $output->value); + $outputPathArray = explode("/", $outputPath); + + echo '<p>' . $output->name . ' : ' . '<a target="_blank" + href="' . URL::to("/") . "/.." . str_replace(Utilities::$experimentDataPathAbsolute, Constant::EXPERIMENT_DATA_ROOT, $output->value) . '">' . + $outputPathArray[ sizeof( $outputPathArray) - 1] . ' <span class="glyphicon glyphicon-new-window"></span></a></p>'; + } + elseif ($output->type == DataType::STRING) + { + echo '<p>' . $output->value . '</p>'; + } + } + } + else + echo "Experiment hasn't completed. Experiment Status is : " . $expStatus; + +} +public static function get_experiment_values( $experiment, $project, $forSearch = false) +{ + $airavataclient = Session::get("airavataClient"); + //var_dump( $experiment); exit; + $expVal = array(); + $expVal["experimentStatusString"] = ""; + $expVal["experimentTimeOfStateChange"] = ""; + $expVal["experimentCreationTime"] = ""; + + if( $experiment->experimentStatus != null) + { + $experimentStatus = $experiment->experimentStatus; + $experimentState = $experimentStatus->experimentState; + $experimentStatusString = ExperimentState::$__names[$experimentState]; + $expVal["experimentStatusString"] = $experimentStatusString; + $expVal["experimentTimeOfStateChange"] = date('Y-m-d H:i:s', $experimentStatus->timeOfStateChange/1000); // divide by 1000 since timeOfStateChange is in ms + $expVal["experimentCreationTime"] = date('Y-m-d H:i:s', $experiment->creationTime/1000); // divide by 1000 since creationTime is in ms + } + $jobStatus = $airavataclient->getJobStatuses($experiment->experimentID); + + if ($jobStatus) + { + $jobName = array_keys($jobStatus); + $jobState = JobState::$__names[$jobStatus[$jobName[0]]->jobState]; + } + else + { + $jobState = null; + } + + $expVal["jobState"] = $jobState; + + if(! $forSearch) + { + $userConfigData = $experiment->userConfigurationData; + $scheduling = $userConfigData->computationalResourceScheduling; + $expVal['scheduling'] = $scheduling; + $expVal["computeResource"] = Utilities::get_compute_resource($scheduling->resourceHostId); + } + $expVal["applicationInterface"] = Utilities::get_application_interface($experiment->applicationId); + + + switch ($experimentStatusString) + { + case 'CREATED': + case 'VALIDATED': + case 'SCHEDULED': + case 'CANCELED': + case 'FAILED': + $expVal["editable"] = true; + break; + default: + $expVal["editable"] = false; + break; + } + + switch ($experimentStatusString) + { + case 'CREATED': + case 'VALIDATED': + case 'SCHEDULED': + case 'LAUNCHED': + case 'EXECUTING': + $expVal["cancelable"] = true; + break; + default: + $expVal["cancelable"] = false; + break; + } + + return $expVal; + +} + + +/** + * Get results of the user's search of experiments + * @return array|null + */ +public static function get_expsearch_results( $inputs) +{ + $airavataclient = Session::get("airavataClient"); + $experiments = array(); + + try + { + switch ( $inputs["search-key"]) + { + case 'experiment-name': + $experiments = $airavataclient->searchExperimentsByName(Session::get('gateway_id'), Session::get('username'), $inputs["search-value"]); + break; + case 'experiment-description': + $experiments = $airavataclient->searchExperimentsByDesc(Session::get('gateway_id'), Session::get('username'), $inputs["search-value"]); + break; + case 'application': + $experiments = $airavataclient->searchExperimentsByApplication(Session::get('gateway_id'), Session::get('username'), $inputs["search-value"]); + break; + case 'creation-time': + $experiments = $airavataclient->searchExperimentsByCreationTime(Session::get('gateway_id'), Session::get('username'), strtotime( $inputs["from-date"])*1000, strtotime( $inputs["to-date"])*1000 ); + break; + case '': + } + } + catch (InvalidRequestException $ire) + { + Utilities::print_error_message('InvalidRequestException!<br><br>' . $ire->getMessage()); + } + catch (AiravataClientException $ace) + { + Utilities::print_error_message('AiravataClientException!<br><br>' . $ace->getMessage()); + } + catch (AiravataSystemException $ase) + { + if ($ase->airavataErrorType == 2) // 2 = INTERNAL_ERROR + { + Utilities::print_info_message('<p>You have not created any experiments yet, so no results will be returned!</p> + <p>Click <a href="create_experiment.php">here</a> to create an experiment, or + <a href="create_project.php">here</a> to create a new project.</p>'); + } + else + { + Utilities::print_error_message('There was a problem with Airavata. Please try again later or report a bug using the link in the Help menu.'); + //print_error_message('AiravataSystemException!<br><br>' . $ase->airavataErrorType . ': ' . $ase->getMessage()); + } + } + catch (TTransportException $tte) + { + Utilities::print_error_message('TTransportException!<br><br>' . $tte->getMessage()); + } + + //get values of all experiments + $expContainer = array(); + $expNum = 0; + foreach( $experiments as $experiment) + { + $expValue = Utilities::get_experiment_values( $experiment, Utilities::get_project($experiment->projectID), true ); + $expContainer[$expNum]['experiment'] = $experiment; + if( $expValue["experimentStatusString"] == "FAILED") + $expValue["editable"] = false; + $expContainer[$expNum]['expValue'] = $expValue; + $expNum++; + } + + return $expContainer; +} + +public static function getExpStates(){ + return ExperimentState::$__names; +} + + +public static function apply_changes_to_experiment($experiment, $input) +{ + $experiment->name = $input['experiment-name']; + $experiment->description = rtrim($input['experiment-description']); + $experiment->projectID = $input['project']; + //$experiment->applicationId = $_POST['application']; + + $userConfigDataUpdated = $experiment->userConfigurationData; + $schedulingUpdated = $userConfigDataUpdated->computationalResourceScheduling; + + $schedulingUpdated->resourceHostId = $input['compute-resource']; + $schedulingUpdated->nodeCount = $input['node-count']; + $schedulingUpdated->queueName = $_POST['queue-name']; + $schedulingUpdated->totalCPUCount = $input['cpu-count']; + $schedulingUpdated->wallTimeLimit = $input['wall-time']; + + $userConfigDataUpdated->computationalResourceScheduling = $schedulingUpdated; + $experiment->userConfigurationData = $userConfigDataUpdated; + + + + + $applicationInputs = Utilities::get_application_inputs($experiment->applicationId); + + $experimentInputs = $experiment->experimentInputs; // get current inputs + $experimentInputs = Utilities::process_inputs($applicationInputs, $experimentInputs); // get new inputs + + if ($experimentInputs) + { + $experiment->experimentInputs = $experimentInputs; + //var_dump($experiment); + return $experiment; + } +} + + + +} + +?> http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/libraries/utilities.php ---------------------------------------------------------------------- diff --git a/app/libraries/utilities.php b/app/libraries/utilities.php index 8368575..8d53f02 100644 --- a/app/libraries/utilities.php +++ b/app/libraries/utilities.php @@ -284,7 +284,8 @@ public static function launch_experiment($expId) Utilities::print_success_message('Experiment launched using ' . $tokenString . ' allocation!'); */ - $hardCodedToken = 'bdc612fe-401e-4684-88e9-317f99409c45'; + $app_config = Utilities::read_config(); + $hardCodedToken = $app_config['credential-store-token']; $airavataclient->launchExperiment($expId, $hardCodedToken); /* @@ -565,7 +566,7 @@ public static function list_input_files($experiment) { $explode = explode('/', $input->value); echo '<p><a target="_blank" - href="' . URL::to("/") . "/../.." . Constant::EXPERIMENT_DATA_ROOT . $explode[sizeof($explode)-2] . '/' . $explode[sizeof($explode)-1] . '">' . + href="' . URL::to("/") . "/../../" . Constant::EXPERIMENT_DATA_ROOT . $explode[sizeof($explode)-2] . '/' . $explode[sizeof($explode)-1] . '">' . $explode[sizeof($explode)-1] . ' <span class="glyphicon glyphicon-new-window"></span></a></p>'; } @@ -751,10 +752,9 @@ public static function assemble_experiment() $scheduling = new ComputationalResourceScheduling(); $scheduling->totalCPUCount = $_POST['cpu-count']; $scheduling->nodeCount = $_POST['node-count']; - //$scheduling->numberOfThreads = $_POST['threads']; - $scheduling->queueName = 'normal'; - $scheduling->wallTimeLimit = $_POST['wall-time']; - //$scheduling->totalPhysicalMemory = $_POST['memory']; + $scheduling->queueName = $_POST['queue-name']; + $scheduling->wallTimeLimit = $_POST['wall-time']; + $scheduling->totalPhysicalMemory = $_POST['total-physical-memory']; $scheduling->resourceHostId = $_POST['compute-resource']; $userConfigData = new UserConfigurationData(); @@ -762,7 +762,6 @@ public static function assemble_experiment() $applicationInputs = Utilities::get_application_inputs($_POST['application']); $experimentInputs = Utilities::process_inputs($applicationInputs, $experimentInputs); - //var_dump($experimentInputs); if( Utilities::$experimentPath == null){ Utilities::create_experiment_folder_path(); @@ -1542,68 +1541,6 @@ public static function create_http_header() } /** - * Create head tag - * Used for all pages - */ -/* - * - * NOW USED DIRECTLY IN BASIC BLADE - * - * -public static function create_html_head() -{ - echo' - <!DOCTYPE html> - <html lang="en"> - <head> - <title>PHP Reference Gateway</title> - <meta charset="utf-8"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <link rel="icon" href="resources/assets/favicon.ico" type="image/x-icon"> - <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css"> - <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> - <script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script> - - <!-- Jira Issue Collector - Report Issue --> - <script type="text/javascript" - src="https://gateways.atlassian.net/s/31280375aecc888d5140f63e1dc78a93-T/en_USmlc07/6328/46/1.4.13/_/download/batch/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector.js?locale=en-US&collectorId=b1572922"></script> - - <!-- Jira Issue Collector - Request Feature --> - <script type="text/javascript" - src="https://gateways.atlassian.net/s/31280375aecc888d5140f63e1dc78a93-T/en_USmlc07/6328/46/1.4.13/_/download/batch/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector/com.atlassian.jira.collector.plugin.jira-issue-collector-plugin:issuecollector.js?locale=en-US&collectorId=674243b0"></script> - - - <script type="text/javascript"> - window.ATL_JQ_PAGE_PROPS = $.extend(window.ATL_JQ_PAGE_PROPS, { - "b1572922": - { - "triggerFunction": function(showCollectorDialog) { - //Requries that jQuery is available! - jQuery("#report-issue").click(function(e) { - e.preventDefault(); - showCollectorDialog(); - }); - } - }, - "674243b0": - { - "triggerFunction": function(showCollectorDialog) { - //Requries that jQuery is available! - jQuery("#request-feature").click(function(e) { - e.preventDefault(); - showCollectorDialog(); - }); - } - } - }); - </script> - - </head> - '; -} -*/ - -/** * Open the XML file containing the community token * @param $tokenFilePath * @throws Exception @@ -1812,8 +1749,11 @@ public static function create_experiment() */ public static function list_output_files($experiment, $expStatus) -{ - if($expStatus == ExperimentState::COMPLETED ) +{ + + $expStatusVal = array_search($expStatus, ExperimentState::$__names); + + if($expStatusVal == ExperimentState::COMPLETED ) { $utility = new Utilities(); $experimentOutputs = $experiment->experimentOutputs; @@ -2076,6 +2016,7 @@ public static function apply_changes_to_experiment($experiment, $input) $schedulingUpdated->resourceHostId = $input['compute-resource']; $schedulingUpdated->nodeCount = $input['node-count']; + $schedulingUpdated->queueName = $_POST['queue-name']; $schedulingUpdated->totalCPUCount = $input['cpu-count']; //$schedulingUpdated->numberOfThreads = $input['threads']; $schedulingUpdated->wallTimeLimit = $input['wall-time']; http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/routes.php ---------------------------------------------------------------------- diff --git a/app/routes.php b/app/routes.php index 7c55a33..212ee90 100755 --- a/app/routes.php +++ b/app/routes.php @@ -70,6 +70,8 @@ Route::get("experiment/edit", "ExperimentController@editView"); Route::post("experiment/edit", "ExperimentController@editSubmit"); +Route::post("experiment/cancel", "ExperimentController@expCancel"); + /* * Compute Resources Routes */ @@ -142,13 +144,16 @@ Route::post("gp/update-crp", "GatewayprofileController@modifyCRP"); Route::get("admin/console", "AdminController@console"); -Route::get("admin/dashboard/gateway", "AdminController@dashboard"); Route::get("admin/dashboard", "AdminController@dashboard"); +Route::get("admin/dashboard/gateway", "AdminController@dashboard"); + Route::get("admin/dashboard/users", "AdminController@usersView"); Route::get("admin/dashboard/roles", "AdminController@rolesView"); +Route::get("admin/dashboard/credential-store", "AdminController@credentialStoreView"); + Route::get("manage/users", "AdminController@usersView"); Route::post("admin/adduser", "AdminController@addAdminSubmit"); @@ -157,6 +162,8 @@ Route::post("admin/addgatewayadmin", "AdminController@addGatewayAdminSubmit"); Route::post("admin/addrole", "AdminController@addRole"); +Route::post("admin/checkroles", "AdminController@getRoles"); + Route::post("admin/deleterole", "AdminController@deleteRole"); //Airavata Server Check http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/views/admin/manage-credentials.blade.php ---------------------------------------------------------------------- diff --git a/app/views/admin/manage-credentials.blade.php b/app/views/admin/manage-credentials.blade.php new file mode 100644 index 0000000..2183739 --- /dev/null +++ b/app/views/admin/manage-credentials.blade.php @@ -0,0 +1,164 @@ +@extends('layout.basic') + +@section('page-header') + @parent + {{ HTML::style('css/admin.css')}} +@stop + +@section('content') + + <div id="wrapper"> + <!-- Sidebar Menu Items - These collapse to the responsive navigation menu on small screens --> + @include( 'partials/dashboard-block') + <div id="page-wrapper"> + + <div class="container-fluid"> + <div class="col-md-12"> + @if( Session::has("message")) + <div class="row"> + <div class="alert alert-success alert-dismissible" role="alert"> + <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> + {{ Session::get("message") }} + </div> + </div> + {{ Session::forget("message") }} + @endif + + <h1 class="text-center">SSH Keys</h1> + + <table class="table table-bordered table-condensed"> + <tr> + <th class="text-center"> + Token</th> + <th class="text-center">Public Key</th> + </tr> + @foreach( $tokens as $token) + <tr> + <td class="role-name">{{ $token }}</td> + <td> + {{ $public-key }} + </td> + </tr> + @endforeach + <tr> + <td>Some token</td> + <td>$ cat ~/.ssh/id_rsa.pub +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU +GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3 +Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA +t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En +mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx +NrRFi9wrf+M7Q== [email protected]</td> + </tr> + </table> + <table class="table"> + <tr class="text-center table-condensed"> + <td><button class="btn btn-default">Generate a new token</button></td> + </tr> + </table> + + <div class="row"> + <h1 class="text-center">My Proxy Credentials</h1> + + <div class="col-md-offset-3 col-md-6"> + <table class="table table-striped table-condensed"> + <tr> + <td>My Proxy Server</td> + <td><input type="text" class="form-control" placeholder="" value=""/></td> + </tr> + <tr> + <td>Username</td> + <td><input type="text" class="form-control" placeholder="" value=""/></td> + </tr> + <tr> + <td>Passphrase</td> + <td><input type="text" class="form-control" placeholder="" value=""/></td> + </tr> + </table> + <table class="table"> + <tr class="text-center table-condensed"> + <td><button class="btn btn-default">Submit</button></td> + </tr> + </table> + </div> + </div> + + <h1 class="text-center">Amazon Credentials</h1> + + <table class="table table-striped table-condensed"> + <tr class="text-center"> + <td>Under Development</td> + </tr> + </table> + + <h1 class="text-center">OAuth MyProxy</h1> + + <table class="table table-striped table-condensed"> + <tr class="text-center"> + <td>Under Development</td> + </tr> + </table> + </div> + </div> + </div> + </div> + + <div class="modal fade" id="delete-role-block" tabindex="-1" role="dialog" aria-labelledby="add-modal" aria-hidden="true"> + <div class="modal-dialog"> + + <form action="{{URL::to('/')}}/admin/deleterole" method="POST"> + <div class="modal-content"> + <div class="modal-header"> + <h3 class="text-center">Delete Role Confirmation</h3> + </div> + <div class="modal-body"> + <input type="hidden" class="form-control delete-roleName" name="role"/> + Do you really want to delete the role - <span class="delete-role-name"></span> + </div> + <div class="modal-footer"> + <div class="form-group"> + <input type="submit" class="btn btn-danger" value="Delete"/> + <input type="button" class="btn btn-default" data-dismiss="modal" value ="Cancel"/> + </div> + </div> + </div> + + </form> + + + </div> + </div> + + +@stop + +@section('scripts') + @parent + <script> + $(".toggle-add-role").click( function(){ + $(".add-role").slideDown(); + }); + + $(".edit-role-name").click( function(){ + var roleNameSpace = $(this).parent().parent().find(".role-name"); + if( roleNameSpace.find(".edit-role-form").length ) + { + roleNameSpace.html( roleNameSpace.find(".original-role-name").val() ); + } + else + { + var role = roleNameSpace.html(); + roleNameSpace.html( $(".edit-role").html() ); + roleNameSpace.find(".original-role-name").val( role ); + roleNameSpace.find(".new-role-name").val( role ); + } + }); + + $(".delete-role").click( function(){ + $("#delete-role-block").modal("show"); + var roleName = $(this).parent().parent().find(".role-name").html(); + $(".delete-role-name").html(roleName); + $(".delete-roleName").val(roleName); + }) + </script> +@stop \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/views/admin/manage-experiments.blade.php ---------------------------------------------------------------------- diff --git a/app/views/admin/manage-experiments.blade.php b/app/views/admin/manage-experiments.blade.php new file mode 100644 index 0000000..e8236e8 --- /dev/null +++ b/app/views/admin/manage-experiments.blade.php @@ -0,0 +1,53 @@ +@extends('layout.basic') + +@section('page-header') + @parent + {{ HTML::style('css/admin.css')}} +@stop + +@section('content') + + <div id="wrapper"> + <!-- Sidebar Menu Items - These collapse to the responsive navigation menu on small screens --> + @include( 'partials/dashboard-block') + <div id="page-wrapper"> + <div class="col-md-12"> + <h3>Experiments</h3> + </div> + <div class="container-fluid"> + + <div class="row"> + + + <div class="well col-md-2 text-center"> + Total 500 + </div> + + </div> + + </div> + <!-- /.container-fluid --> + + </div> + <!-- /#page-wrapper --> + + </div> + +@stop + + +@section('scripts') + @parent + {{ HTML::script('js/gateway.js') }} + <script> + + //make first tab of accordion open by default. + //temporary fix + $("#accordion2").children(".panel").children(".collapse").addClass("in"); + $(".add-tenant").slideUp(); + + $(".toggle-add-tenant").click( function(){ + $(".add-tenant").slideDown(); + }); + </script> +@stop \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/views/admin/manage-gateway.blade.php ---------------------------------------------------------------------- diff --git a/app/views/admin/manage-gateway.blade.php b/app/views/admin/manage-gateway.blade.php index 3dd2661..f7261d1 100644 --- a/app/views/admin/manage-gateway.blade.php +++ b/app/views/admin/manage-gateway.blade.php @@ -11,7 +11,17 @@ <!-- Sidebar Menu Items - These collapse to the responsive navigation menu on small screens --> @include( 'partials/dashboard-block') <div id="page-wrapper"> - + <div class="col-md-12"> + @if( Session::has("message")) + <div class="row"> + <div class="alert alert-success alert-dismissible" role="alert"> + <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> + {{ Session::get("message") }} + </div> + </div> + {{ Session::forget("message") }} + @endif + </div> <div class="container-fluid"> <div class="row"> @@ -100,6 +110,7 @@ <input name="add" type="submit" class="btn btn-primary" value="Add Admin"/> </div> </form> + --> </div> </div> </div> http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/views/admin/manage-roles.blade.php ---------------------------------------------------------------------- diff --git a/app/views/admin/manage-roles.blade.php b/app/views/admin/manage-roles.blade.php index a7a3813..fa39a75 100644 --- a/app/views/admin/manage-roles.blade.php +++ b/app/views/admin/manage-roles.blade.php @@ -133,6 +133,6 @@ var roleName = $(this).parent().parent().find(".role-name").html(); $(".delete-role-name").html(roleName); $(".delete-roleName").val(roleName); - }) + }); </script> @stop \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/views/admin/manage-users.blade.php ---------------------------------------------------------------------- diff --git a/app/views/admin/manage-users.blade.php b/app/views/admin/manage-users.blade.php index 8d098f7..5cbdb7b 100644 --- a/app/views/admin/manage-users.blade.php +++ b/app/views/admin/manage-users.blade.php @@ -11,7 +11,17 @@ <!-- Sidebar Menu Items - These collapse to the responsive navigation menu on small screens --> @include( 'partials/dashboard-block') <div id="page-wrapper"> - + <div class="col-md-12"> + @if( Session::has("message")) + <div class="row"> + <div class="alert alert-success alert-dismissible" role="alert"> + <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button> + {{ Session::get("message") }} + </div> + </div> + {{ Session::forget("message") }} + @endif + </div> <div class="container-fluid"> <div class="col-md-12"> @if( Session::has("message")) @@ -41,10 +51,10 @@ </th> </tr> @foreach( (array)$users as $user) - <tr> + <tr class="user-row"> <td>{{ $user }}</td> <td> - <button class="button btn btn-default check-role" type="button">Check Role</button> + <button class="button btn btn-default check-roles fade" type="button" data-username="{{$user}}">Check All Roles</button> <div class="user-roles"></div> </td> </tr> @@ -58,35 +68,77 @@ <div class="modal fade" id="check-role-block" tabindex="-1" role="dialog" aria-labelledby="add-modal" aria-hidden="true"> <div class="modal-dialog"> - <div class="modal-content"> - <div class="modal-header"> - <h3 class="text-center">User Roles</h3> - </div> - <div class="modal-body"> - User roles will be displayed and modified here. + <div class="modal-content"> + <div class="modal-header"> + <h3 class="text-center">User Roles</h3> + </div> + <div class="modal-body"> + <h4 class="roles-of-user"></h4> + <div class="roles-load"> + Getting User Roles. Please Wait... </div> - <div class="modal-footer"> - <div class="form-group"> - <input type="submit" class="btn btn-primary" data-dismiss="modal" value="Ok"/> - </div> + <div class="roles-list"> + </div> + </div> + <div class="modal-footer"> + <div class="form-group"> + <input type="submit" class="btn btn-primary" data-dismiss="modal" value="Ok"/> </div> </div> - - </form> - - + </div> + <input type="hidden" class="base-url" value="{{URL::to('/')}}"/> </div> </div> + <div class="role-block"> + <div class="btn-group" role="group"> + <button type="button" class="btn btn-default role-name" disabled>Role</button> + <button type="button" class="btn btn-default"><span class="glyphicon glyphicon-remove"></span></button> + </div> + </div> @stop @section('scripts') @parent <script> - $(".check-role").click( function(){ - $("#check-role-block").modal("show"); - }); + $(".user-row").hover( + function(){ + $(this).find(".check-roles").addClass("in"); + }, + function(){ + $(this).find(".check-roles").removeClass("in"); + } + ); + $(".check-roles").click( function(){ + var userName = $(this).data("username"); + $("#check-role-block").modal("show"); + $(".roles-of-user").html( "User : " + userName); + $(".roles-load").removeClass("hide"); + $(".roles-list").addClass("hide"); + $.ajax({ + type: "POST", + url: $(".base-url").val() + "/admin/checkroles", + data: + { + username: userName + } + }) + .complete(function( data ) { + roles = JSON.parse( data.responseText ); + roleBlocks = ""; + for( var i=0; i<roles.length; i++) + { + $(".role-block").find(".role-name").html( roles[i]); + var newRoleBlock = $(".role-block").html(); + roleBlocks += newRoleBlock; + $(".roles-list").html( roleBlocks); + } + $(".roles-load").addClass("hide"); + $(".roles-list").removeClass("hide"); + }); + + }); </script> @stop \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/views/experiment/create-complete.blade.php ---------------------------------------------------------------------- diff --git a/app/views/experiment/create-complete.blade.php b/app/views/experiment/create-complete.blade.php index a3628f6..90df6ba 100644 --- a/app/views/experiment/create-complete.blade.php +++ b/app/views/experiment/create-complete.blade.php @@ -10,70 +10,12 @@ <h1>Create a new experiment</h1> <form action="{{URL::to('/')}}/experiment/create" method="POST" role="form" enctype="multipart/form-data"> - <input type="hidden" name="experiment-name" value="{{$experimentName}}"> - <input type="hidden" name="experiment-description" value="{{$experimentDescription}}"> - <input type="hidden" name="project" value="{{$project}}"> - <input type="hidden" name="application" value="{{$application}}"> + <input type="hidden" name="experiment-name" value="{{$expInputs['experimentName']}}"> + <input type="hidden" name="experiment-description" value="{{$expInputs['experimentDescription']}}"> + <input type="hidden" name="project" value="{{$expInputs['project']}}"> + <input type="hidden" name="application" value="{{$expInputs['application']}}"> - <div class="form-group required"> - <label for="experiment-name" class="control-label">Experiment Name</label> - <input type="text" class="form-control" name="experiment-name" id="experiment-name" placeholder="Enter experiment name" autofocus required="required" {{ $disabled }} value="{{ $experimentName }}"> - </div> - <div class="form-group"> - <label for="experiment-description">Experiment Description</label> - <textarea class="form-control" name="experiment-description" id="experiment-description" placeholder="Optional: Enter a short description of the experiment" {{ $disabled }}>{{ $experimentDescription }}</textarea> - </div> - <div class="form-group required"> - <label for="project" class="control-label">Project</label> - {{ Utilities::create_project_select($project, !$disabled) }} - </div> - <div class="form-group"> - <label for="application">Application</label> - {{ Utilities::create_application_select($application, !$disabled) }} - </div> - - <div class="panel panel-default"> - <div class="panel-heading">Application configuration</div> - <div class="panel-body"> - <label>Application input</label> - <div class="well"> - <input type="hidden" id="allowedFileSize" value="{{$allowedFileSize}}"/> - {{ Utilities::create_inputs($application, true) }} - </div> - <div class="form-group"> - <label for="compute-resource">Compute Resource</label>'; - {{ Utilities::create_compute_resources_select($application, null) }} - </div> - <div class="form-group"> - <label for="node-count">Node Count</label> - <input type="number" class="form-control" name="node-count" id="node-count" value="1" min="1"> - </div> - <div class="form-group"> - <label for="cpu-count">Total Core Count</label> - <input type="number" class="form-control" name="cpu-count" id="cpu-count" value="4" min="1"> - </div> - <div class="form-group"> - <label for="wall-time">Wall Time Limit</label> - <div class="input-group"> - <input type="number" class="form-control" name="wall-time" id="wall-time" value="30" min="0"> - <span class="input-group-addon">minutes</span> - </div> - </div> - </div> - </div> - <h3>Notifications</h3> - <div class="form-group well"> - <label for=""></label> - <input type="checkbox" id="enableEmail" name="enableEmailNotification" value="1">Do you want to receive email notifications for status changes in the experiment?<br/> - <div class="emailSection hide"> - <h4>Enter Email Address here.</h4> - <div class="emailAddresses"> - <input type="email" id="emailAddresses" class="form-control" name="emailAddresses[]" placeholder="Email"/> - </div> - <button type="button" class="addEmail btn btn-default">Add another Email</button> - </div> - </div> - + @include('partials/experiment-inputs', array("expInputs" => $expInputs) ) <div class="btn-toolbar"> <div class="btn-group"> @@ -107,22 +49,22 @@ }); $("#enableEmail").change( function(){ - if( this.checked) + if( this.checked) { $("#emailAddresses").attr("required", "required"); - $(this).parent().children(".emailSection").removeClass("hide"); + $(this).parent().children(".emailSection").removeClass("hide"); } - else + else { - $(this).parent().children(".emailSection").addClass("hide"); + $(this).parent().children(".emailSection").addClass("hide"); $("#emailAddresses").removeAttr("required"); } }); $(".addEmail").click( function(){ - var emailInput = $(this).parent().find("#emailAddresses").clone(); - emailInput.removeAttr("id").removeAttr("required").val("").appendTo(".emailAddresses"); + var emailInput = $(this).parent().find("#emailAddresses").clone(); + emailInput.removeAttr("id").removeAttr("required").val("").appendTo(".emailAddresses"); }); </script> @stop \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/views/experiment/create.blade.php ---------------------------------------------------------------------- diff --git a/app/views/experiment/create.blade.php b/app/views/experiment/create.blade.php index 52873f1..966c740 100755 --- a/app/views/experiment/create.blade.php +++ b/app/views/experiment/create.blade.php @@ -52,4 +52,14 @@ </div> +@stop + + +@section('scripts') + @parent + <script> + window.onbeforeunload = function(e) { + return "Are you sure you want to leave this page?"; + }; + </script> @stop \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/views/experiment/edit.blade.php ---------------------------------------------------------------------- diff --git a/app/views/experiment/edit.blade.php b/app/views/experiment/edit.blade.php index 7e086a2..8eb759a 100755 --- a/app/views/experiment/edit.blade.php +++ b/app/views/experiment/edit.blade.php @@ -16,128 +16,62 @@ <div class="container"> -<h1>Edit Experiment</h1> - -<form action="{{URL::to('/')}}/experiment/edit" method="POST" role="form" enctype="multipart/form-data"> - <input type="hidden" name="expId" value="<?php echo Input::get('expId');?>"/> - - <div class="form-group"> - <label for="experiment-name">Experiment Name</label> - <input type="text" - class="form-control" - name="experiment-name" - id="experiment-name" - value="<?php echo $experiment->name; ?>" - <?php if(!$expVal['editable']) echo 'disabled' ?>> - </div> - <div class="form-group"> - <label for="experiment-description">Experiment Description</label> - <textarea class="form-control" - name="experiment-description" - id="experiment-description" - <?php if(!$expVal['editable']) echo 'disabled' ?>><?php echo $experiment->description ?> - </textarea> - </div> - <div class="form-group"> - <label for="project">Project</label> - <?php Utilities::create_project_select($experiment->projectID, $expVal['editable']); ?> - </div> - <div class="form-group"> - <label for="application">Application</label> - <?php Utilities::create_application_select($experiment->applicationId, false); ?> - </div> - - <div class="panel panel-default"> - <div class="panel-heading">Application configuration</div> - <div class="panel-body"> - <label>Application input</label> - <div class="well"> - <div class="form-group"> - <p><strong>Current inputs</strong></p> - <?php Utilities::list_input_files($experiment); ?> - </div> - <?php Utilities::create_inputs($experiment->applicationId, false); ?> - </div> + <div class="col-md-offset-3 col-md-6"> + <h1>Edit Cloned Experiment</h1> - <div class="form-group"> - <label for="compute-resource">Compute Resource</label> - <?php Utilities::create_compute_resources_select($experiment->applicationId, $expVal['scheduling']->resourceHostId); ?> - </div> + <form action="{{URL::to('/')}}/experiment/edit" method="POST" role="form" enctype="multipart/form-data"> + <input type="hidden" name="expId" value="<?php echo Input::get('expId');?>"/> + + @include('partials/experiment-inputs') - <div class="form-group"> - <label for="node-count">Node Count</label> - <input type="number" - class="form-control" - name="node-count" - id="node-count" - min="1" - value="<?php echo $expVal['scheduling']->nodeCount ?>" - <?php if(!$expVal['editable']) echo 'disabled' ?>> - </div> - <div class="form-group"> - <label for="cpu-count">Total Core Count</label> - <input type="number" - class="form-control" - name="cpu-count" - id="cpu-count" - min="1" - value="<?php echo $expVal['scheduling']->totalCPUCount ?>" - <?php if(!$expVal['editable']) echo 'disabled' ?>> - </div> - <!-- - <div class="form-group"> - <label for="threads">Number of Threads</label> - <input type="number" - class="form-control" - name="threads" - id="threads" - min="0" - value="<?php //echo $expVal['scheduling']->numberOfThreads; ?>" - <?php //if(!$expVal['editable']) echo 'disabled'; ?>> - </div> - --> - <div class="form-group"> - <label for="wall-time">Wall Time Limit</label> - <div class="input-group"> - <input type="number" - class="form-control" - name="wall-time" - id="wall-time" - min="0" - value="<?php echo $expVal['scheduling']->wallTimeLimit ?>" - <?php if(!$expVal['editable']) echo 'disabled' ?>> - <span class="input-group-addon">minutes</span> - </div> - </div> - <!-- - <div class="form-group"> - <label for="memory">Total Physical Memory</label> - <div class="input-group"> - <input type="number" - class="form-control" - name="memory" - id="memory" - min="0" - value="<?php //echo $expVal['scheduling']->totalPhysicalMemory; ?>" - <?php //if(!$expVal['editable']) echo 'disabled'; ?>> - <span class="input-group-addon">kB</span> - </div> - </div> - --> - </div> - </div> - - <div class="btn-toolbar"> - <div class="btn-group"> - <input name="save" type="submit" class="btn btn-primary" value="Save" <?php if(!$expVal['editable']) echo 'disabled' ?>> - <input name="launch" type="submit" class="btn btn-success" value="Save and launch" <?php if(!$expVal['editable']) echo 'disabled' ?>> - </div> - </div> + <div class="btn-toolbar"> + <div class="btn-group"> + <input name="save" type="submit" class="btn btn-primary" value="Save" <?php if(!$expInputs['expVal']['editable']) echo 'disabled' ?>> + <input name="launch" type="submit" class="btn btn-success" value="Save and launch" <?php if(!$expInputs['expVal']['editable']) echo 'disabled' ?>> + </div> + </div> -</form> + </form> + </div> </div> +@stop + + +@section('scripts') + @parent + <script> + $('.file-input').bind('change', function() { + + var inputFileSize = Math.round( this.files[0].size/(1024*1024) ); + if( inputFileSize > $("#allowedFileSize").val()) + { + alert( "The input file size is greater than the allowed file size (" + $("#allowedFileSize").val() + " MB) in a form. Please upload another file."); + $(this).val(""); + } + + }); + + $("#enableEmail").change( function(){ + if( this.checked) + { + $("#emailAddresses").attr("required", "required"); + $(this).parent().children(".emailSection").removeClass("hide"); + } + else + { + $(this).parent().children(".emailSection").addClass("hide"); + $("#emailAddresses").removeAttr("required"); + } + + }); + + $(".addEmail").click( function(){ + var emailInput = $(this).parent().find("#emailAddresses").clone(); + emailInput.removeAttr("id").removeAttr("required").val("").appendTo(".emailAddresses"); + }); + </script> @stop \ No newline at end of file http://git-wip-us.apache.org/repos/asf/airavata-php-gateway/blob/cae5a4dc/app/views/experiment/summary.blade.php ---------------------------------------------------------------------- diff --git a/app/views/experiment/summary.blade.php b/app/views/experiment/summary.blade.php index e94cf9c..f101782 100755 --- a/app/views/experiment/summary.blade.php +++ b/app/views/experiment/summary.blade.php @@ -89,7 +89,12 @@ class="btn btn-success" value="Launch" title="Launch the experiment" <?php if(!$expVal["editable"] ) echo 'disabled' ?>> - <!--<input name="cancel" type="submit" class="btn btn-warning" value="Cancel" <?php //if(!$cancelable) echo 'disabled'; ?>>--> + <a href="{{URL::to('/') }}/experiment/cancel?expId={{ $experiment->experimentID }}" + class="btn btn-default" + role="button" + title="Edit the experiment's settings" <?php if(!$expVal["cancelable"] ) echo 'disabled' ?>> + <input name="cancel" type="submit" class="btn btn-warning" value="Cancel" <?php if(!$expVal["cancelable"]) echo 'disabled'; ?> > + </a> <input name="clone" type="submit" class="btn btn-primary"
