From a4f41fd39275735b5e44697204e64125138674c0 Mon Sep 17 00:00:00 2001 From: Eike Foken Date: Wed, 14 Sep 2011 05:44:20 +0200 Subject: [PATCH] Implement detail page for experiments --- application/controllers/experiments.php | 63 +++- application/helpers/MY_date_helper.php | 50 ++- application/models/experiment.php | 38 ++- application/models/job.php | 16 + application/views/experiments/detail.php | 70 +++++ assets/css/style.css | 369 ++++++++++++++++------- 6 files changed, 473 insertions(+), 133 deletions(-) create mode 100644 application/views/experiments/detail.php diff --git a/application/controllers/experiments.php b/application/controllers/experiments.php index 7288194..d8ccc7d 100644 --- a/application/controllers/experiments.php +++ b/application/controllers/experiments.php @@ -1,6 +1,6 @@ - + * Copyright (c) 2011 Karsten Heiken, Eike Foken * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -38,6 +38,7 @@ class Experiments extends CI_Controller { $this->load->model('parameter'); $this->load->model('program'); $this->load->model('experiment'); + $this->load->model('job'); } /** @@ -131,6 +132,64 @@ class Experiments extends CI_Controller { $this->load->view('experiments/new', $data); } + + /** + * Copies an experiment. + * + * @param string $experimentId + */ + public function copy($experimentId = '') { + $experiment = $this->experiment->getByID($experimentId); + if (isset($experiment['project_id'])) { + redirect('experiments/create/' . $experiment['project_id'] . '/' . $experimentId); + } else { + show_404(); + } + } + + /** + * Shows detailed informations about a specific experiment. + * + * @param string $experimentId + */ + public function detail($experimentId = '') { + $experiment = $this->experiment->getByID($experimentId); + if (empty($experimentId) || !isset($experiment['id'])){ + show_404(); + } + + $this->load->helper('typography'); + + $data['experiment'] = $experiment; + $data['job'] = $this->job->getByExperimentId($experiment['id']); + $data['project'] = $this->project->getById($experiment['project_id']); + + $this->load->view('experiments/detail', $data); + } + + /** + * Allows users to delete an experiment. + * + * @param string $experimentId + */ + public function delete($experimentId = '') { + // TODO: Check user rights + + $experiment = $this->experiment->getByID($experimentId); + if (empty($experimentId) || !isset($experiment['id'])){ + show_404(); + } + + $this->load->helper('file'); + + $experimentPath = FCPATH . 'uploads/' . $experiment['project_id'] . '/' . $experiment['id'] . '/'; + if (delete_files($experimentPath, true)) { + rmdir($experimentPath); + } + + $this->experiment->delete($experimentId); + redirect('projects/detail/' . $experiment['project_id'], 303); + } } /* End of file experiments.php */ diff --git a/application/helpers/MY_date_helper.php b/application/helpers/MY_date_helper.php index 87b329d..df91804 100644 --- a/application/helpers/MY_date_helper.php +++ b/application/helpers/MY_date_helper.php @@ -48,9 +48,9 @@ if ( ! function_exists('mysql_now')) * @param string $end * @return array */ -if ( ! function_exists('time_diff')) +if ( ! function_exists('time_diff')) { - function time_diff($start, $end) + function time_diff($start, $end) { $uts['start'] = strtotime($start); $uts['end'] = strtotime($end); @@ -73,27 +73,27 @@ if ( ! function_exists('time_diff')) * @param boolean $includeseconds should seconds be appended to the string? * @return string */ -if ( ! function_exists('prettyTime')) +if ( ! function_exists('prettyTime')) { - function prettyTime($secs, $includeseconds = false) + function prettyTime($secs, $includeseconds = false) { - + if(!defined('SECOND')) define("SECOND", 1); if(!defined('MINUTE')) define("MINUTE", 60 * SECOND); if(!defined('HOUR')) define("HOUR", 60 * MINUTE); if(!defined('DAY')) define("DAY", 24 * HOUR); if(!defined('MONTH')) define("MONTH", 30 * DAY); - + $days = intval($secs / 86400); $hours = intval($secs / 3600 % 24); $minutes = intval($secs / 60 % 60); $seconds = intval($secs % 60); - + $d = sprintf(ngettext('%d day', '%d days', $days), $days); $h = sprintf(ngettext('%d hour', '%d hours', $hours), $hours); $m = sprintf(ngettext('%d minute', '%d minutes', $minutes), $minutes); $s = sprintf(ngettext('%d second', '%d seconds', $seconds), $seconds); - + $output = ""; if($days > 0) { $output .= $d; @@ -107,7 +107,39 @@ if ( ! function_exists('prettyTime')) if($includeseconds || empty($output)) { $output .= !empty($output) ? ", ". $s : "". $s; } - + + return $output; + } +} + +/** + * Parses any english textual datetime description into a relative date string. + * + * @author Eike Foken + * @param string $date + * @param boolean $show_seconds + * @return string + */ +if (!function_exists('relative_time')) { + function relative_time($date, $show_seconds = false) { + $diff = time() - strtotime($date); + + if ($diff < 120 && !$show_seconds) { + $output = _('just now'); + } else if ($diff < 60 && $show_seconds) { + $output = sprintf(ngettext('%d second ago', '%d seconds ago', $diff), $diff); + } else if (($diff = round($diff / 60)) < 60) { + $output = sprintf(ngettext('%d minute ago', '%d minutes ago', $diff), $diff); + } else if (($diff = round($diff / 60)) < 24) { + $output = sprintf(ngettext('%d hour ago', '%d hours ago', $diff), $diff); + } else if (($diff = round($diff / 24)) < 7) { + $output = sprintf(ngettext('%d day ago', '%d days ago', $diff), $diff); + } else if (($diff = round($diff / 7)) < 4) { + $output = sprintf(ngettext('%d week ago', '%d weeks ago', $diff), $diff); + } else { + $output = _('on') . ' ' . strftime('%B %Y', strtotime($date)); + } + return $output; } } diff --git a/application/models/experiment.php b/application/models/experiment.php index 09729b6..6dd6442 100644 --- a/application/models/experiment.php +++ b/application/models/experiment.php @@ -1,6 +1,6 @@ + * Copyright (c) 2011 Karsten Heiken, Eike Foken * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,14 +25,15 @@ * Experiments are used to store different variations of the same project. * * @author Karsten Heiken + * @author Eike Foken */ class Experiment extends CI_Model { /** - * Create a new experiment. + * Creates a new experiment. * * @param array $data The data of the new experiment - * @return boolean Was the insert successful? + * @return boolean Returns TRUE if the insert was successful. */ public function create($data) { if (!isset($data['project_id'])) { @@ -51,13 +52,26 @@ class Experiment extends CI_Model { } /** - * Deletes a experiment. + * Updates an experiment. + * + * @param array $data + * @param string $experimentId + * @return boolean Returns TRUE if the update was successful. + */ + public function update($data, $experimentId) { + $this->db->update('experiments', $data, array('id' => $experimentId)); + return $this->db->affected_rows() == 1; + } + + /** + * Deletes an experiment. * * @param string $experimentId The experiments ID to delete - * @return boolean Was the deletion successful? + * @return boolean Returns TRUE if the deletion was successful. */ public function delete($experimentId) { - return $this->db->delete('experiments', array('id' => $experimentId)); + $this->db->delete('experiments', array('id' => $experimentId)); + return $this->db->affected_rows() == 1; } /** @@ -99,14 +113,15 @@ class Experiment extends CI_Model { } /** - * Gets an experiment by ID. + * Gets an experiment by ID (kept for backwards compatibility). * + * @deprecated 14-09-2011 + * @see Experiment::getById() * @param string $experimentId The experiment to get * @return array The experiment */ public function get($experimentId) { - $query = $this->db->get_where('experiments', array('id' => $experimentId)); - return $query->row_array(); + return $this->getById($experimentId); } /** @@ -142,4 +157,7 @@ class Experiment extends CI_Model { return $results; } -} \ No newline at end of file +} + +/* End of file group.php */ +/* Location: ./application/controllers/group.php */ diff --git a/application/models/job.php b/application/models/job.php index 698fcb2..c3ac190 100644 --- a/application/models/job.php +++ b/application/models/job.php @@ -66,6 +66,22 @@ class Job extends CI_Model { return $this->db->where('id', $job_id)->update('jobs', $data); } + /** + * Gets a specific job. + * + * @param string $experimentId + * @return array The job data + */ + public function getByExperimentId($experimentId) { + $this->db->select('jobs.*, users.username, users.firstname, users.lastname'); + $this->db->join('users', 'jobs.started_by = users.id', 'left'); + $this->db->where('experiment_id', $experimentId); + + $query = $this->db->get('jobs'); + + return $query->row_array(); + } + /** * Gets a list of recent jobs. * diff --git a/application/views/experiments/detail.php b/application/views/experiments/detail.php new file mode 100644 index 0000000..fdda633 --- /dev/null +++ b/application/views/experiments/detail.php @@ -0,0 +1,70 @@ +load->view('header');?> + +
+ +
+

» »

+
+ +
+

+
+

+ +

Actions

+

+ + + + + + + +

+
+ + +
+

+

+ :
+ :
+ : + +

+ % +   +
+

+ +

+ +
+ + +
+ + + +load->view('footer');?> diff --git a/assets/css/style.css b/assets/css/style.css index 2f81973..7bd91f6 100644 --- a/assets/css/style.css +++ b/assets/css/style.css @@ -42,7 +42,7 @@ li { } .editable { - background: #fffbcc; + background: #fffbcc; } .clear { @@ -291,10 +291,10 @@ div.error { } #content div.title a.share { - background: url(../images/icons/user-share.png) 6px center no-repeat; - padding-left: 28px; - font-size: 12px; - font-weight: normal; + background: url(../images/icons/user-share.png) 6px center no-repeat; + padding-left: 28px; + font-size: 12px; + font-weight: normal; line-height: 12px; } @@ -325,13 +325,13 @@ div.error { } .pagination ul li.clickable { - color: #0088cc; - cursor: pointer; + color: #0088cc; + cursor: pointer; } .pagination ul li.active { - color: #222; - cursor: default; + color: #222; + cursor: default; } /* Footer */ @@ -357,71 +357,88 @@ div.error { /* Buttons */ .buttons { - float: left; - padding-bottom: 20px; - clear: both; + float: left; + padding-bottom: 20px; + clear: both; } a.button { - color: #6e6e6e; - font: bold 12px Helvetica, Arial, sans-serif; - text-decoration: none; - padding: 7px 12px; - position: relative; - display: inline-block; - text-shadow: 0 1px 0 #fff; - -webkit-transition: border-color .218s; - -moz-transition: border .218s; - -o-transition: border-color .218s; - transition: border-color .218s; - background: #f3f3f3; - background: -webkit-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1)); - background: -moz-linear-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1)); - border: solid 1px #dcdcdc; - border-radius: 2px; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; - margin-right: 10px; + color: #6e6e6e; + font: bold 12px Helvetica, Arial, sans-serif; + text-decoration: none; + padding: 7px 12px; + position: relative; + display: inline-block; + text-shadow: 0 1px 0 #fff; + -webkit-transition: border-color .218s; + -moz-transition: border .218s; + -o-transition: border-color .218s; + transition: border-color .218s; + background: #f3f3f3; + background: -webkit-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1)); + background: -moz-linear-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1)); + border: solid 1px #dcdcdc; + border-radius: 2px; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + margin-right: 10px; } a.button.locked { - color: #333; - border-color: #999; - -moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); + color: #333; + border-color: #999; + -moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); +} + +a.button.disabled { + color: #6e6e6e; + background-color: #fff; + /*border: 1px solid rgba(0, 0, 0, 0.05);*/ + cursor: default; + opacity: 0.4; + filter: alpha(opacity=40); +} + +a.button.disabled:hover, a.button.disabled:active { + color: inherit; + border: 1px solid #dcdcdc; + -moz-box-shadow: inherit; + -webkit-box-shadow: inherit; + box-shadow: inherit; } a.button:hover { - color: #333; - border-color: #999; - -moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); - box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); + color: #333; + border-color: #999; + -moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); } a.button:active { - color: #000; - border-color: #444; + color: #000; + border-color: #444; } a.left { - -webkit-border-top-right-radius: 0; - -moz-border-radius-topright: 0; - border-top-right-radius: 0; - -webkit-border-bottom-right-radius: 0; - -moz-border-radius-bottomright: 0; - border-bottom-right-radius: 0; - margin: 0; + -webkit-border-top-right-radius: 0; + -moz-border-radius-topright: 0; + border-top-right-radius: 0; + -webkit-border-bottom-right-radius: 0; + -moz-border-radius-bottomright: 0; + border-bottom-right-radius: 0; + margin: 0; } a.middle { - border-radius: 0; - -webkit-border-radius: 0; - -moz-border-radius: 0; - border-left: solid 1px #f3f3f3; - margin: 0; - border-left: solid 1px rgba(255, 255, 255, 0); + border-radius: 0; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-left: solid 1px #f3f3f3; + margin: 0; + border-left: solid 1px rgba(255, 255, 255, 0); } a.middle:hover, @@ -430,107 +447,122 @@ a.right:hover { } a.right { - -webkit-border-top-left-radius: 0; - -moz-border-radius-topleft: 0; - border-top-left-radius: 0; - -webkit-border-bottom-left-radius: 0; - -moz-border-radius-bottomleft: 0; - border-bottom-left-radius: 0; - border-left: solid 1px #f3f3f3; - border-left: solid 1px rgba(255, 255, 255, 0); + -webkit-border-top-left-radius: 0; + -moz-border-radius-topleft: 0; + border-top-left-radius: 0; + -webkit-border-bottom-left-radius: 0; + -moz-border-radius-bottomleft: 0; + border-bottom-left-radius: 0; + border-left: solid 1px #f3f3f3; + border-left: solid 1px rgba(255, 255, 255, 0); } a.big { - font-size: 16px; - padding: 10px 15px; + font-size: 16px; + padding: 10px 15px; } a.supersize { - font-size: 20px; - padding: 15px 20px; + font-size: 20px; + padding: 15px 20px; } a.save { - background: url(../images/icons/tick.png) 10px center no-repeat #f3f3f3; - padding-left: 30px; + background: url(../images/icons/tick.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; } a.upload { - background: url(../images/icons/drive-upload.png) 10px center no-repeat #f3f3f3; - padding-left: 30px; + background: url(../images/icons/drive-upload.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; } a.add { - background: url(../images/icons/plus-circle.png) 10px center no-repeat #f3f3f3; - padding-left: 30px; + background: url(../images/icons/plus-circle.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; } a.delete { - background: url(../images/icons/minus-circle.png) 10px center no-repeat #f3f3f3; - padding-left: 30px; + background: url(../images/icons/minus-circle.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; } a.edit { - background: url(../images/icons/pencil.png) 10px center no-repeat #f3f3f3; - padding-left: 30px; + background: url(../images/icons/pencil.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; } a.flag { - background: url(../images/icons/flag.png) 10px center no-repeat #f3f3f3; - padding-left: 30px; + background: url(../images/icons/flag.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; } a.cancel { - background: url(../images/icons/slash.png) 10px center no-repeat #f3f3f3; - padding-left: 30px; + background: url(../images/icons/slash.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; +} + +a.copy { + background: url(../images/icons/document-copy.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; +} + +a.job_start { + background: url(../images/icons/server--arrow.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; +} + +a.results { + background: url(../images/icons/blue-folder-open-document-text.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; } a.up { - background: url(../images/button-sprite.png) 13px -131px no-repeat #f3f3f3; - width: 18px; + background: url(../images/button-sprite.png) 13px -131px no-repeat #f3f3f3; + width: 18px; } a.down { - background: url(../images/button-sprite.png) 13px -166px no-repeat #f3f3f3; - width: 18px; + background: url(../images/button-sprite.png) 13px -166px no-repeat #f3f3f3; + width: 18px; } a.save-big { - background: url(../images/icons/tick.png) 15px center no-repeat #f3f3f3; - font-size: 16px; - padding: 10px 15px 10px 35px; + background: url(../images/icons/tick.png) 15px center no-repeat #f3f3f3; + font-size: 16px; + padding: 10px 15px 10px 35px; } a.add-big { - background: url(../images/icons/plus-circle.png) 15px center no-repeat #f3f3f3; - font-size: 16px; - padding: 10px 15px 10px 35px; + background: url(../images/icons/plus-circle.png) 15px center no-repeat #f3f3f3; + font-size: 16px; + padding: 10px 15px 10px 35px; } a.delete-big { - background: url(../images/icons/minus-circle.png) 15px center no-repeat #f3f3f3; - font-size: 16px; - padding: 10px 15px 10px 35px; + background: url(../images/icons/minus-circle.png) 15px center no-repeat #f3f3f3; + font-size: 16px; + padding: 10px 15px 10px 35px; } a.flag-big { - background: url(../images/icons/flag.png) 15px center no-repeat #f3f3f3; - font-size: 16px; - padding: 10px 15px 10px 35px; + background: url(../images/icons/flag.png) 15px center no-repeat #f3f3f3; + font-size: 16px; + padding: 10px 15px 10px 35px; } a.up-big { - background: url(../images/button-sprite.png) 15px -126px no-repeat #f3f3f3; - width: 18px; - font-size: 16px; - padding: 10px 15px; + background: url(../images/button-sprite.png) 15px -126px no-repeat #f3f3f3; + width: 18px; + font-size: 16px; + padding: 10px 15px; } a.down-big { - background: url(../images/button-sprite.png) 15px -161px no-repeat #f3f3f3; - width: 18px; - font-size: 16px; - padding: 10px 15px; + background: url(../images/button-sprite.png) 15px -161px no-repeat #f3f3f3; + width: 18px; + font-size: 16px; + padding: 10px 15px; } /* Tabs */ @@ -595,6 +627,119 @@ html ul.tabs li.active a:hover { padding: 0; } +/* Progress bar */ + +div.progress_bar { + position: relative; + margin: 0 0 15px 0; + padding: 1px; + width: 250px; + height: 20px; + border: 1px solid #999; + background: #fff; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; +} + +div.progress_bar > span { + display: block; + height: 100%; + background-color: #5b86ac; + background-image: -webkit-gradient( + linear, + left bottom, + left top, + from(#5b86ac), + to(#6891b7) + ); + background-image: -moz-linear-gradient( + center bottom, + #5b86ac, + #6891b7 + ); + background-image: -ms-linear-gradient( + center bottom, + #5b86ac, + #6891b7 + ); + background-image: -o-linear-gradient( + center bottom, + #5b86ac, + #6891b7 + ); + position: relative; + overflow: hidden; +} + +div.progress_bar > span:after { + content: ""; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + background-image: -webkit-gradient( + linear, + 0 0, + 100% 100%, + color-stop(.25, rgba(255, 255, 255, .08)), + color-stop(.25, transparent), + color-stop(.5, transparent), + color-stop(.5, rgba(255, 255, 255, .08)), + color-stop(.75, rgba(255, 255, 255, .08)), + color-stop(.75, transparent), + to(transparent) + ); + background-image: -moz-linear-gradient( + -45deg, + rgba(255, 255, 255, .08) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, .08) 50%, + rgba(255, 255, 255, .08) 75%, + transparent 75%, + transparent + ); + background-image: -ms-linear-gradient( + -45deg, + rgba(255, 255, 255, .08) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, .08) 50%, + rgba(255, 255, 255, .08) 75%, + transparent 75%, + transparent + ); + background-image: -o-linear-gradient( + -45deg, + rgba(255, 255, 255, .08) 25%, + transparent 25%, + transparent 50%, + rgba(255, 255, 255, .08) 50%, + rgba(255, 255, 255, .08) 75%, + transparent 75%, + transparent + ); + z-index: 1; + -webkit-background-size: 50px 50px; + -moz-background-size: 50px 50px; + -webkit-animation: move 2s linear infinite; + overflow: hidden; +} + +div.progress_bar strong { + color: #111; + position: absolute; + top: 0; + left: 0; + z-index: 2; + width: 100%; + display: block; + line-height: 24px; + text-align: center; +} + /* JTip styles */ .form_info a, @@ -611,7 +756,7 @@ html ul.tabs li.active a:hover { left: -11px; height: 23px; width: 10px; - top: -3px; + top: -3px; } #jt_arrow_right { @@ -620,7 +765,7 @@ html ul.tabs li.active a:hover { z-index: 101; height: 23px; width: 11px; - top: -2px; + top: -2px; } #jt { @@ -695,11 +840,11 @@ input.inplace_field, textarea.inplace_field { } a.inplace_save { - background: url(../images/icons/tick.png) 10px center no-repeat #f3f3f3; - padding-left: 30px; + background: url(../images/icons/tick.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; } a.inplace_cancel { - background: url(../images/icons/slash.png) 10px center no-repeat #f3f3f3; - padding-left: 30px; + background: url(../images/icons/slash.png) 10px center no-repeat #f3f3f3; + padding-left: 30px; }