Merge pull request #61 from apache/jkovalsky/notificationTemplate

More appropriate template for notifications about new NetBeans version
diff --git a/pp3/README.txt b/pp3/README.txt
index 8da513e..fe6b3cd 100644
--- a/pp3/README.txt
+++ b/pp3/README.txt
@@ -4,14 +4,15 @@
 
 Local Setup Instructions:
 
-0) required software: php >=5.6 cli, web server with php >=5.6 (apache, nginx...), mysql >=5.7, composer 
-1) clone project
-2) in root folder run $ composer install
-3) copy over config .dist files and set proper values there 
+0) required software: php >= 7.2 cli, web server with php >= 7.2 (apache, nginx...), mysql >=5.7, composer 
+1) run pear install Console_GetoptPlus
+2) clone project
+3) in root folder run $ composer install
+4) copy over config .dist files and set proper values there 
 	config/autoload/
 	module/Application/config/
-4) setup local database with config/pp3.sql script
-5) to become admin user add your google email into module/Application/config/module.config.php: pp3->admin property 
-6) set write access to data/, vendor/ and public/data folders so app can store files (user uploads, app cache etc...
-7) open pp3/public/ folder in teh browser to see the application
+5) setup local database with config/pp3.sql script
+6) to become admin user add your google email into module/Application/config/module.config.php: pp3->admin property 
+7) set write access to data/, vendor/ and public/data folders so app can store files (user uploads, app cache etc...
+8) open pp3/public/ folder in teh browser to see the application
 
diff --git a/pp3/au/Db.php b/pp3/au/Db.php
index 85a803d..1258171 100755
--- a/pp3/au/Db.php
+++ b/pp3/au/Db.php
@@ -10,16 +10,25 @@
     private static $_isConnected = false;
     private static $_insData=array();
     private static $_insCounter=0;
+    public static $link;
 
     private static function connect() {
-        $link = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD);
-        if (!$link) {
+        Db::$link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD);
+
+        if (!Db::$link) {
             throw new Exception('Can\'t connect to DB: '.DB_USER.':*****@'.DB_HOST);
+
+            die();
         }
-        $db_selected = mysql_select_db(DB_NAME, $link);
+
+        $db_selected = mysqli_select_db(Db::$link, DB_NAME);
+
         if (!$db_selected) {
             throw new Exception('Can\'t select DB: '.DB_USER.':*****@'.DB_HOST.'/'.DB_NAME);
+
+            die();
         }
+
         self::$_isConnected = true;
     }
 
@@ -32,13 +41,13 @@
         if (self::$_isConnected == false) {
             self::connect();
         }
-        $result = mysql_query($sql);
+        $result = mysqli_query(Db::$link, $sql);
         if ($result != false) {
             self::$counter++;
             return $result;
         } else {
             // log error
-            Logger::write(Logger::DEBUG, 'SQL error for: '.$sql.'; ERR: '.mysql_error());
+            Logger::write(Logger::DEBUG, 'SQL error for: '.$sql.'; ERR: '.mysqli_error(Db::$link));
             return false;
         }
     }
diff --git a/pp3/au/Importer.php b/pp3/au/Importer.php
index ffa4af1..3c12e9d 100755
--- a/pp3/au/Importer.php
+++ b/pp3/au/Importer.php
@@ -36,7 +36,7 @@
         // resolve the catalog
         if (!self::$catalogCache[$path]) {
             Db::query("INSERT INTO catalogs (path, source) VALUES ('$path', '$source')");
-            $catalogId = mysql_insert_id();
+            $catalogId = mysqli_insert_id(Db::$link);
             self::$catalogCache[$path] = $catalogId;
         } else {
             $catalogId = self::$catalogCache[$path];
@@ -47,6 +47,8 @@
             $distro_code = "";
             $config = "";
             $config_sig = 0;
+            $warning_count = 0;
+
             if (isset($match[1])) {
                 $distro_code = $match[1];
             }
@@ -65,7 +67,7 @@
         }
         if (!self::$distroCache[$distro_code]) {
             Db::query("INSERT INTO distros (distro) VALUES ('$distro_code')");
-            $distroId = mysql_insert_id();
+            $distroId = mysqli_insert_id(Db::$link);
             self::$distroCache[$distro_code] = $distroId;
         } else {
             $distroId = self::$distroCache[$distro_code];
@@ -74,7 +76,7 @@
         // resolve config
         if (!self::$configCache[$config]) {
             Db::query("INSERT INTO configs (config, signature) VALUES ('$config', $config_sig)");
-            $configId = mysql_insert_id();
+            $configId = mysqli_insert_id(Db::$link);
             self::$configCache[$config] = $configId;
         } else {
             $configId = self::$configCache[$config];
@@ -85,13 +87,13 @@
 //            // lookup db for it
 //            $res = Db::query('SELECT id FROM ips WHERE ip="' . $ip . '" LIMIT 1');
 //            if ($res) {
-//                if (mysql_num_rows($res) > 0) {
-//                    $row = mysql_fetch_assoc($res);
+//                if (mysqli_num_rows($res) > 0) {
+//                    $row = mysqli_fetch_assoc($res);
 //                    $ipId = $row['id'];
 //                    self::$ipCache[$ip] = $ipId;
 //                } else {
 //                    Db::query("INSERT INTO ips (ip, country) VALUES ('$ip' ,'" . getCCByIP($ip) . "')");
-//                    $ipId = mysql_insert_id();
+//                    $ipId = mysqli_insert_id();
 //                    self::$ipCache[$ip] = $ipId;
 //                }
 //            }
@@ -105,8 +107,8 @@
         if (!self::$userCache[$user_id]) {
             $res = Db::query('SELECT id, since, last_seen FROM users WHERE unique_id="' . $user_id . '"');
             if ($res) {
-                if (mysql_num_rows($res) > 0) {
-                    $row = mysql_fetch_assoc($res);
+                if (mysqli_num_rows($res) > 0) {
+                    $row = mysqli_fetch_assoc($res);
                     $userId = $row['id'];
                     // if this ping is newer then one in DB, save it as last seen and calc delay
                     if ($tsInt > $row['last_seen']) {
@@ -120,7 +122,7 @@
                     self::$userCache[$user_id] = array('id' => $userId, 'since' => $row['since'], 'last_seen' => $tsInt, 'catalog_id' => $catalogId, 'delay' => $delay);
                 } else {
                     Db::query("INSERT INTO users (unique_id, since, last_seen, catalog_id, delay) VALUES ('$user_id', '$ts', $tsInt, $catalogId, 9999)");
-                    $userId = mysql_insert_id();
+                    $userId = mysqli_insert_id(Db::$link);
                     self::$userCache[$user_id] = array('id' => $userId, 'since' => $ts, 'last_seen' => $tsInt, 'catalog_id' => $catalogId, 'delay' => 9999);
                 }
             }
@@ -136,8 +138,8 @@
         if (!self::$user2Cache[$user2_id]) {
             $res = Db::query('SELECT id, since, last_seen FROM users2 WHERE unique_id="' . $user2_id . '"');
             if ($res) {
-                if (mysql_num_rows($res) > 0) {
-                    $row = mysql_fetch_assoc($res);
+                if (mysqli_num_rows($res) > 0) {
+                    $row = mysqli_fetch_assoc($res);
                     $user2Id = $row['id'];
                     // if this ping is newer then one in DB, save it as last seen and calc delay
                     if ($tsInt > $row['last_seen']) {
@@ -151,7 +153,7 @@
                     self::$user2Cache[$user2_id] = array('id' => $user2Id, 'since' => $row['since'], 'last_seen' => $tsInt, 'delay' => $delay);
                 } else {
                     Db::query("INSERT INTO users2 (unique_id, since, last_seen, delay) VALUES ('$user2_id', '$ts', $tsInt,9999)");
-                    $user2Id = mysql_insert_id();
+                    $user2Id = mysqli_insert_id(Db::$link);
                     self::$user2Cache[$user2_id] = array('id' => $user2Id, 'since' => $ts, 'last_seen' => $tsInt, 'delay' => 9999);
                 }
             }
@@ -186,23 +188,23 @@
         return $ret;
     }
 
-    private function loadCache() {
+    private static function loadCache() {
         $memBeforeCache = memory_get_usage();
         $res = Db::query('SELECT id, path FROM catalogs WHERE source="' . self::$_source . '"');
         if ($res) {
-            while ($r = mysql_fetch_assoc($res)) {
+            while ($r = mysqli_fetch_assoc($res)) {
                 self::$catalogCache[$r['path']] = $r['id'];
             }
         }
         $res = Db::query('SELECT id, config FROM configs');
         if ($res) {
-            while ($r = mysql_fetch_assoc($res)) {
+            while ($r = mysqli_fetch_assoc($res)) {
                 self::$configCache[$r['config']] = $r['id'];
             }
         }
         $res = Db::query('SELECT id, distro FROM distros');
         if ($res) {
-            while ($r = mysql_fetch_assoc($res)) {
+            while ($r = mysqli_fetch_assoc($res)) {
                 self::$distroCache[$r['distro']] = $r['id'];
             }
         }
diff --git a/pp3/au/Runner.php b/pp3/au/Runner.php
index 6fbcf5a..692508f 100755
--- a/pp3/au/Runner.php
+++ b/pp3/au/Runner.php
@@ -37,8 +37,9 @@
         self::unlock();
     }
 
-    private function procesSource($source) {
+    private static function procesSource($source) {
         Logger::write(Logger::INFO, 'Starting processing soucre: ' . $source);
+
         if (self::grabSourceLogfile($source) == true) {
             // reset Db counter
             Db::$counter = 0;
@@ -50,7 +51,7 @@
 
     //dsd
 
-    private function parseLogfile($source) {
+    private static function parseLogfile($source) {
         $importCounter = 0;
         Logger::write(Logger::INFO, 'Starting parsing of the ' . $source . ' logfile ' . self::$grabbedLogFile);
         $months = array(
@@ -145,7 +146,7 @@
         return true;
     }
 
-    private function grabSourceLogfile($source) {
+    private static function grabSourceLogfile($source) {
         // get the current date
         $currentDate = strtotime('+1 day', self::getLastRunDate($source));
         Logger::write(Logger::INFO, 'Current date set to ' . date('Y-m-d', $currentDate));
@@ -186,7 +187,7 @@
         }
     }
 
-    private function getLastRunDate($source) {
+    private static function getLastRunDate($source) {
         $ld = file(LAST_DATE_FILE_PREFIX . $source);
         if ($ld) {
             Logger::write(Logger::INFO, 'Last run date identified as ' . trim($ld[0], "\n"));
@@ -196,7 +197,7 @@
         }
     }
 
-    private function incrementRunDate($source) {
+    private static function incrementRunDate($source) {
         if (file_put_contents(LAST_DATE_FILE_PREFIX . $source, date('Y-m-d', self::$currentDate)) != false) {
             Logger::write(Logger::INFO, 'Setting the last run date to ' . date('Y-m-d', self::$currentDate));
         } else {
@@ -204,7 +205,7 @@
         }
     }
 
-    private function lock() {
+    private static function lock() {
         if (file_exists(LOCKFILE)) {
             throw new Exception('Previous run still runnig, lockfile ' . LOCKFILE . ' from ' . date("F d Y H:i:s.", filemtime(LOCKFILE)) . ". Remove lockfile first\n");
         }
@@ -216,7 +217,7 @@
         }
     }
 
-    private function unlock() {
+    private static function unlock() {
         if (unlink(LOCKFILE) == true) {
             Logger::write(Logger::INFO, 'Lockfile removed');
         } else {
@@ -224,7 +225,7 @@
         }
     }
 
-    private function removeLogfile() {
+    private static function removeLogfile() {
         if (unlink(self::$grabbedLogFile) == true) {
             Logger::write(Logger::INFO, 'Source logfile removed');
         } else {
diff --git a/pp3/au/build-dashboard_v2.php b/pp3/au/build-dashboard_v2.php
index 3328d45..5e672f4 100644
--- a/pp3/au/build-dashboard_v2.php
+++ b/pp3/au/build-dashboard_v2.php
@@ -18,9 +18,9 @@
 // Connection config, edit to match your env
 $connection = array(
     'driver' => 'mysqli',
-    'host' => 'localhost',
+    'host' => '127.0.0.1',
     'username' => 'jchalupa',
-    'password' => 'root',
+    'password' => '',
     'database' => 'root',
     'profiler' => TRUE,
 );
@@ -32,7 +32,7 @@
 $debug = false;
 
 // lockfile
-$lockfile = '/tmp/run.lck';
+$lockfile = './tmp/run.lck';
 
 /*
  * == DO NOT EDIT BELOW THIS LINE UNLESS YOU ARE SURE YOU KNOW WHAT YOU ARE DOING ==
@@ -146,7 +146,7 @@
             $qr = 'REPLACE INTO results_counts SET  month="' . $month . '", release_id=' . $r['release_id'] . ', results_index_id=1, value="' . $r['count'] . '", product="' . $product . '", pack_signature=0, distro_id=0, country_id=0';
             $res2 = mysqli_query($dbc, $qr);
             if (!$res2) {
-                echo "ERR: " . mysqli_error() . $qr . "\n";
+                echo "ERR: " . mysqli_error($dbc) . $qr . "\n";
             }
         }
     } catch (Exception $e) {
@@ -178,7 +178,7 @@
             $qr = 'REPLACE INTO results_counts SET month="' . $month . '", release_id=' . $r['release_id'] . ', results_index_id=3, value="' . $r['count'] . '", product="' . $product . '", country_id=0, pack_signature=0, distro_id=0';
             $res2 = mysqli_query($dbc, $qr);
             if (!$res2) {
-                echo "ERR: " . mysqli_error() . $qr . "\n";
+                echo "ERR: " . mysqli_error($dbc) . $qr . "\n";
             }
         }
     } catch (Exception $e) {
@@ -217,7 +217,7 @@
             $qr = 'REPLACE INTO results_counts SET month="' . $month . '", country_id=' . $r['country_id'] . ', results_index_id=2, value="' . $r['count'] . '", product="' . $product . '", release_id=0, distro_id=0, pack_signature=0';
             $res2 = mysqli_query($dbc, $qr);
             if (!$res2) {
-                echo "ERR: " . mysqli_error() . $qr . "\n";
+                echo "ERR: " . mysqli_error($dbc) . $qr . "\n";
             }
         }
     } catch (Exception $e) {
@@ -250,7 +250,7 @@
             $qr = 'REPLACE INTO results_counts SET  month="' . $month . '", distro_id=' . $r['distro_id'] . ', results_index_id=4, value="' . $r['count'] . '", product="' . $product . '", release_id=0, pack_signature=0, country_id=0';
             $res2 = mysqli_query($dbc, $qr);
             if (!$res2) {
-                echo "ERR: " . mysqli_error() . $qr . "\n";
+                echo "ERR: " . mysqli_error($dbc) . $qr . "\n";
             }
         }
     } catch (Exception $e) {
@@ -292,7 +292,7 @@
             $res2 = mysqli_query($dbc, $qr);
             echo "\t" . $pack . " : " . $hits . " users\n";
             if (!$res2) {
-                echo "ERR: " . mysqli_error() . $qr . "\n";
+                echo "ERR: " . mysqli_error($dbc) . $qr . "\n";
             }
         }
     } catch (Exception $e) {
diff --git a/pp3/au/config.php b/pp3/au/config.php
index bc225c0..b0c13d2 100755
--- a/pp3/au/config.php
+++ b/pp3/au/config.php
@@ -3,19 +3,18 @@
 /**
  * Config variables 
  */
-DEFINE('LOGFILE_PATH', '/tmp');
+DEFINE('LOGFILE_PATH', './tmp');
 DEFINE('ADMIN_EMAIL', 'jan.pirek@oracle.com');
-DEFINE('LOCKFILE', '/tmp/run.lck');
+DEFINE('LOCKFILE', './tmp/run.lck');
 
 // last dates file prefix
-DEFINE('LAST_DATE_FILE_PREFIX', '/home/honza/checkout/web-content~au-statistics/apache-netbeans/last-date-');
+DEFINE('LAST_DATE_FILE_PREFIX', './last-date-');
 
 // DB connection
 DEFINE('DB_USER', 'root');
-DEFINE('DB_PASSWORD', 'root');
-DEFINE('DB_HOST', 'localhost');
+DEFINE('DB_PASSWORD', '');
+DEFINE('DB_HOST', '127.0.0.1');
 DEFINE('DB_NAME', 'jchalupa');
 
 // Path to logfiles served by local web server
-DEFINE('URL_PREFIX_DLC', 'http://localhost/work/oracle/au/logs/');
-?>
+DEFINE('URL_PREFIX_DLC', 'http://localhost/au/logs/');
\ No newline at end of file
diff --git a/pp3/au/db_connect.php.inc b/pp3/au/db_connect.php.inc
index b60f421..043b7da 100755
--- a/pp3/au/db_connect.php.inc
+++ b/pp3/au/db_connect.php.inc
@@ -1,8 +1,8 @@
 <?php
 
 DEFINE('DB_USER', 'root');
-DEFINE('DB_PASSWORD', 'root');
-DEFINE('DB_HOST', 'localhost');
+DEFINE('DB_PASSWORD', '');
+DEFINE('DB_HOST', '127.0.0.1');
 DEFINE('DB_NAME', 'jchalupa');
 
 $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME) 
diff --git a/pp3/au/lib/Getopt.php b/pp3/au/lib/Getopt.php
index d603566..313e76f 100755
--- a/pp3/au/lib/Getopt.php
+++ b/pp3/au/lib/Getopt.php
@@ -242,7 +242,8 @@
     public function __construct($rules, $argv = null, $getoptConfig = array())
     {
         if (!isset($_SERVER['argv'])) {
-            require_once 'Zend/Console/Getopt/Exception.php';
+            require_once 'Console/GetoptPlus/Exception.php';
+
             if (ini_get('register_argc_argv') == false) {
                 throw new Zend_Console_Getopt_Exception(
                     "argv is not available, because ini option 'register_argc_argv' is set Off"
diff --git a/pp3/au/lib/Getopt/Exception.php b/pp3/au/lib/Getopt/Exception.php
index cabb406..1faa4bc 100755
--- a/pp3/au/lib/Getopt/Exception.php
+++ b/pp3/au/lib/Getopt/Exception.php
@@ -23,7 +23,7 @@
 /**
  * @see Zend_Console_Getopt_Exception
  */
-require_once 'Zend/Exception.php';
+require_once 'PEAR/Exception.php';
 
 
 /**
@@ -32,7 +32,7 @@
  * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  * @license    http://framework.zend.com/license/new-bsd     New BSD License
  */
-class Zend_Console_Getopt_Exception extends Zend_Exception
+class Zend_Console_Getopt_Exception extends PEAR_Exception
 {
     /**
      * Usage
diff --git a/pp3/module/Application/src/Application/Controller/AdminController.php b/pp3/module/Application/src/Application/Controller/AdminController.php
index f91ae9a..67d0cf0 100644
--- a/pp3/module/Application/src/Application/Controller/AdminController.php
+++ b/pp3/module/Application/src/Application/Controller/AdminController.php
@@ -229,6 +229,7 @@
     }
 
     public function searchUserByEmailAction() {
+        $this->_checkAdminUser();
         /* @var $response Response */
         $response = $this->getResponse();
         $response->getHeaders()->addHeaderLine("Content-Type", "application/json");
@@ -242,7 +243,7 @@
                     'email' => $user->getEmail(),
                     'idpProviderId' => $user->getIdpProviderId(),
                     'verifier' => !!$user->isVerifier(),
-                    'admin' => !!$user->isVerifier()
+                    'admin' => !!$user->isAdmin()
                 );
             }
         }
diff --git a/pp3/module/Application/src/Application/Controller/LoginController.php b/pp3/module/Application/src/Application/Controller/LoginController.php
index 493897d..efd2db1 100644
--- a/pp3/module/Application/src/Application/Controller/LoginController.php
+++ b/pp3/module/Application/src/Application/Controller/LoginController.php
@@ -218,8 +218,8 @@
         $_SESSION['sessionUserEmail'] = $user->getEmail();
         $_SESSION['sessionIdpProviderId'] = $user->getIdpProviderId();
         $_SESSION['sessionUserName'] = $user->getName();
-        $_SESSION['isVerifier'] = $user->isVerifier();
-        $_SESSION['isAdmin'] = $user->isAdmin();
+        $_SESSION['isVerifier'] = $user->isVerifier() || $userinfo['committer'];
+        $_SESSION['isAdmin'] = $user->isAdmin() || $userinfo['pmc'];
 
         return $this->redirect()->toRoute("home");
     }
@@ -301,24 +301,32 @@
         if(! $json) {
             return null;
         }
-        $userinfo = array();
+        $userinfo = [];
         $userinfo['providerId'] = $providerId;
         if($type == 'github') {
             $userinfo['id'] = "" . $json['id'];
             $userinfo['email'] = "" . $json['email'];
             $userinfo['name'] = "" . $json['name'];
+            $userinfo['pmc'] = false;
+            $userinfo['committer'] = false;
         }  else if ($type == 'google') {
             $userinfo['id'] = "" . $json['sub'];
             $userinfo['email'] = "" . $json['email'];
             $userinfo['name'] = "" . $json['name'];
+            $userinfo['pmc'] = false;
+            $userinfo['committer'] = false;
         } else if ($type == 'amazon') {
             $userinfo['id'] = "" . $json['user_id'];
             $userinfo['email'] = "" . $json['email'];
             $userinfo['name'] = "" . $json['name'];
+            $userinfo['pmc'] = false;
+            $userinfo['committer'] = false;
         } else if ($type == 'apache' && $json['state'] == $state) {
             $userinfo['id'] = "" . $json['uid'];
             $userinfo['email'] = "" . $json['email'];
             $userinfo['name'] = "" . $json['fullname'];
+            $userinfo['pmc'] = is_array($json['pmcs']) && in_array("netbeans", $json['pmcs']);
+            $userinfo['committer'] = is_array($json['projects']) && in_array("netbeans", $json['projects']);
         }
         return $userinfo;
     }
diff --git a/pp3/module/Application/src/Application/Controller/VerificationController.php b/pp3/module/Application/src/Application/Controller/VerificationController.php
index 294f118..c70879e 100644
--- a/pp3/module/Application/src/Application/Controller/VerificationController.php
+++ b/pp3/module/Application/src/Application/Controller/VerificationController.php
@@ -22,22 +22,36 @@
 
 use Zend\View\Model\ViewModel;
 use Application\Entity\Verification;
-use Application\Pp\Catalog;
-use Zend\Session\Container;
 use Zend\Mail;
 use HTMLPurifier;
 use HTMLPurifier_Config;
+use \Application\Entity\VerificationRequest;
 
 class VerificationController extends AuthenticatedController {
 
+    /**
+     * @var \Application\Repository\NbVersionPluginVersionRepository
+     */
     private $_nbVersionPluginVersionRepository;
+    /**
+     * @var \Application\Repository\VerificationRepository
+     */
     private $_verificationRepository;
     /**
      * @var \Application\Repository\UserRepository
      */
     private $_userRepository;
+    /**
+     * @var \Application\Repository\VerificationRequestRepository
+     */
     private $_verificationRequestRepository;
+    /**
+     * @var \Application\Repository\PluginVersionRepository
+     */
     private $_pluginVersionRepository;
+    /**
+     * @var \Application\Repository\NbVersionRepository
+     */
     private $_nbVersionRepository;
 
     public function __construct($nbVersionPluginVersionRepo, $verificationRepo, 
@@ -53,39 +67,73 @@
     } 
 
     public function listAction() {
+        if(!$this->_checkVerifierUser()) {
+            return;
+        }
+        /**
+         * @var \Application\Entity\VerificationRequest[]
+         */
+        $verificationRequests = [];
+        foreach($this->_verificationRequestRepository->getVerificationRequestsForVerifier($this->getAuthenticatedUserId()) as $vr) {
+            $verificationRequests[$vr->getVerification()->getId()] = $vr;
+        }
         return new ViewModel([
-            'verificationRequests' => $this->_verificationRequestRepository->getVerificationRequestsForVerifier($this->getAuthenticatedUserId()),
-            'isAdmin' => $this->isAdmin(),
+            'verificationRequests' => $verificationRequests,
+            'pendingVerifications' => $this->_verificationRepository->getPendingVerifications(),
+            'isAdmin' => $this->isAdmin()
         ]);
     }
 
     public function voteGoAction() {
+        if(!$this->_checkVerifierUser()) {
+            return;
+        }
         return $this->_handleVote(\Application\Entity\VerificationRequest::VOTE_GO);
     }
 
     public function voteNoGoAction() {
+        if(!$this->_checkVerifierUser()) {
+            return;
+        }
         return $this->_handleVote(\Application\Entity\VerificationRequest::VOTE_NOGO);
     }
 
     public function voteUndecidedAction() {
+        if(!$this->_checkVerifierUser()) {
+            return;
+        }
         return $this->_handleVote(\Application\Entity\VerificationRequest::VOTE_UNDECIDED);
     }
 
     private function _handleVote($vote) {
-        $reqId = $this->params()->fromQuery('id');
-        $bailOut = false;
-        if (empty($reqId)) {
-            $bailOut = true;
-        }
-        $req = $this->_verificationRequestRepository->find($reqId);
-        if (!$req || $req->getVerifier()->getId() !== $this->getAuthenticatedUserId()) {
-            $bailOut = true;
-        }
-        if ($bailOut) {
+        $verificationId = $this->params()->fromQuery('id');
+        if (empty($verificationId)) {
             return $this->redirect()->toRoute('verification', array(
                 'action' => 'list'
             ));
         }
+        $verification = $this->_verificationRepository->find($verificationId);
+        if (!$verification) {
+            return $this->redirect()->toRoute('verification', array(
+                'action' => 'list'
+            ));
+        }
+        $req = null;
+        foreach($verification->getVerificationRequests() as $verificationRequest) {
+            if($verificationRequest->getVerifierId() == $this->getAuthenticatedUserId()) {
+                $req = $verificationRequest;
+                break;
+            }
+        }
+        if($req == null) {
+            $req = new VerificationRequest();
+            $req->setCreatedAt(new \DateTime('now'));
+            $req->setVerification($verification);
+            $req->setVerifier($this->_userRepository->find($this->getAuthenticatedUserId()));
+            $req->setVote(VerificationRequest::VOTE_UNDECIDED);
+            $verification->addVerificationRequest($req);
+        }
+
         $req->setVote($vote);
         $req->setVotedAt(new \DateTime('now'));
         $comment = $this->params()->fromPost('comment');
@@ -115,24 +163,28 @@
     }
 
     public function voteMasterGoAction() {
+        if(!$this->_checkAdminUser()) {
+            return;
+        }
         return $this->_handleMasterVote(\Application\Entity\Verification::STATUS_GO);
     }
 
     public function voteMasterNoGoAction() {
+        if(!$this->_checkAdminUser()) {
+            return;
+        }
         return $this->_handleMasterVote(\Application\Entity\Verification::STATUS_NOGO);
     }
 
     private function _handleMasterVote($vote) {
         $verId = $this->params()->fromQuery('id');
-        $bailOut = false;
         if (empty($verId) || !$this->isAdmin()) {
-            $bailOut = true;
+            return $this->redirect()->toRoute('verification', array(
+                'action' => 'list'
+            ));
         }
         $ver = $this->_verificationRepository->find($verId);
         if (!$ver) {
-            $bailOut = true;
-        }
-        if ($bailOut) {
             return $this->redirect()->toRoute('verification', array(
                 'action' => 'list'
             ));
@@ -155,21 +207,21 @@
     }
 
     public function createAction() {
-        $bailOut = false;
         $nbvPvId = $this->params()->fromQuery('nbvPvId');
         if (empty($nbvPvId)) {
-            $bailOut = true;
+            return $this->redirect()->toRoute('plugin', array(
+                    'action' => 'list'
+            ));
         }
         /** @var \Application\Entity\NbVersionPluginVersion $nbVersionPluginVersion   */
         $nbVersionPluginVersion = $this->_nbVersionPluginVersionRepository->find($nbvPvId);
         if (!$nbVersionPluginVersion) {
-            $bailOut = true;
+            return $this->redirect()->toRoute('plugin', array(
+                    'action' => 'list'
+            ));
         }
         $plugin = $nbVersionPluginVersion->getPluginVersion()->getPlugin();
         if (!$plugin->isOwnedBy($this->getAuthenticatedUserId()) && !$this->isAdmin()) {
-            $bailOut = true;
-        }
-        if ($bailOut) {
             return $this->redirect()->toRoute('plugin', array(
                 'action' => 'list'
             ));
@@ -256,4 +308,24 @@
         $transport->send($mail);
         // die(var_dump($mail->getBody()));
     }
+
+    private function _checkVerifierUser() {
+        if (!$this->isVerifier()) {
+            $this->redirect()->toRoute('home', array(
+                'action' => 'index'
+            ));
+            return false;
+        }
+        return true;
+    }
+
+    private function _checkAdminUser() {
+        if (!$this->isAdmin()) {
+            return $this->redirect()->toRoute('home', array(
+                'action' => 'index'
+            ));
+            return false;
+        }
+        return true;
+    }
 }
diff --git a/pp3/module/Application/src/Application/Entity/Base/Verification.php b/pp3/module/Application/src/Application/Entity/Base/Verification.php
index 7839871..b11a2d5 100644
--- a/pp3/module/Application/src/Application/Entity/Base/Verification.php
+++ b/pp3/module/Application/src/Application/Entity/Base/Verification.php
@@ -91,7 +91,10 @@
     public function getPluginVersion() {
         return $this->plugin_version;
     }      
-    
+
+    /**
+     * @return \Application\Entity\VerificationRequest[]
+     */
     public function getVerificationRequests() {
         return $this->verification_requests;
     }
diff --git a/pp3/module/Application/src/Application/Entity/Base/VerificationRequest.php b/pp3/module/Application/src/Application/Entity/Base/VerificationRequest.php
index b2b836b..60ef6ab 100644
--- a/pp3/module/Application/src/Application/Entity/Base/VerificationRequest.php
+++ b/pp3/module/Application/src/Application/Entity/Base/VerificationRequest.php
@@ -120,7 +120,10 @@
     public function setVerifierId($vid) {
         $this->verifier_id = $vid;
     }
-    
+
+    /**
+     * @return Verification
+     */
     public function getVerification() {
         return $this->verification;
     }
diff --git a/pp3/module/Application/src/Application/Repository/VerificationRepository.php b/pp3/module/Application/src/Application/Repository/VerificationRepository.php
index 9ec478b..984195f 100644
--- a/pp3/module/Application/src/Application/Repository/VerificationRepository.php
+++ b/pp3/module/Application/src/Application/Repository/VerificationRepository.php
@@ -29,4 +29,24 @@
         return $this->entityRepository;
     }
 
+    /**
+     * @param int $id
+     * @return \Application\Entity\Verification
+     */
+    public function find($id) {
+        return parent::find($id);
+    }
+
+    /**
+     * @return \Application\Entity\Verification[]
+     */
+    public function getPendingVerifications() {
+        $queryBuilder = $this->getEntityManager()->createQueryBuilder();
+        $queryBuilder->select('verification')
+        ->from('Application\Entity\Verification', 'verification')
+        ->where('verification.status IN (:status)')
+        ->orderBy('verification.created_at', 'DESC')
+        ->setParameter('status', array(\Application\Entity\Verification::STATUS_REQUESTED, \Application\Entity\Verification::STATUS_PENDING));
+        return $queryBuilder->getQuery()->getResult();
+    }
 }
diff --git a/pp3/module/Application/src/Application/Repository/VerificationRequestRepository.php b/pp3/module/Application/src/Application/Repository/VerificationRequestRepository.php
index 5bdaa04..7d3b39c 100644
--- a/pp3/module/Application/src/Application/Repository/VerificationRequestRepository.php
+++ b/pp3/module/Application/src/Application/Repository/VerificationRequestRepository.php
@@ -37,6 +37,10 @@
         );
     }
 
+    /**
+     * @param int $verifierId
+     * @return \Application\Entity\VerificationRequest[]
+     */
     public function getVerificationRequestsForVerifier($verifierId) {
         $queryBuilder = $this->getEntityManager()->createQueryBuilder();
         $queryBuilder->select('vrq, verification, verifier, nbVersionPluginVersion, pluginVersion, plugin, nbVersion')
diff --git a/pp3/module/Application/view/application/verification/list.phtml b/pp3/module/Application/view/application/verification/list.phtml
index 95bcfe2..172fd12 100644
--- a/pp3/module/Application/view/application/verification/list.phtml
+++ b/pp3/module/Application/view/application/verification/list.phtml
@@ -36,10 +36,10 @@
     </thead>
     <tbody>
 <?php
-    foreach ($this->verificationRequests as $vReq) {
-        $verification = $vReq->getVerification();
+    foreach ($this->pendingVerifications as $verification) {
+        $verificationRequest = $this->verificationRequests[$verification->getId()];
         $nbVersion = $verification->getNbVersionPluginVersion()->getNbVersion();
-        $pluginVersion = $vReq->getVerification()->getNbVersionPluginVersion()->getPluginVersion();
+        $pluginVersion = $verification->getNbVersionPluginVersion()->getPluginVersion();
         $plugin = $pluginVersion->getPlugin();
         echo '<tr>
         <td>
@@ -60,14 +60,18 @@
         <td>
             <span class="badge '.$verification->getStatusBadgeClass().'" title="'.$verification->getStatusBadgeTitle().'">'.$verification->getStatusBadgeTitle().'</span>
         </td>
-        <td>    
-            <span class="badge '.$vReq->getVoteBadgeClass().'" title="'.$vReq->getVoteBadgeTitle().'">'.$vReq->getVoteBadgeTitle().'</span>
-        </td>
+        <td>';
+        printf(
+            '<span class="badge %1$s" title="%2$s">%2$s</span>',
+            htmlspecialchars($verificationRequest ? $verificationRequest->getVoteBadgeClass() : ''),
+            htmlspecialchars($verificationRequest ? $verificationRequest->getVoteBadgeTitle() : 'Undecided')
+        );
+        echo '</td>
         <td>
         <div>
-        <a href="'.$this->url('verification', array('action'=>'vote-go'), array('query' => array('id'=>$vReq->getId()))).'" class="btn btn-success" role="button">Go</a>
-        <a href="'.$this->url('verification', array('action'=>'vote-undecided'), array('query' => array('id'=>$vReq->getId()))).'" class="btn btn-default" role="button">Undecided</a>
-        <button type="button" class="btn btn-danger" role="button" data-toggle="modal" data-target="#noGoModal-'.$vReq->getId().'">NoGo</button>
+        <a href="'.$this->url('verification', array('action'=>'vote-go'), array('query' => array('id'=>$verification->getId()))).'" class="btn btn-success" role="button">Go</a>
+        <a href="'.$this->url('verification', array('action'=>'vote-undecided'), array('query' => array('id'=>$verification->getId()))).'" class="btn btn-default" role="button">Undecided</a>
+        <button type="button" class="btn btn-danger" role="button" data-toggle="modal" data-target="#noGoModal-'.$verification->getId().'">NoGo</button>
         
         ';
 
@@ -80,8 +84,8 @@
         }
         echo '
         </div>
-        <div class="modal fade" id="noGoModal-'.$vReq->getId().'" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
-            <form action="'.$this->url('verification', array('action'=>'vote-nogo'), array('query' => array('id'=>$vReq->getId()))).'" method="post">
+        <div class="modal fade" id="noGoModal-'.$verification->getId().'" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+            <form action="'.$this->url('verification', array('action'=>'vote-nogo'), array('query' => array('id'=>$verification->getId()))).'" method="post">
                 <div class="modal-dialog" role="document">
                     <div class="modal-content">
                     <div class="modal-header">