Infra 1429 - adding webmaster view to My Account

Change-Id: Ib125a29853e01932fc80f0881a75bec073d907f9
Signed-off-by: Christopher Guindon <chris.guindon@eclipse.org>
diff --git a/eclipse.org-common/classes/friends/friend.class.php b/eclipse.org-common/classes/friends/friend.class.php
index 9ebeee3..4fb1d0d 100755
--- a/eclipse.org-common/classes/friends/friend.class.php
+++ b/eclipse.org-common/classes/friends/friend.class.php
@@ -446,6 +446,15 @@
   }
 
   /**
+   * Verify if our Friend is a Webmaster.
+   *
+   * @return boolean
+   */
+  public function checkUserIsWebmaster() {
+    return $this->_checkUserInGroup('admins');
+  }
+
+  /**
    * Verify if a user is in a group
    *
    * A group name might change in the future,
diff --git a/eclipse.org-common/classes/webmaster/firewall.class.php b/eclipse.org-common/classes/webmaster/firewall.class.php
new file mode 100644
index 0000000..47eaecd
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/firewall.class.php
@@ -0,0 +1,255 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Chrisotpher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+require_once("webmaster.class.php");
+
+class Firewall extends Webmaster {
+
+  private $search_results = NULL;
+
+  private $all_blocks = NULL;
+
+  private $recent_blocks = NULL;
+
+  private $period = "12";
+
+  public function __construct(App $App){
+    parent::__construct($App);
+    if ($this->getFormName() === 'webmaster-firewall') {
+      // Checking the page state
+      switch ($this->getState()) {
+        case 'change_recent_blocks_period':
+          $this->period = filter_var($this->App->getHTTPParameter('period', 'POST'), FILTER_SANITIZE_STRING);
+          break;
+        case 'insert_block':
+          $this->_insertBlock();
+          break;
+        case 'delete_block':
+          $this->_deleteBlock();
+          break;
+        case 'search_block':
+          $this->getSearchResults();
+          break;
+      }
+    }
+  }
+
+  /**
+   * This function gets the recent blocks
+   *
+   * @return array
+   * */
+  public function getRecentBlocks() {
+    if (is_null($this->recent_blocks)) {
+      $this->recent_blocks = $this->_fetchBlocks($this->period);
+    }
+    return $this->recent_blocks;
+  }
+
+  /**
+   * This function gets all the blocks from the Attacks table
+   *
+   * @return array
+   * */
+  public function getAllBlocks() {
+    if (is_null($this->all_blocks)) {
+      $this->all_blocks = $this->_fetchBlocks();
+    }
+    return $this->all_blocks;
+  }
+
+  /**
+   * This function gets all the search results from the Attacks table
+   *
+   * @return array
+   * */
+  public function getSearchResults() {
+    if (is_null($this->search_results) && $this->getState() === 'search_block') {
+      $this->_searchBlocks();
+    }
+    return $this->search_results;
+  }
+
+  /**
+   * This function deletes a blocked row from the Attacks table
+   * */
+  private function _deleteBlock() {
+    $subnet = filter_var($this->App->getHTTPParameter('subnet_to_delete', 'POST'), FILTER_SANITIZE_STRING);
+    if ($this->_isValidIp($subnet)) {
+      $sql = "DELETE FROM Attacks
+              WHERE Subnet = " . $this->App->returnQuotedString($this->App->sqlSanitize($subnet));
+      $delete = $this->App->infra_sql($sql);
+      $this->_updateStats($subnet,'0');
+      $this->App->setSystemMessage('delete_block','You have successfully deleted the blocked subnet: ' . $subnet . '.', 'success');
+      return TRUE;
+    }
+    $this->App->setSystemMessage('delete_block','There was a problem blocking the subnet: ' . $subnet . '.', 'danger');
+    return FALSE;
+  }
+
+  /**
+   * This function updates the Stats table
+   *
+   * @param $subnet contains a string
+   * @param $blocking contains a string of either 1 or 0
+   * */
+  private function _updateStats($subnet, $blocking_option) {
+    if ($this->_isValidIp($subnet)) {
+
+      // By default we assume we're inserting a block
+      $blocking_where = "0";
+      $count = "";
+
+      // But if we're deleting a block
+      if ($blocking_option === "0") {
+        $blocking_where = "1";
+        $count = ", Count = (Count-1)";
+      }
+      $sql = "UPDATE Stats
+              SET Blocking = ". $this->App->sqlSanitize($blocking_option) . $this->App->sqlSanitize($count) . "
+              WHERE Blocking = ". $this->App->sqlSanitize($blocking_where) ."
+              AND Subnet = " . $this->App->returnQuotedString($this->App->sqlSanitize($subnet));
+      $update = $this->App->infra_sql($sql);
+    }
+  }
+
+  /**
+   * This function fetches recent blocks based on a number of hours
+   *
+   * @param $period
+   *
+   * @return array
+   * */
+  private function _fetchBlocks($period = "") {
+    if (empty($period)) {
+      $this->App->setSystemMessage('fetch_blocks','Please select a period of time.', 'danger');
+      return;
+    }
+    $sql = "SELECT * FROM Attacks";
+    if ($period != "") {
+      $sql .= " WHERE AttackDateTime > DATE_SUB(NOW(), INTERVAL " . $this->App->sqlSanitize($period) . " HOUR)";
+    }
+    $sql .= " ORDER BY AttackDateTime DESC";
+    $result = $this->App->infra_sql($sql);
+
+    $recent_blocks = array();
+    while ($row = mysql_fetch_array($result)) {
+      $recent_blocks[] = $row;
+    }
+    if (empty($recent_blocks)) {
+      return "There weren't any blocks whitin the past <strong>". $period ." hours</strong>.";
+    }
+
+    return $recent_blocks;
+  }
+
+  /**
+   * This function validates an IP addresses or Subnets
+   *
+   * @return bool
+   * */
+  private function _isValidIp($ip) {
+    if (!empty($ip) && $ip != '0.0.0.0'){
+      if (preg_match("/^[1-9][0-9]{0,2}(\.[0-9]{1,3}){3}$/",$ip) == 1) {
+        return TRUE;
+      }
+    }
+    $msg = "The IP " . $ip." is not valid.";
+    if (empty($ip)) {
+      $msg = "You need to enter an IP address.";
+    }
+    $this->App->setSystemMessage('validate_ip',$msg,"danger");
+    return FALSE;
+  }
+
+  /**
+   * This function inserts or update attacking ip in the attacks table
+   *
+   */
+  private function _insertBlock() {
+    $ip = filter_var($this->App->getHTTPParameter('insert_block_ip', 'POST'), FILTER_SANITIZE_STRING);
+    $time = strtoupper(str_replace('_', ' ', filter_var($this->App->getHTTPParameter('insert_block_time', 'POST'), FILTER_SANITIZE_STRING)));
+    $port = filter_var($this->App->getHTTPParameter('insert_block_port', 'POST'), FILTER_SANITIZE_STRING);
+    if ($port != "22") {
+      $port = "0";
+    }
+
+
+    if (empty($time) && !filter_var($time, FILTER_SANITIZE_STRING)) {
+      $msg_type = "danger";
+      $msg = "Please select a valid amount of time for the ip to be blocked.";
+    }
+
+    // Making sure the ip is valid
+    if ($this->_isValidIp($ip) && !(isset($msg_type) && $msg_type == 'danger')) {
+
+      // getting the subnet
+      $exploded_ip = explode('.',$ip);
+      $subnet = $exploded_ip[0].".".$exploded_ip[1].".".$exploded_ip[2].".0";
+
+      $sql = "INSERT INTO Attacks
+              (AttackingIp,Subnet,Port,AttackDateTime,ExpiryDateTime,UserID,VictimNode)
+              VALUES (
+                ".$this->App->returnQuotedString($this->App->sqlSanitize($ip)).",
+                ".$this->App->returnQuotedString($this->App->sqlSanitize($subnet)).",
+                " . $this->App->returnQuotedString($this->App->sqlSanitize($port)) .",
+                NOW(),
+                DATE_ADD(NOW(), INTERVAL ". $this->App->sqlSanitize($time) ."),
+                'Webmaster',
+                'Portal'
+              )
+              ON DUPLICATE KEY
+              UPDATE
+                AttackDateTime = NOW(),
+                ExpiryDateTime = DATE_ADD(NOW(), INTERVAL ". $this->App->sqlSanitize($time) .")";
+
+      $insert = $this->App->infra_sql($sql);
+
+      $this->_updateStats($subnet, '1');
+
+      $msg_type = "success";
+      $msg = "You have successfully blocked <strong>" . $ip . "</strong> for <strong>" . $time . "</strong>.";
+    }
+    $this->App->setSystemMessage('insert_block', $msg, $msg_type);
+  }
+
+  /**
+   * This function returns an array of blocked IP addresses or Subnets
+   *
+   * @return array
+   * */
+  private function _searchBlocks() {
+    $ip = filter_var($this->App->getHTTPParameter('search_block_ip', 'POST'), FILTER_SANITIZE_STRING);
+    $search_results = array();
+
+    if ($this->_isValidIp($ip)) {
+      $sql = "SELECT DISTINCT
+              Subnet,Port,UserID,VictimNode,AttackDateTime,ExpiryDateTime
+              FROM Attacks
+              WHERE (Subnet = " . $this->App->returnQuotedString($this->App->sqlSanitize($ip)) . "
+              OR AttackingIp = " . $this->App->returnQuotedString($this->App->sqlSanitize($ip)) . ")
+              ORDER BY AttackDateTime DESC";
+      $result = $this->App->infra_sql($sql);
+
+      while ($row = mysql_fetch_array($result)) {
+        $search_results[] = $row;
+      }
+    }
+    if (empty($search_results)) {
+      $search_results[0]['no_results'] = "No results were found.";
+    }
+
+    $this->search_results = $search_results;
+    return $search_results;
+  }
+
+}
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/jobs.class.php b/eclipse.org-common/classes/webmaster/jobs.class.php
new file mode 100644
index 0000000..46ad1b5
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/jobs.class.php
@@ -0,0 +1,173 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php");
+require_once("webmaster.class.php");
+
+class Jobs extends Webmaster{
+
+  private $job_status = NULL;
+
+  private $pending_jobs = NULL;
+
+  private $rsync_config_job = "/home/admin/rsync.configs.sh";
+
+  public function __construct(App $App){
+    parent::__construct($App);
+
+    if ($this->getFormName() === 'webmaster-jobs') {
+      if ($this->getState() === 'add_job') {
+        $this->_addScheduledJobs();
+      }
+    }
+  }
+
+  /**
+   * This function gets the Jobs status
+   *
+   * @return array
+   * */
+  public function getJobStatus() {
+    if (is_null($this->job_status)) {
+      $this->_fetchJobStatus();
+    }
+    return $this->job_status;
+  }
+
+  /**
+   * This function gets the Pending Jobs
+   *
+   * @return array
+   * */
+  public function getPendingJobs() {
+    if (is_null($this->pending_jobs)) {
+      $this->_fetchPendingJobs();
+    }
+    return $this->pending_jobs;
+  }
+
+  /**
+   * This function fetches the 5 latest Jobs status
+   *
+   * @return array
+   * */
+  private function _fetchJobStatus() {
+    $sql = "SELECT
+            sj.job_id as job_id,
+            sj.job as job,
+            sj.options as options,
+            sjs.job_status as job_status,
+            sjs.node as node,
+            sjs.run_when as run_when
+            FROM scheduled_jobs as sj
+            LEFT JOIN scheduled_jobs_status as sjs
+            ON sj.job_id = sjs.job_id
+            WHERE sj.date_code < " . time() . "
+            AND sj.job_id IN
+              (
+                select DISTINCT job_id
+                FROM scheduled_jobs_status
+                ORDER BY run_when DESC
+              )
+            ORDER BY sj.job_id DESC
+            LIMIT 5";
+    $result = $this->App->eclipse_sql($sql);
+    $status = array();
+    while ($row = mysql_fetch_array($result)) {
+      $status[] = $row;
+    }
+    $this->job_status = $status;
+    return $status;
+  }
+
+  /**
+   * This function fetches the 25 latest pending jobs
+   *
+   * @return array
+   * */
+  private function _fetchPendingJobs() {
+    $sql = "SELECT
+              job_id,
+              job,
+              options,
+              date_code
+            FROM scheduled_jobs
+            WHERE job_id NOT IN (SELECT DISTINCT job_id FROM scheduled_jobs_status)
+            ORDER BY job_id
+            DESC LIMIT 25";
+    $result = $this->App->eclipse_sql($sql);
+    $pending_jobs = array();
+    while ($row = mysql_fetch_array($result)) {
+      $row['date_code'] = date("F j, Y, g:i a", $row['date_code']);
+      $pending_jobs[] = $row;
+    }
+    $this->pending_jobs = $pending_jobs;
+    return $pending_jobs;
+  }
+
+  /**
+   * This function adds new scheduled jobs to the scheduled_jobs table
+   * */
+  private function _addScheduledJobs() {
+
+    $rsync = filter_var($this->App->getHTTPParameter('rsync', 'POST'), FILTER_SANITIZE_STRING);
+    $postfix = filter_var($this->App->getHTTPParameter('postfix', 'POST'), FILTER_SANITIZE_STRING);
+    $nscd = filter_var($this->App->getHTTPParameter('nscd', 'POST'), FILTER_SANITIZE_STRING);
+    $newaliases = filter_var($this->App->getHTTPParameter('newaliases', 'POST'), FILTER_SANITIZE_STRING);
+
+    $options = '';
+    if ($rsync == 'on') {
+      $options .= "rsync,";
+    }
+    if ($postfix == 'on') {
+      $options .= "postfix,";
+    }
+    if ($nscd == 'on') {
+      $options .= "nscd,";
+    }
+    if ($newaliases == 'on') {
+      $options .= "newaliases,";
+    }
+
+    // trim any trailing commas
+    $options = rtrim($options, ",");
+
+    // Default status message
+    $msg = "The new job couldn't be created";
+    $msg_type = 'danger';
+
+    if (empty($options)) {
+      $msg .= "<br>- You need at least one rsync configs job option";
+    }
+
+    if (empty($this->rsync_config_job)) {
+      $msg .= "<br>- You need to enter a valid path for the rsync config job";
+    }
+
+    if (!empty($this->rsync_config_job) && !empty($options)) {
+      $sql = "INSERT INTO scheduled_jobs
+          (job,options,date_code)
+        VALUES (
+          '" . $this->App->sqlSanitize($this->rsync_config_job) . "',
+          " . $this->App->returnQuotedString($options) . ",
+          UNIX_TIMESTAMP()
+        )";
+      $result = $this->App->eclipse_sql($sql);
+
+      // Set Success System Message
+      $msg = 'You have successfully added a new job.';
+      $msg_type = 'success';
+    }
+
+    $this->App->setSystemMessage('add_job', $msg, $msg_type);
+  }
+}
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/mailinglists.class.php b/eclipse.org-common/classes/webmaster/mailinglists.class.php
new file mode 100644
index 0000000..b794bcb
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/mailinglists.class.php
@@ -0,0 +1,384 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+require_once("webmaster.class.php");
+
+class MailingLists extends Webmaster{
+
+  private $default_search_table_and_name = array();
+
+  private $default_search_options = array();
+
+  private $newsgroups = NULL;
+
+  private $mailing_lists = NULL;
+
+  private $search_results = NULL;
+
+  public function __construct(App $App){
+    parent::__construct($App);
+    if ($this->getFormName() === 'webmaster-mailinglists') {
+      switch ($this->getState()) {
+        case 'create':
+          $this->_createMailingLists();
+          break;
+        case 'delete':
+          $this->_deleteItem();
+          break;
+        case 'search':
+          $this->getSearchResults();
+          break;
+      }
+    }
+  }
+
+  /**
+   * This function returns the word selected wheter or not
+   * an option is selected.
+   * This helps adding the Selected parameter to <option> tags
+   * @return string
+   * */
+  public function checkSelectedOption($title, $value) {
+    if (!empty($title) && !empty($value)) {
+      foreach ($this->default_search_options as $option){
+        if ($option['title'] == $title && $option['value'] == $value) {
+          return "selected";
+        }
+      }
+    }
+  }
+
+  /**
+   * Get Newsgroups
+   */
+  public function getNewsgroups() {
+    if (is_null($this->newsgroups)) {
+      $this->_fetchMailingListsOrNewsgroups('newsgroups');
+    }
+    return $this->newsgroups;
+  }
+
+  /**
+   * Get HTML for mailing list table
+   *
+   * @param array $list
+   * @param string $table
+   */
+  public function getMailingListTable($list = array(), $table = '') {
+    $html = '';
+    if ($this->_isValidTable($table) && !empty($list)) {
+      ob_start();
+      include('tpl/mailinglists/mailinglists-table.tpl.php');
+      $html = ob_get_clean();
+    }
+    return $html;
+  }
+
+  /**
+   * Get Mailing lists.
+   */
+  public function getMailingLists(){
+    if (is_null($this->mailing_lists)) {
+      $this->_fetchMailingListsOrNewsgroups('mailing_lists');
+    }
+    return $this->mailing_lists;
+  }
+
+  /**
+   * This function returns the search results
+   *
+   * @return array
+   * */
+  public function getSearchResults() {
+    if (is_null($this->search_results)) {
+      $this->_searchForItems();
+    }
+    return $this->search_results;
+  }
+
+  /**
+   * This function return a list of status
+   * @return array
+   * */
+  public function getStatusList() {
+    return array(
+        'approve',
+        'wait',
+        'active',
+        'completed',
+        'pending'
+    );
+  }
+
+  /**
+   * This function returns the appropriate column title for the specified table
+   *
+   * @param $table - either mailing_lists or newgroups
+   *
+   * @return string
+   * */
+  public function getTableTitleName($table) {
+    $name = 'group_name';
+    if ($table == 'mailing_lists') {
+      $name = 'list_name';
+    }
+    return $name;
+  }
+
+  /**
+   * Get default_search_table_and_name value
+   */
+  public function getDefaultSearchTableAndName(){
+    return $this->default_search_table_and_name;
+  }
+
+  /**
+   * This function adds new mailing lists or newsgroups
+   * */
+  private function _createMailingLists() {
+    $table = filter_var($this->App->getHTTPParameter('create_table', 'POST'), FILTER_SANITIZE_STRING);
+    if ($this->_isValidTable($table)) {
+      $project = filter_var($this->App->getHTTPParameter('create_project', 'POST'), FILTER_SANITIZE_STRING);
+      $name = filter_var($this->App->getHTTPParameter('create_name', 'POST'), FILTER_SANITIZE_STRING);
+      $description = filter_var($this->App->getHTTPParameter('create_description', 'POST'), FILTER_SANITIZE_STRING);
+
+      // Default Message type
+      $msg_type = "success";
+
+      // Check for any empty values
+
+      if (empty($project) || empty($name) || empty($description) || $this->_itemInTable($table, $name) === TRUE || !in_array(array('ProjectID' => $project), $this->getProjects())) {
+        $msg_type = 'danger';
+      }
+      if (empty($project)) {
+        $this->App->setSystemMessage('create_mailinglist','You must select a project.', $msg_type);
+      }
+      if (!empty($project) && !in_array(array('ProjectID' => $project), $this->getProjects())) {
+        $this->App->setSystemMessage('create_mailinglist','The selected project is not part of the projects list.', $msg_type);
+      }
+      if (empty($name)) {
+        $this->App->setSystemMessage('create_mailinglist','You must enter a name.', $msg_type);
+      }
+      if (empty($description)) {
+        $this->App->setSystemMessage('create_mailinglist','You must enter a description.', $msg_type);
+      }
+      if (!empty($name) && $this->_itemInTable($table, $name) === TRUE) {
+        $this->App->setSystemMessage('create_mailinglist',$name.' already exists in the '.$table.' table.', $msg_type);
+      }
+
+      if ($msg_type != 'danger') {
+        $sql = "";
+        switch ($table) {
+          case "mailing_lists":
+            $sql = "INSERT INTO mailing_lists
+                    (list_name,list_description,is_private,project_id,list_short_description,create_date,created_by)
+                    VALUES (
+                      ".$this->App->returnQuotedString($this->App->sqlSanitize($name)).",
+                      ".$this->App->returnQuotedString($this->App->sqlSanitize($description)).",
+                      0,
+                      ".$this->App->returnQuotedString($this->App->sqlSanitize($project)).",
+                      ".$this->App->returnQuotedString($this->App->sqlSanitize($description)).",
+                      NOW(),
+                      ".$this->App->returnQuotedString("PORTAL")."
+                    )";
+            break;
+          case "newsgroups":
+            $sql = "INSERT INTO newsgroups
+                    (group_name,project_id,group_description,create_date,created_by)
+                    VALUES (
+                    ".$this->App->returnQuotedString($this->App->sqlSanitize($name)).",
+                    ".$this->App->returnQuotedString($this->App->sqlSanitize($project)).",
+                    ".$this->App->returnQuotedString($this->App->sqlSanitize($description)).",
+                    NOW(),
+                    ".$this->App->returnQuotedString("PORTAL")."
+                    )";
+            break;
+        }
+
+        $result = $this->App->eclipse_sql($sql);
+
+        $msg = "You have successfully created a new <strong>" .
+               ($table == 'mailing_lists' ? "Mailing List" : "Newsgroup") .
+               "</strong> called <strong>" . $name . "</strong>.";
+        $this->App->setSystemMessage('create_mailinglist',$msg, $msg_type);
+      }
+    }
+  }
+
+  /**
+   * This function deletes an item from a specific table
+   * */
+  private function _deleteItem() {
+    $item = filter_var($this->App->getHTTPParameter('item_to_delete', 'POST'), FILTER_SANITIZE_STRING);
+    $table = filter_var($this->App->getHTTPParameter('item_type', 'POST'), FILTER_SANITIZE_STRING);
+
+    if ($this->_isValidTable($table) && $this->_itemInTable($table, $item) === TRUE) {
+      $item_name = $this->getTableTitleName($table);
+
+      $sql = "DELETE FROM " . $table . "
+              WHERE ". $item_name ." = " . $this->App->returnQuotedString($this->App->sqlSanitize($item));
+      $delete = $this->App->eclipse_sql($sql);
+      $msg = 'You have successfully deleted <strong>' .
+              $item . '</strong> from the <strong>'. $table .'</strong> table.';
+      $this->App->setSystemMessage('delete_item', $msg, 'success');
+    }
+  }
+
+
+  /**
+   * This function fetches mailing lists of newsgroups
+   *
+   * @param $table - This is the table name
+   * @param $provision_status - NULL, pending or completed
+   * @param $limit - Number of item to fetch
+   *
+   * @return array
+   * */
+  private function _fetchMailingListsOrNewsgroups($table) {
+    $lists = array();
+    if ($this->_isValidTable($table)) {
+      $name = $this->getTableTitleName($table);
+      $sql = "SELECT ". $this->App->sqlSanitize($name) ." as name, create_date, project_id, provision_status
+              FROM " . $this->App->sqlSanitize($table) . "
+              WHERE is_deleted = 0";
+      $sql .= " ORDER BY create_date DESC LIMIT 2000";
+
+      $result = $this->App->eclipse_sql($sql);
+      while ($row = mysql_fetch_array($result)) {
+        if (is_null($row['provision_status'])){
+          $row['provision_status'] = 'NULL';
+        }
+        $lists[$row['provision_status']][] = $row;
+      }
+    }
+    $this->{$table} = $lists;
+    return $lists;
+  }
+
+  /**
+   * This function validates a new entry by checking for any duplicates
+   *
+   * @param $table - string containing the name of the table
+   * @param $entry_name - string containing the name of the new group / mailing list
+   *
+   * @return bool
+   * */
+  private function _itemInTable($table, $item_name) {
+    $sql = "SELECT * FROM " . $table;
+    $result = $this->App->eclipse_sql($sql);
+    while ($row = mysql_fetch_array($result)) {
+      if (in_array($item_name, $row)) {
+        return TRUE;
+      }
+    }
+    return FALSE;
+  }
+
+  /**
+   * This function searches through a specified table for items
+   * @return array
+   * */
+  private function _searchForItems() {
+    $table = filter_var($this->App->getHTTPParameter('search_table', 'POST'), FILTER_SANITIZE_STRING);
+    $search_results = array();
+
+    if (!empty($table) && $this->_isValidTable($table)) {
+
+      $name = $this->getTableTitleName($table);
+      $search_options = array();
+      $array_item = 0;
+
+      // Building the $search_options array using a loop depending on the options inserted in the search
+      for ($i = 1; $i <= 3; $i++) {
+        $value = filter_var($this->App->getHTTPParameter('search_'.$i, 'POST'), FILTER_SANITIZE_STRING);
+        if (!empty($value)) {
+          switch ($i) {
+            case 1:
+              $title = "project_id";
+              break;
+            case 2:
+              $title = "provision_status";
+              break;
+            case 3:
+              $title = $name;
+              break;
+          }
+          $search_options[$array_item] = array(
+            "title" => $title,
+            "value" => filter_var($this->App->getHTTPParameter('search_'.$i, 'POST'), FILTER_SANITIZE_STRING)
+          );
+          $array_item++;
+        }
+      }
+
+      $this->_setDefaultSearchFields($table, $search_options);
+
+      // Build the SQL query depending on what options were inserted in the search
+      $sql = "SELECT ". $name ." as name, create_date, project_id, provision_status
+              FROM " . $table;
+      foreach ($search_options as $key => $option) {
+        if ($key == 0) {
+          $sql_where = " WHERE ";
+        }
+        if ($key >= 1) {
+          $sql_where = " AND ";
+        }
+        $sql .= $sql_where . $search_options[$key]['title'] . " = " . $this->App->returnQuotedString($this->App->sqlSanitize($search_options[$key]['value']));
+      }
+      $sql .= " ORDER BY create_date DESC";
+      $result = $this->App->eclipse_sql($sql);
+
+      while ($row = mysql_fetch_array($result)) {
+        $row['table'] = $table;
+        $search_results[] = $row;
+      }
+
+      if (empty($search_results)) {
+        $search_results[0]['no_results'] = "No results were found.";
+      }
+    }
+    $this->search_results = $search_results;
+    return $search_results;
+  }
+
+  /**
+   * This function sets the default search fields
+   * */
+  private function _setDefaultSearchFields($table, $search_options) {
+    $this->default_search_table_and_name = array('table' => $table);
+    foreach ($search_options as $option) {
+      if ($option['title'] == 'list_name' || $option['title'] == 'group_name') {
+        $this->default_search_table_and_name['name'] = $option['value'];
+      }
+    }
+    $this->default_search_options = $search_options;
+  }
+
+  /**
+   * This function verifies if a certain tables is part of the accepted tables
+   * @param $table - Specified table name
+   * @return bool
+   * */
+  private function _isValidTable($table) {
+    $accepted_tables = array(
+        'mailing_lists',
+        'newsgroups'
+    );
+    if (!empty($table) && in_array($table, $accepted_tables)) {
+      return TRUE;
+    }
+    if (empty($table)) {
+      $this->App->setSystemMessage('create_mailinglist','You must select a table between Mailing lists or a Newsgroups.', 'danger');
+    }
+    return FALSE;
+  }
+}
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/mirrors.class.php b/eclipse.org-common/classes/webmaster/mirrors.class.php
new file mode 100644
index 0000000..63beb50
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/mirrors.class.php
@@ -0,0 +1,157 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+require_once("webmaster.class.php");
+
+class Mirrors extends Webmaster{
+
+  private $mirrors = NULL;
+
+  function __construct(App $App) {
+    parent::__construct($App);
+
+    if ($this->getFormName() === 'webmaster-mirror-update') {
+      if ($this->getState() === 'update_mirrors') {
+        $this->_updateMirrors();
+      }
+    }
+  }
+
+  /**
+   * This function returns the mirrors for approval
+   *
+   * @return array
+   * */
+  public function getMirrors($status = '') {
+    if (is_null($this->mirrors)) {
+      $this->_fetchMirrors();
+    }
+
+    if (!empty($status) && !empty($this->mirrors[$status])) {
+      return $this->mirrors[$status];
+    }
+
+    return $this->mirrors;
+  }
+
+  /**
+   * This function returns an array of Mirror statuses
+   *
+   * @return array
+   * */
+  public function getMirrorStatuses() {
+    return array(
+        'approve',
+        'wait',
+        'active',
+        'dropped',
+        'NULL',
+    );
+  }
+
+  /**
+   * This function fetches the mirrors depending on the status
+   * @param $status - string
+   * @return array
+   * */
+  private function _fetchMirrors() {
+
+    $sql = "SELECT
+      m.mirror_id,
+      m.organization,
+      m.is_internal,
+      m.create_status,
+      mp.protocol,
+      mp.base_path
+
+      FROM mirrors as m
+      LEFT JOIN mirror_protocols  as mp
+      ON m.mirror_id = mp.mirror_id";
+    $sql .= ' ORDER BY FIELD(m.create_status, NULL, "", "approve", "wait", "active", "dropped") ASC';
+    $result = $this->App->eclipse_sql($sql);
+
+    $mirrors = array();
+    while ($row = mysql_fetch_array($result)) {
+      $row['is_internal'] = 'No';
+      if ($row['is_internal'] === "1") {
+        $row['is_internal'] = 'yes';
+      }
+      if ($row['create_status'] === NULL) {
+        $create_status = 'NULL';
+      }
+      $row['row_context'] = "default";
+      switch ($row['create_status']) {
+        case 'active':
+          $row['row_context'] = "success";
+          break;
+        case 'approve':
+          $row['row_context'] = "warning";
+          break;
+        case 'wait':
+          $row['row_context'] = "info";
+          break;
+        case 'dropped':
+          $row['row_context'] = "danger";
+          break;
+        case NULL:
+          $row['create_status'] = 'NULL';
+          break;
+      }
+      $mirrors[$row['create_status']][] = $row;
+    }
+    $this->mirrors = $mirrors;
+    return $mirrors;
+  }
+
+  /**
+   * This function updates the status of selected mirrors
+   * */
+  private function _updateMirrors() {
+
+    $ids = array();
+    $mirror_status = filter_var($this->App->getHTTPParameter('status', 'POST'), FILTER_SANITIZE_STRING);
+    $mirrors = $this->getMirrors($mirror_status);
+
+    foreach ($mirrors as $mirror) {
+      $status = filter_var($this->App->getHTTPParameter('status_update_' . $mirror['mirror_id'], 'POST'), FILTER_SANITIZE_STRING);
+      if ($status != $mirror['create_status']) {
+        $ids[] = array(
+          'mirror_id' => $mirror['mirror_id'],
+          'create_status' => $status
+        );
+      }
+    }
+
+    if (!empty($ids[0]['mirror_id']) && !empty($ids[0]['create_status'])) {
+      $sql = "UPDATE mirrors SET create_status = CASE ";
+      $in = array();
+      foreach ($ids as $id) {
+        $create_status = $this->App->returnQuotedString($this->App->sqlSanitize($id['create_status']));
+        if ($id['create_status'] === 'NULL') {
+          $create_status = 'NULL';
+        }
+        $mirror_id = $this->App->returnQuotedString($this->App->sqlSanitize($id['mirror_id']));
+        $sql .= " WHEN mirror_id = " . $mirror_id . " THEN " . $create_status;
+        $in[] = $mirror_id;
+      }
+      $in = implode(', ', $in);
+      $sql .= " END WHERE mirror_id in (" . $in . ")";
+
+      $result = $this->App->eclipse_sql($sql);
+      $this->_fetchMirrors();
+      $this->App->setSystemMessage('mirror_updated', 'You have successfully updated ' . count($ids) .' mirror(s).', 'success');
+    }
+    else {
+      $this->App->setSystemMessage('mirror_updated', "The mirrors could not be updated.(#webmaster-mirrors-001)", 'danger');
+    }
+  }
+}
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/firewall.tpl.php b/eclipse.org-common/classes/webmaster/tpl/firewall.tpl.php
new file mode 100644
index 0000000..c5dd4e1
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/firewall.tpl.php
@@ -0,0 +1,186 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+if(!is_a($this, 'Firewall') || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+$recent_blocks = $this->getRecentBlocks();
+$search_results = $this->getSearchResults();
+?>
+<div class="container padding-top-25 padding-bottom-25">
+  <div id="maincontent">
+    <div id="midcolumn">
+      <h1><?php print $pageTitle; ?></h1>
+      <!-- Nav tabs -->
+      <ul class="nav nav-tabs" role="tablist">
+        <li role="presentation" class="active">
+          <a href="#firewall-recent-block" aria-controls="firewall-recent-block" role="tab" data-toggle="tab">Recent Blocks</a>
+        </li>
+        <li role="presentation">
+          <a href="#firewall-insert-block" aria-controls="firewall-insert-block" role="tab" data-toggle="tab">Insert a Block</a>
+        </li>
+        <li role="presentation">
+          <a href="#firewall-search-block" aria-controls="firewall-search-block" role="tab" data-toggle="tab">Search</a>
+        </li>
+      </ul>
+
+      <!-- Tab panes -->
+      <div class="tab-content">
+        <div role="tabpanel" class="tab-pane active" id="firewall-recent-block">
+          <p><strong>Show last:</strong></p>
+          <form action="<?php print $this->getFormActionUrl();?>" method="POST">
+            <input type="hidden" name="form_name" value="webmaster-firewall">
+
+            <div class="form-group">
+              <label class="radio-inline">
+                <input type="radio" name="period" value="12"> 12 hours
+              </label>
+              <label class="radio-inline">
+                <input type="radio" name="period" value="24"> 24 hours
+              </label>
+              <label class="radio-inline">
+                <input type="radio" name="period" value="48"> 48 hours
+              </label>
+              <label class="radio-inline">
+                <input type="radio" name="period" value="72"> 72 hours
+              </label>
+            </div>
+            <input type="hidden" name="state" value="change_recent_blocks_period">
+            <input type="submit" class="btn btn-primary" value="Change">
+          </form>
+          <?php if (!empty($recent_blocks)):?>
+            <hr>
+            <?php if (is_array($recent_blocks)): ?>
+            <table class="table table-stripped">
+              <thead>
+                <tr>
+                  <th>Subnet</th>
+                  <th>Port</th>
+                  <th>UserID</th>
+                  <th>Reporting Node</th>
+                  <th>Inserted</th>
+                  <th>Expires</th>
+                  <th>Delete</th>
+                </tr>
+              </thead>
+              <tbody>
+
+                <?php foreach ($recent_blocks as $recent_block): ?>
+                <tr>
+                  <td><?php print $recent_block['Subnet']; ?></td>
+                  <td><?php print $recent_block['Port']; ?></td>
+                  <td><?php print $recent_block['UserID']; ?></td>
+                  <td><?php print $recent_block['VictimNode']; ?></td>
+                  <td><?php print $recent_block['AttackDateTime']; ?></td>
+                  <td><?php print $recent_block['ExpiryDateTime']; ?></td>
+                  <td>
+                    <form action="<?php print $this->getFormActionUrl();?>" method="POST">
+                      <input type="hidden" name="form_name" value="webmaster-firewall">
+                      <input type="hidden" name="state" value="delete_block">
+                      <input type="hidden" name="subnet_to_delete" value="<?php print $recent_block['Subnet']; ?>">
+                      <input type="submit" class="btn btn-default btn-xs" value="DELETE">
+                    </form>
+                  </td>
+                </tr>
+                <?php endforeach; ?>
+              </tbody>
+            </table>
+            <?php else: ?>
+              <tr>
+                <td colspan="7"><?php print $recent_blocks; ?></td>
+              </tr>
+            <?php endif; ?>
+          <?php endif; ?>
+        </div>
+
+        <div role="tabpanel" class="tab-pane" id="firewall-insert-block">
+          <form class="form-horizontal" method="POST" action="<?php print $this->getFormActionUrl();?>">
+            <input type="hidden" name="form_name" value="webmaster-firewall">
+            <div class="form-group">
+              <label for="inputEmail3" class="col-sm-2 control-label">IP:<span class="required">*</span></label>
+              <div class="col-sm-10">
+                <input type="text" class="form-control" name="insert_block_ip" placeholder="IP Address">
+              </div>
+            </div>
+            <div class="form-group">
+              <label for="inputPassword3" class="col-sm-2 control-label">Port:</label>
+              <div class="col-sm-10">
+                <input type="text" class="form-control" name="insert_block_port" placeholder="Port">
+              </div>
+            </div>
+            <div class="form-group">
+              <div class="col-sm-offset-2 col-sm-10">
+                <div class="radio">
+                  <label><input type="radio" name="insert_block_time" value="1_day"> Block for 24 hours</label><br>
+                  <label><input type="radio" name="insert_block_time" value="6_month"> Block for 6 months</label><br>
+                  <label><input type="radio" name="insert_block_time" value="1_year"> Block for 1 year</label>
+                </div>
+              </div>
+            </div>
+            <div class="form-group">
+              <div class="col-sm-offset-2 col-sm-10">
+                <input type="hidden" name="state" value="insert_block">
+                <button type="submit" class="btn btn-primary">Block</button>
+              </div>
+            </div>
+          </form>
+        </div>
+
+        <div role="tabpanel" class="tab-pane" id="firewall-search-block">
+          <form method="POST" action="<?php print $this->getFormActionUrl();?>#firewall-search-block">
+            <input type="hidden" name="form_name" value="webmaster-firewall">
+            <div class="form-group">
+              <input type="search" placeholder="Search for IP/Subnet" name="search_block_ip" class="form-control">
+            </div>
+            <input type="hidden" name="state" value="search_block">
+            <input type="submit" class="btn btn-primary">
+          </form>
+          <?php if (!empty($search_results)): ?>
+            <hr>
+            <h3>Search Results</h3>
+            <table class="table">
+              <thead>
+                <tr>
+                  <th>Subnet</th>
+                  <th>Port</th>
+                  <th>UserID</th>
+                  <th>VictimNode</th>
+                  <th>AttackDateTime</th>
+                  <th>ExpiryDateTime</th>
+                </tr>
+              </thead>
+              <tbody>
+                <?php foreach ($search_results as $result): ?>
+                  <tr>
+                    <?php if (!empty($result['no_results'])): ?>
+                      <td colspan="5"><?php print $result['no_results']; ?></td>
+                    <?php else: ?>
+                      <td><?php print $result['Subnet']; ?></td>
+                      <td><?php print $result['Port']; ?></td>
+                      <td><?php print $result['UserID']; ?></td>
+                      <td><?php print $result['VictimNode']; ?></td>
+                      <td><?php print $result['AttackDateTime']; ?></td>
+                      <td><?php print $result['ExpiryDateTime']; ?></td>
+                    <?php endif; ?>
+                  </tr>
+                <?php endforeach; ?>
+              </tbody>
+            </table>
+          <?php endif; ?>
+        </div>
+      </div>
+    </div>
+    <div id="rightcolumn">
+      <?php include "sidebar.tpl.php" ?>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/index.tpl.php b/eclipse.org-common/classes/webmaster/tpl/index.tpl.php
new file mode 100644
index 0000000..8cc0bfb
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/index.tpl.php
@@ -0,0 +1,31 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+if(basename(__FILE__) == basename($_SERVER['PHP_SELF']) || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+?>
+
+<div class="container padding-top-25 padding-bottom-25">
+  <div id="maincontent">
+    <div id="midcolumn">
+      <h1><?php print $pageTitle; ?></h1>
+      <ul>
+        <li><a href="/webmaster/mirrors.php">Manage mirrors</a></li>
+        <li><a href="/webmaster/jobs.php">Work with jobs</a></li>
+        <li><a href="/webmaster/firewall.php">Manage firewall rules</a></li>
+        <li><a href="/webmaster/mailinglists.php">Manage mailing lists and newsgroups</a></li>
+      </ul>
+    </div>
+  </div>
+</div>
+
diff --git a/eclipse.org-common/classes/webmaster/tpl/jobs.tpl.php b/eclipse.org-common/classes/webmaster/tpl/jobs.tpl.php
new file mode 100644
index 0000000..af58031
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/jobs.tpl.php
@@ -0,0 +1,135 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+if(!is_a($this, 'Jobs') || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+
+$job_statuses = $this->getJobStatus();
+$pending_jobs = $this->getPendingJobs();
+
+?>
+<div class="container padding-top-25 padding-bottom-25">
+  <div id="maincontent">
+    <div id="midcolumn">
+      <h1><?php print $pageTitle; ?></h1>
+      <!-- Nav tabs -->
+      <ul class="nav nav-tabs" role="tablist">
+
+        <li role="presentation" class="active">
+          <a href="#job-add-a-job" aria-controls="job-add-a-job" role="tab" data-toggle="tab">Add a Job</a>
+        </li>
+
+        <li role="presentation">
+          <a href="#job-job-status" aria-controls="job-job-status" role="tab" data-toggle="tab">Job Status</a>
+        </li>
+
+        <li role="presentation">
+          <a href="#job-pending-jobs" aria-controls="job-pending-jobs" role="tab" data-toggle="tab">Pending jobs</a>
+        </li>
+
+      </ul>
+
+      <!-- Tab panes -->
+      <div class="tab-content">
+        <div role="tabpanel" class="tab-pane active" id="job-add-a-job">
+          <p><strong>Add rsync configs job</strong></p>
+          <form action="<?php print $this->getFormActionUrl();?>" method="POST">
+            <input type="hidden" name="form_name" value="webmaster-jobs">
+
+            <div class="checkbox">
+              <label>
+                <input type="checkbox" name="rsync" value="on">
+                Restart Rsync
+              </label>
+            </div>
+            <div class="checkbox">
+              <label>
+                <input type="checkbox" name="postfix" value="on">
+                Restart Postfix
+              </label>
+            </div>
+            <div class="checkbox">
+              <label>
+                <input type="checkbox" name="nscd" value="on">
+                Restart nscd
+              </label>
+            </div>
+            <div class="checkbox">
+              <label>
+                <input type="checkbox" name="newaliases" value="on">
+                Run newaliases
+              </label>
+            </div>
+            <input type="hidden" name="state" value="add_job">
+            <input class="btn btn-primary" type="submit" value="Add job">
+          </form>
+        </div>
+
+        <div role="tabpanel" class="tab-pane" id="job-job-status">
+          <table class="table table-stripped">
+            <thead>
+              <tr>
+                <th>Job</th>
+                <th>Options</th>
+                <th>System</th>
+                <th>Status</th>
+                <th>Date</th>
+              </tr>
+            </thead>
+            <tbody>
+              <?php foreach ($job_statuses as $job_status): ?>
+              <tr>
+                <td><?php print $job_status["job"]; ?></td>
+                <td><?php print $job_status["options"]; ?></td>
+                <td class="<?php print ($job_status['job_status'] == "OK" ? "green" : 'red'); ?>">
+                  <strong><?php print $job_status["node"]; ?></strong>
+                </td>
+                <td class="<?php print ($job_status['job_status'] == "OK" ? "green" : 'red'); ?>">
+                  <strong><?php print $job_status["job_status"]; ?></strong>
+                </td>
+                <td><?php print $job_status["run_when"]; ?></td>
+              </tr>
+              <?php endforeach; ?>
+            </tbody>
+          </table>
+        </div>
+
+        <div role="tabpanel" class="tab-pane" id="job-pending-jobs">
+          <table class="table">
+            <thead>
+              <tr>
+                <th>ID</th>
+                <th>Job</th>
+                <th>Options</th>
+                <th>Date Submitted</th>
+              </tr>
+            </thead>
+            <tbody>
+            <?php foreach ($pending_jobs as $pending_job): ?>
+              <tr>
+                <td><?php print $pending_job['job_id'];?></td>
+                <td><?php print $pending_job['job'];?></td>
+                <td><?php print $pending_job['options'];?></td>
+                <td><?php print $pending_job['date_code'];?></td>
+              </tr>
+              <?php endforeach; ?>
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+    <div id="rightcolumn">
+      <?php include "sidebar.tpl.php" ?>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/mailinglists.tpl.php b/eclipse.org-common/classes/webmaster/tpl/mailinglists.tpl.php
new file mode 100644
index 0000000..2bef506
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/mailinglists.tpl.php
@@ -0,0 +1,62 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Christopher Guindon (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+if(!is_a($this, 'MailingLists') || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+
+$projects = $this->getProjects();
+?>
+
+<div class="container padding-top-25 padding-bottom-25">
+  <div id="maincontent">
+    <div id="midcolumn">
+      <h1><?php print $pageTitle; ?></h1>
+
+       <!-- Nav tabs -->
+       <ul class="nav nav-tabs" role="tablist">
+         <li role="presentation" class="active">
+           <a href="#mailinglists-create" aria-controls="mailinglists-create" role="tab" data-toggle="tab">Create</a>
+         </li>
+         <li role="presentation">
+           <a href="#mailinglists-search" aria-controls="mailinglists-search" role="tab" data-toggle="tab">Search</a>
+         </li>
+         <li role="presentation">
+           <a href="#mailinglists-completed" aria-controls="mailinglists-completed" role="tab" data-toggle="tab">Recently Completed</a>
+         </li>
+         <li role="presentation">
+           <a href="#mailinglists-new" aria-controls="mailinglists-new" role="tab" data-toggle="tab">New/Pending</a>
+         </li>
+       </ul>
+
+       <!-- Tab panes -->
+       <div class="tab-content">
+         <div role="tabpanel" class="tab-pane active" id="mailinglists-create">
+           <?php include('mailinglists/mailinglists-create.tpl.php');?>
+         </div>
+         <div role="tabpanel" class="tab-pane" id="mailinglists-search">
+           <?php include('mailinglists/mailinglists-search.tpl.php');?>
+         </div>
+         <div role="tabpanel" class="tab-pane" id="mailinglists-completed">
+           <?php include('mailinglists/mailinglists-completed.tpl.php');?>
+         </div>
+         <div role="tabpanel" class="tab-pane" id="mailinglists-new">
+           <?php include('mailinglists/mailinglists-new.tpl.php');?>
+         </div>
+       </div>
+
+      </div>
+    </div>
+    <div id="rightcolumn">
+      <?php include "sidebar.tpl.php" ?>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-completed.tpl.php b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-completed.tpl.php
new file mode 100644
index 0000000..f3ca788
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-completed.tpl.php
@@ -0,0 +1,31 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Christopher Guindon (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+if(!is_a($this, 'MailingLists') || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+
+$mailing_list = $this->getMailingLists();
+$newsgroups_list = $this->getNewsgroups();
+
+$mailinglist['completed'] = array();
+$newsgroups['completed'] = array();
+
+if (!empty($mailing_list['completed'])) {
+  $mailinglist['completed'] = $mailing_list['completed'];
+}
+
+if (!empty($newsgroups_list['completed'])) {
+  $newsgroups['completed'] = $newsgroups_list['completed'];
+}
+
+print $this->getMailingListTable($mailinglist, 'mailing_lists');
+print $this->getMailingListTable($newsgroups, 'newsgroups');
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-create.tpl.php b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-create.tpl.php
new file mode 100644
index 0000000..e5da70c
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-create.tpl.php
@@ -0,0 +1,58 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Christopher Guindon (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+if(!is_a($this, 'MailingLists') || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+?>
+
+<form class="form-horizontal" method="POST" action="<?php print $this->getFormActionUrl();?>">
+  <input type="hidden" name="form_name" value="webmaster-mailinglists">
+  <input type="hidden" name="state" value="create">
+  <div class="form-group">
+    <div class="col-sm-18 col-md-offset-6">
+      <div class="radio">
+        <label><input type="radio" name="create_table" value="mailing_lists"> Create mailing lists</label>
+      </div>
+      <div class="radio">
+        <label><input type="radio" name="create_table" value="newsgroups"> Create newsgroups</label>
+      </div>
+    </div>
+  </div>
+  <div class="form-group">
+    <label class="col-sm-6 control-label">Projects  <span class="required">*</span></label>
+    <div class="col-sm-18">
+      <select name="create_project" class="form-control">
+        <option>Select a project</option>
+        <?php foreach ($projects as $project): ?>
+          <option value="<?php print $project['ProjectID']; ?>"><?php print $project['ProjectID']; ?></option>
+        <?php endforeach; ?>
+      </select>
+    </div>
+  </div>
+  <div class="form-group">
+    <label class="col-sm-6 control-label">Name <span class="required">*</span></label>
+    <div class="col-sm-18">
+      <input class="form-control" type="text" name="create_name" placeholder="Choose a name">
+    </div>
+  </div>
+  <div class="form-group">
+    <label class="col-sm-6 control-label">Description  <span class="required">*</span></label>
+    <div class="col-sm-18">
+      <input class="form-control" type="text" name="create_description" placeholder="Enter a description">
+    </div>
+  </div>
+  <div class="form-group">
+    <div class="col-sm-18 col-sm-offset-6">
+      <input type="submit" value="Create" class="btn btn-primary">
+    </div>
+  </div>
+</form>
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-new.tpl.php b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-new.tpl.php
new file mode 100644
index 0000000..43a377a
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-new.tpl.php
@@ -0,0 +1,28 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Christopher Guindon (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+if(!is_a($this, 'MailingLists') || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+
+$mailing_lists = $this->getMailingLists();
+$newsgroups = $this->getNewsgroups();
+
+if (isset($mailing_lists['completed'])) {
+  unset($mailing_lists['completed']);
+}
+
+if (isset($newsgroups['completed'])) {
+  unset($newsgroups['completed']);
+}
+
+print $this->getMailingListTable($mailing_lists, 'mailing_lists');
+print $this->getMailingListTable($newsgroups, 'newsgroups');
diff --git a/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-search.tpl.php b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-search.tpl.php
new file mode 100644
index 0000000..f606818
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-search.tpl.php
@@ -0,0 +1,102 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Christopher Guindon (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+if(!is_a($this, 'MailingLists') || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+
+$search_results = $this->getSearchResults();
+$default_search_table_and_name = $this->getDefaultSearchTableAndName();
+?>
+<form action="<?php print $this->getFormActionUrl();?>#mailinglists-search" method="GET" class="form-horizontal">
+  <input type="hidden" name="form_name" value="webmaster-mailinglists">
+  <div class="form-group">
+    <label class="col-sm-6 control-label">By Table <span class="required">*</span></label>
+    <div class="col-sm-18">
+      <select name="search_table" class="form-control">
+        <option value="">Select a Table</option>
+        <option <?php print ($default_search_table_and_name['table'] == 'mailing_lists' ? 'selected' : '');?> value="mailing_lists">Mailing Lists</option>
+        <option <?php print ($default_search_table_and_name['table'] == 'newsgroups' ? 'selected' : '');?> value="newsgroups">Newsgroups</option>
+      </select>
+    </div>
+  </div>
+  <div class="form-group">
+    <label class="col-sm-6 control-label">By Project</label>
+    <div class="col-sm-18">
+      <select name="search_1" class="form-control">
+        <option value="">Select a project</option>
+        <?php foreach ($projects as $project): ?>
+          <option <?php print $this->checkSelectedOption('project_id', $project['ProjectID']); ?> value="<?php print $project['ProjectID']; ?>"><?php print $project['ProjectID']; ?></option>
+        <?php endforeach; ?>
+      </select>
+    </div>
+  </div>
+  <div class="form-group">
+    <label class="col-sm-6 control-label">By Status</label>
+    <div class="col-sm-18">
+      <select name="search_2" class="form-control">
+        <option value="">Choose a status</option>
+        <?php foreach ($this->getStatusList() as $status): ?>
+          <option <?php print $this->checkSelectedOption('provision_status',$status); ?> value="<?php print $status; ?>"><?php print $status; ?></option>
+        <?php endforeach; ?>
+      </select>
+    </div>
+  </div>
+  <div class="form-group">
+    <label class="col-sm-6 control-label">By Name</label>
+    <div class="col-sm-18">
+      <input type="text" name="search_3" value="<?php print $default_search_table_and_name['name']; ?>" placeholder="Enter a name" class="form-control">
+    </div>
+  </div>
+  <input type="hidden" name="state" value="search">
+  <div class="form-group">
+    <div class="col-sm-18 col-sm-offset-6">
+      <input type="submit" value="Search" class="btn btn-primary">
+    </div>
+  </div>
+</form>
+<?php if (!empty($search_results)) :?>
+  <h3>Search Results</h3>
+  <table class="table">
+    <thead>
+      <tr>
+        <th>Name</th>
+        <th>Project ID</th>
+        <th>Provision Status</th>
+        <th>Created Date</th>
+        <th></th>
+      </tr>
+    </thead>
+    <tbody>
+      <?php foreach ($search_results as $search_result): ?>
+        <tr>
+          <?php if (!empty($search_result['no_results'])): ?>
+            <td colspan="5"><?php print $search_result['no_results']; ?></td>
+          <?php else: ?>
+            <td><?php print $search_result['name']; ?></td>
+            <td><?php print $search_result['project_id']; ?></td>
+            <td><?php print $search_result['provision_status']; ?></td>
+            <td><?php print $search_result['create_date']; ?></td>
+            <td class="text-right">
+            <form action="<?php print $this->getFormActionUrl();?>" method="POST">
+              <input type="hidden" name="form_name" value="webmaster-mailinglists">
+              <input type="hidden" name="state" value="delete">
+              <input type="hidden" name="item_type" value="<?php print $search_result['table']; ?>">
+              <input type="hidden" name="item_to_delete" value="<?php print $search_result['name']; ?>">
+              <input type="submit" class="btn btn-default btn-xs" value="DELETE">
+            </form>
+            </td>
+          <?php endif; ?>
+        </tr>
+      <?php endforeach; ?>
+    </tbody>
+  </table>
+<?php endif; ?>
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-table.tpl.php b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-table.tpl.php
new file mode 100644
index 0000000..a12d503
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/mailinglists/mailinglists-table.tpl.php
@@ -0,0 +1,52 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Christopher Guindon (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+if(!is_a($this, 'MailingLists') || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+$title = 'Mailing';
+if ($table == 'newsgroups') {
+  $title = 'Newsgroup';
+}
+?>
+
+<table class="table">
+  <thead>
+    <tr>
+      <th class="col-sm-6"><?php print $title;?> Name</th>
+      <th class="col-sm-5">Project ID</th>
+      <th class="col-sm-5">Provision Status</th>
+      <th class="col-sm-6">Created Date</th>
+      <th class="col-sm-2"></th>
+    </tr>
+  </thead>
+  <tbody>
+    <?php foreach ($list as $key => $list_group): ?>
+      <?php foreach ($list_group as $item): ?>
+        <tr>
+          <td><?php print $item['name']; ?></td>
+          <td><?php print $item['project_id']; ?></td>
+          <td><?php print $item['provision_status']; ?></td>
+          <td><?php print $item['create_date']; ?></td>
+          <td class="text-right">
+            <form action="<?php print $this->getFormActionUrl();?>" method="POST">
+              <input type="hidden" name="form_name" value="webmaster-mailinglists">
+              <input type="hidden" name="state" value="delete">
+              <input type="hidden" name="item_type" value="<?php print $table;?>">
+              <input type="hidden" name="item_to_delete" value="<?php print $item['name']; ?>">
+              <input type="submit" class="btn btn-default btn-xs" value="DELETE">
+            </form>
+          </td>
+        </tr>
+      <?php endforeach; ?>
+    <?php endforeach; ?>
+  </tbody>
+</table>
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/mirrors-table.tpl.php b/eclipse.org-common/classes/webmaster/tpl/mirrors-table.tpl.php
new file mode 100644
index 0000000..79235b8
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/mirrors-table.tpl.php
@@ -0,0 +1,56 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Christopher Guindon (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+if(basename(__FILE__) == basename($_SERVER['PHP_SELF']) || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+$statuses = $this->getMirrorStatuses();
+?>
+
+<?php if (!empty($mirrors)):?>
+  <form method="POST" name="webmaster-mirror-update" action="<?php print $this->getFormActionUrl();?>">
+    <table class="table table-stripped text-left">
+      <thead>
+        <tr>
+          <th>Mirror Id</th>
+          <th>Organization</th>
+          <th>Internal?</th>
+          <th>Protocol</th>
+          <th style="width:120px;">Status</th>
+        </tr>
+      </thead>
+      <tbody>
+        <?php foreach ($mirrors as $mirror): ?>
+          <tr class="<?php print $mirror['row_context']?>">
+            <td><?php print $mirror['mirror_id']; ?></td>
+            <td><?php print $mirror['organization']; ?><br/><?php print $mirror['base_path']; ?></td>
+            <td><?php print $mirror['is_internal']; ?></td>
+            <td><?php print $mirror['protocol']; ?></td>
+            <td class="text-right">
+              <div class="form-group form-group-sm">
+                <select name="status_update_<?php print $mirror['mirror_id']; ?>" class="form-control">
+                  <?php foreach ($statuses as $options):?>
+                    <option <?php print ($options === $mirror['create_status'] ? 'selected' : ''); ?> value="<?php print $options; ?>"><?php print $options; ?></option>
+                  <?php endforeach;?>
+                </select>
+              </div>
+            </td>
+          </tr>
+        <?php endforeach; ?>
+      </tbody>
+    </table>
+    <hr>
+    <input type="hidden" name="form_name" value="webmaster-mirror-update">
+    <input type="hidden" name="status" value="<?php print $status;?>">
+    <input type="hidden" name="state" value="update_mirrors">
+    <button class="btn btn-primary">update</button>
+  </form>
+<?php endif;?>
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/mirrors.tpl.php b/eclipse.org-common/classes/webmaster/tpl/mirrors.tpl.php
new file mode 100644
index 0000000..704dad4
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/mirrors.tpl.php
@@ -0,0 +1,54 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+if(!is_a($this, 'Mirrors') || !$this->Friend->checkUserIsWebmaster()){
+  exit();
+}
+$mirrors_array = $this->getMirrors();
+?>
+<div class="container padding-top-25 padding-bottom-25">
+  <div id="maincontent">
+    <div id="midcolumn">
+      <h1><?php print $pageTitle; ?></h1>
+
+      <?php if (!empty($mirrors_array)):?>
+        <!-- Nav tabs -->
+        <ul class="nav nav-tabs" role="tablist">
+          <?php foreach ($mirrors_array as $status => $mirrors) :?>
+            <?php $active = (!isset($active)) ? TRUE : FALSE;?>
+            <li role="presentation" <?php print ($active === TRUE ? 'class="active"' : ''); ?>><a href="#mirror_<?php print $status;?>" aria-controls="#mirror_<?php print $status;?>" role="tab" data-toggle="tab"><?php print strtoupper($status);?></a></li>
+          <?php endforeach;?>
+          <?php unset($active);?>
+        </ul>
+      <?php endif; ?>
+
+      <?php if (!empty($mirrors_array)):?>
+        <!-- Tab panes -->
+        <div class="tab-content">
+          <?php foreach ($mirrors_array as $status => $mirrors) :?>
+            <?php $active = (!isset($active)) ? TRUE : FALSE;?>
+            <div role="tabpanel" class="tab-pane <?php print ($active ? 'active' : ''); ?>" id="mirror_<?php print $status;?>">
+              <?php include('mirrors-table.tpl.php');?>
+            </div>
+          <?php endforeach;?>
+        </div>
+      <?php endif; ?>
+
+      <?php if (empty($mirrors_array)): ?>
+        <p>There are no active mirrors and mirrors that needs approval.</p>
+      <?php endif; ?>
+    </div>
+    <div id="rightcolumn">
+      <?php include "sidebar.tpl.php" ?>
+    </div>
+  </div>
+</div>
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/tpl/sidebar.tpl.php b/eclipse.org-common/classes/webmaster/tpl/sidebar.tpl.php
new file mode 100644
index 0000000..30978d2
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/tpl/sidebar.tpl.php
@@ -0,0 +1,26 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015, 2016 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+if(basename(__FILE__) == basename($_SERVER['PHP_SELF'])){
+  exit();
+}
+?>
+
+<div class="sideitem background-white">
+<h3>Webmaster</h3>
+  <ul>
+    <li><a href="/webmaster/mirrors.php">Manage mirrors</a></li>
+    <li><a href="/webmaster/jobs.php">Work with jobs</a></li>
+    <li><a href="/webmaster/firewall.php">Manage firewall rules</a></li>
+    <li><a href="/webmaster/mailinglists.php">Manage mailing lists and newsgroups</a></li>
+  </ul>
+</div>
\ No newline at end of file
diff --git a/eclipse.org-common/classes/webmaster/webmaster.class.php b/eclipse.org-common/classes/webmaster/webmaster.class.php
new file mode 100644
index 0000000..dfaac02
--- /dev/null
+++ b/eclipse.org-common/classes/webmaster/webmaster.class.php
@@ -0,0 +1,104 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation)
+ *******************************************************************************/
+
+require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php");
+
+class Webmaster {
+
+  protected $App = NULL;
+
+  protected $Friend = NULL;
+
+  protected $projects = array();
+
+  private $state = '';
+
+  public function __construct(App $App) {
+    $this->App = $App;
+
+    // Determine if the user is a webmaster
+    $Session = $this->App->useSession(true);
+    $this->Friend = $Session->getFriend();
+    if(!$this->Friend->checkUserIsWebmaster()) {
+      header("HTTP/1.1 403 Forbidden");
+      exit;
+    }
+    $this->state = filter_var($this->App->getHTTPParameter('state', 'POST'), FILTER_SANITIZE_STRING);
+  }
+
+  public function getFormActionUrl(){
+    return basename($_SERVER['SCRIPT_FILENAME']);
+  }
+
+  /**
+   * This function get all the active projects
+   *
+   * @return array
+   * */
+  public function getProjects() {
+    if (empty($this->projects)) {
+      $this->projects = $this->_fetchProjects();
+    }
+    return $this->projects;
+  }
+
+  /**
+   * This function fetches the project ID for all Active projects
+   *
+   * @return array
+   * */
+  protected function _fetchProjects() {
+    $sql = "SELECT ProjectID FROM Projects WHERE IsActive = 1";
+    $result = $this->App->foundation_sql($sql);
+    $projects = array();
+    while ($row = mysql_fetch_array($result)) {
+      $projects[] = array(
+        'ProjectID' => $row['ProjectID']
+      );
+    }
+    $this->projects = $projects;
+    return $projects;
+  }
+
+  /**
+   * Get State
+   */
+  public function getState() {
+    return $this->state;
+  }
+
+  /**
+   * Get form_name
+   */
+  public function getFormName() {
+    return filter_var($this->App->getHTTPParameter('form_name', 'POST'), FILTER_SANITIZE_STRING);
+  }
+
+  /**
+   * Fetch HTML template for specific page
+   *
+   * @param $pageTitle - Title of the page
+   * @param $page - The page we'd like to retrieve
+   */
+  public function outputPage($pageTitle, $page = "") {
+    $page = filter_var($page, FILTER_SANITIZE_STRING);
+    $path = $_SERVER['DOCUMENT_ROOT'] . '/eclipse.org-common/classes/webmaster/tpl/' . $page . '.tpl.php';
+    if (!file_exists($path)) {
+      $this->App->setSystemMessage('webmaster', 'An error has occurred. (#webmaster-001)', 'danger');
+      return '';
+    }
+    ob_start();
+    include($path);
+    return ob_get_clean();
+  }
+}
\ No newline at end of file
diff --git a/eclipse.org-common/system/app.class.php b/eclipse.org-common/system/app.class.php
index 826b80a..1c194ce 100644
--- a/eclipse.org-common/system/app.class.php
+++ b/eclipse.org-common/system/app.class.php
@@ -138,6 +138,39 @@
     date_default_timezone_set("America/Montreal");
   }
 
+/**
+   * This function returns the Webmaster object
+   * @return object
+   * */
+  public function getWebmaster($action = '') {
+    switch ($action) {
+      case "webmaster":
+        require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/classes/webmaster/webmaster.class.php");
+        return new Webmaster($this);
+        break;
+      case "mirrors":
+        require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/classes/webmaster/mirrors.class.php");
+        return new Mirrors($this);
+        break;
+      case "jobs":
+        require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/classes/webmaster/jobs.class.php");
+        return new Jobs($this);
+        break;
+      case "committers":
+        require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/classes/webmaster/committers.class.php");
+        return new Committers($this);
+        break;
+      case "firewall":
+        require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/classes/webmaster/firewall.class.php");
+        return new Firewall($this);
+        break;
+      case "mailinglists":
+        require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/classes/webmaster/mailinglists.class.php");
+        return new MailingLists($this);
+        break;
+    }
+  }
+
   /**
    * This function sets System Messages
    * @param $name - string containing the name of the message
diff --git a/eclipse.org-common/system/breadcrumbs.class.php b/eclipse.org-common/system/breadcrumbs.class.php
index 6f15fbf..9307f08 100644
--- a/eclipse.org-common/system/breadcrumbs.class.php
+++ b/eclipse.org-common/system/breadcrumbs.class.php
@@ -59,6 +59,7 @@
     "screenshots" => "Screenshots",
     "site_login" => "My Account",
     "users"  => "Getting started",
+    'webmaster' => "Webmaster",
     "" => "", //Homepage
   );
 
diff --git a/site_login/content/myaccount/en_sidebar.php b/site_login/content/myaccount/en_sidebar.php
index b5f4868..5278c4e 100644
--- a/site_login/content/myaccount/en_sidebar.php
+++ b/site_login/content/myaccount/en_sidebar.php
@@ -83,6 +83,10 @@
 </div>
 <?php endif;?>
 
+<?php if ($Friend->checkUserIsWebmaster()): ?>
+  <?php include $_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/classes/webmaster/tpl/sidebar.tpl.php" ?>
+<?php endif; ?>
+
 <div class="sideitem background-white">
 <h3>Development Tools</h3>
   <ul>
diff --git a/webmaster/_projectCommon.php b/webmaster/_projectCommon.php
new file mode 100755
index 0000000..87fe234
--- /dev/null
+++ b/webmaster/_projectCommon.php
@@ -0,0 +1,16 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2009-2015 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Denis Roy (Eclipse Foundation)- initial API and implementation
+ *    Christopher Guindon (Eclipse Foundation) - Bug 432355 - Update l&f of the Eclipse site login
+ *******************************************************************************/
+
+  $theme = "solstice";
+  $App->AddExtraJSFooter('<script type="text/javascript" src="/site_login/public/js/script.min.js"></script>');
+  $App->AddExtraHtmlHeader('<link type="text/css" href="/site_login/public/css/styles.min.css" rel="stylesheet"/>');
diff --git a/webmaster/firewall.php b/webmaster/firewall.php
new file mode 100644
index 0000000..16ff12b
--- /dev/null
+++ b/webmaster/firewall.php
@@ -0,0 +1,40 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/nav.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/menu.class.php");
+
+  $App   = new App();
+  $Nav  = new Nav();
+  $Menu   = new Menu();
+  $Firewall = $App->getWebmaster('firewall');
+
+  $App->preventCaching();
+
+  include("_projectCommon.php");
+
+  $pageTitle     = "Manage Firewall Rules";
+  $pageKeywords  = "";
+  $pageAuthor    = "Eclipse Foundation, Inc.";
+
+  // Custom theme variables
+  $variables = array();
+  $variables['main_container_classes'] = 'container-full footer-offset breadcrumbs-offset background-grey';
+  $App->setThemeVariables($variables);
+
+  ob_start();
+  print $Firewall->outputPage($pageTitle, 'firewall');
+  $html = ob_get_clean();
+
+  # Generate the web page
+  $App->generatePage($theme, $Menu, NULL, $pageAuthor, $pageKeywords, $pageTitle, $html);
diff --git a/webmaster/index.php b/webmaster/index.php
new file mode 100644
index 0000000..845070d
--- /dev/null
+++ b/webmaster/index.php
@@ -0,0 +1,40 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/nav.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/menu.class.php");
+
+  $App   = new App();
+  $Nav  = new Nav();
+  $Menu   = new Menu();
+  $Webmaster = $App->getWebmaster('webmaster');
+
+  $App->preventCaching();
+
+  include("_projectCommon.php");
+
+  $pageTitle     = "Webmaster View";
+  $pageKeywords  = "";
+  $pageAuthor    = "Eclipse Foundation, Inc.";
+
+  // Custom theme variables
+  $variables = array();
+  $variables['main_container_classes'] = 'container-full footer-offset breadcrumbs-offset background-grey';
+  $App->setThemeVariables($variables);
+
+  ob_start();
+  print $Webmaster->outputPage($pageTitle, 'index');
+  $html = ob_get_clean();
+
+  # Generate the web page
+  $App->generatePage($theme, $Menu, NULL, $pageAuthor, $pageKeywords, $pageTitle, $html);
diff --git a/webmaster/jobs.php b/webmaster/jobs.php
new file mode 100644
index 0000000..c4795b7
--- /dev/null
+++ b/webmaster/jobs.php
@@ -0,0 +1,40 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/nav.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/menu.class.php");
+
+  $App   = new App();
+  $Nav  = new Nav();
+  $Menu   = new Menu();
+  $Jobs = $App->getWebmaster('jobs');
+
+  $App->preventCaching();
+
+  include("_projectCommon.php");
+
+  $pageTitle     = "Manage Jobs";
+  $pageKeywords  = "";
+  $pageAuthor    = "Eclipse Foundation, Inc.";
+
+  // Custom theme variables
+  $variables = array();
+  $variables['main_container_classes'] = 'container-full footer-offset breadcrumbs-offset background-grey';
+  $App->setThemeVariables($variables);
+
+  ob_start();
+  print $Jobs->outputPage($pageTitle, 'jobs');
+  $html = ob_get_clean();
+
+  # Generate the web page
+  $App->generatePage($theme, $Menu, NULL, $pageAuthor, $pageKeywords, $pageTitle, $html);
diff --git a/webmaster/mailinglists.php b/webmaster/mailinglists.php
new file mode 100644
index 0000000..c4daa60
--- /dev/null
+++ b/webmaster/mailinglists.php
@@ -0,0 +1,40 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/nav.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/menu.class.php");
+
+  $App   = new App();
+  $Nav  = new Nav();
+  $Menu   = new Menu();
+  $MailingLists = $App->getWebmaster('mailinglists');
+
+  $App->preventCaching();
+
+  include("_projectCommon.php");
+
+  $pageTitle     = "Manage Mailing Lists and Newsgroups";
+  $pageKeywords  = "";
+  $pageAuthor    = "Eclipse Foundation, Inc.";
+
+  // Custom theme variables
+  $variables = array();
+  $variables['main_container_classes'] = 'container-full footer-offset breadcrumbs-offset background-grey';
+  $App->setThemeVariables($variables);
+
+  ob_start();
+  print $MailingLists->outputPage($pageTitle, 'mailinglists');
+  $html = ob_get_clean();
+
+  # Generate the web page
+  $App->generatePage($theme, $Menu, NULL, $pageAuthor, $pageKeywords, $pageTitle, $html);
diff --git a/webmaster/mirrors.php b/webmaster/mirrors.php
new file mode 100644
index 0000000..61e1343
--- /dev/null
+++ b/webmaster/mirrors.php
@@ -0,0 +1,40 @@
+<?php
+/*******************************************************************************
+ * Copyright (c) 2015 Eclipse Foundation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Eric Poirier (Eclipse Foundation) - initial API and implementation
+ *******************************************************************************/
+
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/app.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/nav.class.php");
+  require_once($_SERVER['DOCUMENT_ROOT'] . "/eclipse.org-common/system/menu.class.php");
+
+  $App   = new App();
+  $Nav  = new Nav();
+  $Menu   = new Menu();
+  $Mirrors = $App->getWebmaster('mirrors');
+
+  $App->preventCaching();
+
+  include("_projectCommon.php");
+
+  $pageTitle     = "Manage Mirrors";
+  $pageKeywords  = "";
+  $pageAuthor    = "Eclipse Foundation, Inc.";
+
+  // Custom theme variables
+  $variables = array();
+  $variables['main_container_classes'] = 'container-full footer-offset breadcrumbs-offset background-grey';
+  $App->setThemeVariables($variables);
+
+  ob_start();
+  print $Mirrors->outputPage($pageTitle, 'mirrors');
+  $html = ob_get_clean();
+
+  # Generate the web page
+  $App->generatePage($theme, $Menu, NULL, $pageAuthor, $pageKeywords, $pageTitle, $html);