From 0ffa58ac3b7688b4849d082abf0378b78f3cb618 Mon Sep 17 00:00:00 2001 From: Eudyptula Date: Fri, 17 Jul 2009 12:27:02 -0400 Subject: Fixed bugs and added new/popular builds to welcome page --- frontend/css/build.css | 10 ++++++++++ frontend/modules/gentoo/step3.php | 20 +++++++++++--------- frontend/pages/downloadimage.php | 10 ++++++++-- frontend/pages/welcome.php | 33 +++++++++++++++++++++++++++++++++ shared/classes/0sql_row_obj.php | 6 +++++- shared/classes/build.php | 13 ++++++++----- shared/classes/download.php | 29 +++++++++++++++++++++++++++++ shared/classes/gentoo_package.php | 2 ++ todo | 7 +------ 9 files changed, 107 insertions(+), 23 deletions(-) create mode 100644 shared/classes/download.php diff --git a/frontend/css/build.css b/frontend/css/build.css index 7ae7345..43ade38 100644 --- a/frontend/css/build.css +++ b/frontend/css/build.css @@ -25,6 +25,16 @@ div.build span.status.queued { div.build span.status.config { color: teal; } +div.build span.downloads { + font-weight: bold; + font-size: 90%; + text-align: right; + float: right; +} +div.build span.downloads span.time { + font-size: 90%; + font-family: monospace; +} div.build span.links { font-size: 90%; } diff --git a/frontend/modules/gentoo/step3.php b/frontend/modules/gentoo/step3.php index 89e7ec3..a4f1b30 100644 --- a/frontend/modules/gentoo/step3.php +++ b/frontend/modules/gentoo/step3.php @@ -4,17 +4,19 @@ if (in_array('timezone', $opts)) $this->select('timezone', 'timezone', 'Timezone', get_timezones()); if (in_array('dev-manager', $opts)) $this->select('dev-manager', 'dev-manager', '/dev Manager', array('udev' => 'udev', 'static-dev' => 'Static /dev')); -$profile=new sql_gentoo_profile($opts['profile']); -foreach (explode(' ', $this->get_opt('pkgsets')) as $pkgset) { - $pkgset=new sql_gentoo_pkgset($pkgset); - $pkgs=array(); - foreach (explode("\n", $pkgset->packages) as $pkg) { - if ($obj=sql_gentoo_package::from_atom($pkg, $profile)) { - $array=$obj->to_array(); - $pkgs[$pkg]="$pkg: {$array['desc']}"; +$profile=new sql_gentoo_profile($this->get_opt('profile')); +if (strlen($pkgsets=$this->get_opt('pkgsets'))) { + foreach (explode(' ', $this->get_opt('pkgsets')) as $pkgset) { + $pkgset=new sql_gentoo_pkgset($pkgset); + $pkgs=array(); + foreach (explode("\n", $pkgset->packages) as $pkg) { + if ($obj=sql_gentoo_package::from_atom($pkg, $profile)) { + $array=$obj->to_array(); + $pkgs[$pkg]="$pkg: {$array['desc']}"; + } } + $this->checkbox_array('pkgset-'.$pkgset->id, 'pkgset-'.$pkgset->id, $pkgset->name, $pkgs); } - $this->checkbox_array('pkgset-'.$pkgset->id, 'pkgset-'.$pkgset->id, $pkgset->name, $pkgs); } // TODO This shouldn't be a step at all, it should be in wizard.php to choose between bundlers // TODO This shouldn't be part of configurations, except possibly a default value. It should be for builds diff --git a/frontend/pages/downloadimage.php b/frontend/pages/downloadimage.php index 91c1161..2234a10 100644 --- a/frontend/pages/downloadimage.php +++ b/frontend/pages/downloadimage.php @@ -4,16 +4,20 @@ function init_downloadimage() { if (!isset($S['user'])) { return 'login'; } - if (!isset($request['build']) || !preg_match('/^[a-zA-Z0-9]{6}$/', $request['build'])) { + if (!(isset($request['build']) && strlen($request['build']) == 6 && ctype_alnum($request['build']))) { debug('downlaodimage', 'No build or badly formatted build requested'); return '404'; } - $r=$S['pdo']->query('SELECT * FROM `builds` WHERE `owner`='.$S['user']->id.' AND `id`="'.$request['build'].'"'); + $r=$S['pdo']->query('SELECT * FROM `builds` WHERE `id`="'.$request['build'].'"'); if ($r->rowCount() == 0) { debug('downloadimage', 'build not found or not owned by user'); return '404'; } $build=new sql_build($r->fetch(PDO::FETCH_ASSOC)); + if (!owner_or_admin($build->owner)) { + debug('downloadimage', 'Permission denied'); + return '404'; + } $files=glob(COMPLETED.'/build-'.$build->id.'.*'); if (count($files)) { if (count($files) > 1) { @@ -30,6 +34,8 @@ function init_downloadimage() { debug('downloadimage', 'image file not found'); return '404'; } + $dl=new sql_download($build->id, $S['user']->id, time()); + $dl->write(); contenttype('application/octet-stream'); header('Content-Length: '.filesize($S['file'])); header('Content-Description: File Transfer'); diff --git a/frontend/pages/welcome.php b/frontend/pages/welcome.php index d326f08..4b727e3 100644 --- a/frontend/pages/welcome.php +++ b/frontend/pages/welcome.php @@ -6,5 +6,38 @@ function init_welcome() { function body_welcome() { global $S; echo '

Welcome

'; + echo '

Most Popular Downloads

'; + $r=$S['pdo']->query('SELECT `build` FROM `downloads` GROUP BY `build` ORDER BY COUNT(*) DESC LIMIT 3'); + if ($r->rowCount()) { + while ($build=$r->fetch(PDO::FETCH_COLUMN)) { + $build=new sql_build($build); + echo $build->display(); + } + } else { + echo print_warning('No downloads'); + } + echo '
'; + echo '

Recently Downloaded

'; + $r=$S['pdo']->query('SELECT * FROM `downloads` ORDER BY `time` DESC LIMIT 3'); + if ($r->rowCount()) { + while ($download=$r->fetch(PDO::FETCH_ASSOC)) { + $download=new sql_download($download); + echo $download->get_build()->display(); + } + } else { + echo print_warning('No downloads'); + } + echo '
'; + echo '

Recently Built

'; + $r=$S['pdo']->query('SELECT * FROM `builds` WHERE `status`="finished/success" ORDER BY `finish` DESC LIMIT 3'); + if ($r->rowCount()) { + while ($build=$r->fetch(PDO::FETCH_ASSOC)) { + $build=new sql_build($build); + echo $build->display(); + } + } else { + echo print_warning('No finished builds'); + } + echo '
'; } ?> diff --git a/shared/classes/0sql_row_obj.php b/shared/classes/0sql_row_obj.php index 564da0a..a86552a 100644 --- a/shared/classes/0sql_row_obj.php +++ b/shared/classes/0sql_row_obj.php @@ -232,7 +232,11 @@ abstract class sql_row_obj { // If the name of this class changes, it must be up } $r=self::sql_query('SELECT * FROM `'.$this->table.'` WHERE '.$this->sql_id()); if ($r->rowCount() == 0) { - throw new Exception(get_class($this).' object constructed with '.func_num_args().' values '.$this->sql_id().' but no rows were found (PRIMARY KEY).'); + if (count($this->primary_key) == count($this->columns)) { + $this->values=$this->db_values; + $this->db_values=array(); + } else + throw new Exception(get_class($this).' object constructed with '.func_num_args().' values '.$this->sql_id().' but no rows were found (PRIMARY KEY).'); } else { $this->from_array($r->fetch(), true); return; diff --git a/shared/classes/build.php b/shared/classes/build.php index 8b19b41..8212d56 100644 --- a/shared/classes/build.php +++ b/shared/classes/build.php @@ -50,7 +50,7 @@ class sql_build extends conf_build_common { // Returns HTML code describing this build's status (for human consumption) function display() { global $S; - $format='D j M Y G:i:s'; + $format='D j M Y G:i:s T'; $html='
'.(isset($this->name) && strlen($this->name)?htmlentities($this->name):'Unnamed Build').' '; $status=explode('/', $this->status, 2); if ($status[0] == 'config') { @@ -71,7 +71,10 @@ class sql_build extends conf_build_common { } elseif ($status[0] == 'finished') { $status=explode(': ', $status[1], 2); if ($status[0] == 'success') { - $html.='[successful]
Download imageBuild log'; + $r=$S['pdo']->query('SELECT COUNT(*) as `count`, MAX(`time`) as `time` FROM `downloads` WHERE `build`="'.$this->id.'"'); + $r=$r->fetch(PDO::FETCH_ASSOC); + $d=$r['count'].' download'.($r['count'] != 1?'s':'').($r['count']?'
(last at '.date($format, $r['time']).')':''); + $html.=''.$d.'[successful]
Download imageBuild log'; } elseif ($status[0] == 'failed') { $html.='[failed: '.htmlentities($status[1]).']
View output of failed commandBuild log'; } elseif ($status[0] == 'uploading') { @@ -83,11 +86,11 @@ class sql_build extends conf_build_common { throw_exception('Unrecognized build status '.$this->status); } if (isset($this->ctime)) { - $html.='
Submitted for build at: '.date($format, $this->ctime).' UTC
'; + $html.='
Submitted for build at: '.date($format, $this->ctime).'
'; if (isset($this->start)) { - $html.='Build started at: '.date($format, $this->start).' UTC
'; + $html.='Build started at: '.date($format, $this->start).'
'; if (isset($this->finish)) { - $html.='Build finished at: '.date($format, $this->finish).' UTC
Total build time: '.display_time($this->finish-$this->start).''; + $html.='Build finished at: '.date($format, $this->finish).'
Total build time: '.display_time($this->finish-$this->start).''; } else { $html.='Running for: '.display_time(time()-$this->start).''; } diff --git a/shared/classes/download.php b/shared/classes/download.php new file mode 100644 index 0000000..f255f1c --- /dev/null +++ b/shared/classes/download.php @@ -0,0 +1,29 @@ + array ( + 'type' => 'CHAR', + 'length' => 6, + 'not_null' => true, + 'default' => '', + 'refers_to' => 'builds.id' + ), + 'user' => array ( + 'type' => 'INT', + 'length' => 10, + 'unsigned' => true, + 'not_null' => true, + 'default' => 0, + 'refers_to' => 'users.id' + ), + 'time' => array ( + 'type' => 'INT', + 'length' => 10, + 'unsigned' => true, + 'not_null' => true, + 'default' => 0 + ) + + ); +} +?> diff --git a/shared/classes/gentoo_package.php b/shared/classes/gentoo_package.php index 4426565..41fec01 100644 --- a/shared/classes/gentoo_package.php +++ b/shared/classes/gentoo_package.php @@ -68,6 +68,7 @@ class sql_gentoo_package extends sql_row_obj { } public static function from_atom($atom, &$profile=null) { global $S; + if (strlen($atom) == 0) return null; if (strpos($atom, '/')) { list($bcat, $name)=explode('/', $atom); if ($i=strpos($bcat, '-')) { @@ -75,6 +76,7 @@ class sql_gentoo_package extends sql_row_obj { $bcat=substr($bcat, 0, strlen($bcat)-strlen($lcat)); } } else { + $bcat=$lcat=null; $name=$atom; } $c=array(); diff --git a/todo b/todo index 821c195..6bc708c 100644 --- a/todo +++ b/todo @@ -1,19 +1,14 @@ -Have backend handle builds that it finds to already be running (break in to steps and store current status) -Make package adding friendly for browsers without JS/CSS (use ul/li, not div) Write a live git ebuild -Write an AJAX-based self-updating status viewer *** Add logging besides just commands *** -Make backend do a dummy run through and queue all commands and other tasks, then execute them (for better status handling, easier debugging, etc.) Have builds and tasks not give links to logs if we're already viewing the logs Either make task status a TEXT or stop putting command name in the status (via thrown exception) - we can fetch this later anyway - just store the task id that failed (or use the last task) -Consider saving env. passed to tasks, path if we ever use it +Consider logging env. passed to tasks, path if we ever use it Add metadata back to logviewer Add a statistics page Add a profiles management page/backend utility Add cleanup functions to the frontend and backend (tasks dir in backend containing scripts that can be launched through frontend) Separate variables we got from the URL from the rest, stop using $request, instead keep super globals and strip slashes on them Add masked indicator back to step2, support ~arch installation -Completely plan out how frontend modules should function - each step needs to report if it finished successfully (required values) Allow backend to define bail-out functions to call when it dies (things like unmounting the ISO it was copying) Add STDERR (maybe STDOUT) only option to log viewer Simplify status to numeric on builds - varchar isn't necessary -- cgit v1.2.3-65-gdbad