htdocs/index.php

Summary

Maintainability
F
3 days
Test Coverage
<?php

require_once 'phplib/CurlClient.php';
require_once 'phplib/Postmortem.php';
require_once 'phplib/Configuration.php';
require_once 'phplib/Auth.php';

require_once 'vendor/autoload.php';

@include 'phplib/deploy_version.php';

if (!defined('MORGUE_VERSION')) {
    define('MORGUE_VERSION', '');
}

$config = Configuration::get_configuration();

if (!$config) {
    $message = "Could not parse configuration file.";
    $content = "error";
    error_log("ERROR: " . $message);
    include '../views/page.php';
    die();
}
$app = new \Slim\Slim(array(
    'cookies.encrypt' => true,
    'cookies.secret_key' => 'PMS_R_US',
    'cookies.cipher' => MCRYPT_RIJNDAEL_256,
    'cookies.cipher_mode' => MCRYPT_MODE_CBC,
));
$app->config('debug', true);

$app->log->setEnabled(true);

if ($config['environment'] == "development") {
    $app->log->setLevel(4);
} else {
    $app->log->setLevel(1);
}

// must be require_once'd after the Slim autoloader is registered
require_once 'phplib/AssetVersionMiddleware.php';

// helper method for returning the selected timezone.
// If set, get the user timezone else get it from the global config
// otherwise default to 'America/New_York'
// returns: string
function getUserTimezone() {
    $config = Configuration::get_configuration();
    $tz = 'America/New_York';

    if ( isset($_SESSION['timezone']) ) {
      $tz = $_SESSION['timezone'];
    } else if ( isset($config['timezone']) ) {
      $tz = $config['timezone'];
    }

    return $tz;
}

// helper method to sort events reverse by starttime
function cmp($first, $second) {
    if ($first['starttime'] == $second['starttime'] ) {
        return 0;
    }
    return ( $first['starttime'] < $second['starttime'] ) ? 1 : -1;
}
// helper method to format the difference between two dates
// param: diff between two dates
// returns: string
function getTimeString($diff) {
    $min = floor($diff / 60 % 60);
    $hours = floor($diff / 60 / 60);
    if ($min == 1) {
        $min = $min." minute";
    } else {
        $min = $min." minutes";
    }
    if ($hours == 1) {
        $hours = $hours." hour";
    } else {
        $hours = $hours." hours";
    }
    return $hours.", ".$min;
}

/**
 * Helper function for default statustime.
 */
function default_status_time() {
    return new DateTime('1970-01-01', new DateTimeZone('UTC'));
}


$app->add(
    new \Slim\Middleware\SessionCookie(
        array(
            'expires' => '60 minutes',
            'path' => '/',
            'domain' => null,
            'secure' => false,
            'httponly' => false,
            'name' => 'postmortem',
        )
    )
);

$app->add(new AssetVersionMiddleware);



/*
 * Now include all routes and libraries for features before actually running the app
 */
foreach ($config['feature'] as $feature) {
    if ($feature['enabled'] == "on") {
        $app->log->debug("Including Feature {$feature['name']}");
        include  'features/' . $feature['name'] . '/lib.php';
        include  'features/' . $feature['name'] . '/routes.php';
    }
}



// set admin info on the environment array
// so it's available to our request handlers
$env = $app->environment;
$env['admin'] = MorgueAuth::get_auth_data();

$app->get('/', function() use ($app) {
    $content = 'content/frontpage';
    $show_sidebar = true;

    $selected_tags = trim($app->request()->get('tags'));
    if (strlen($selected_tags) > 0) {
        $selected_tags = explode(",", $selected_tags);
        $selected_tags = array_map('trim', $selected_tags);
        $events = Postmortem::get_events_for_tags($selected_tags);
    } else {
        $selected_tags = null;
        $events = Postmortem::get_all_events();
    }

    if ($events["status"] == Postmortem::OK) {
        $events = $events["values"];
    } else {
        $app->response->status(500);
        echo json_encode($events["error"]);
        return;
    }

    uasort($events, 'cmp');

    $tags = Postmortem::get_tags();

    if ($tags["status"] == Postmortem::OK) {
        $tags = $tags["values"];
    } else {
        $tags = array();
    }


    include 'views/page.php';
});

$app->post('/timezone', function () use ($app) {
    $_SESSION['timezone'] = $app->request->post('timezone');
    $app->redirect($app->request()->getReferrer());
});

$app->post('/events', function () use ($app) {
    $title = $app->request->post('title');
    $start_date = $app->request->post('start_date');
    $start_time = $app->request->post('start_time');
    $end_date = $app->request->post('end_date');
    $end_time = $app->request->post('end_time');
    $detect_date = $app->request->post('detect_date');
    $detect_time = $app->request->post('detect_time');
    $status_date = $app->request->post('status_date');
    $status_time = $app->request->post('status_time');
    $timezone = $app->request->post('timezone');
    $severity = $app->request->post('severity');
    $problem_type = $app->request->post('problem_type');
    $impact_type = $app->request->post('impact_type');
    $incident_cause = $app->request->post('incident_cause');
    $subsystem = $app->request->post('subsystem');
    $owner_team = $app->request->post('owner_team');
    $startdate = new DateTime($start_date." ".$start_time, new DateTimeZone($timezone));
    $enddate = new DateTime($end_date." ".$end_time, new DateTimeZone($timezone));
    $detectdate = new DateTime($detect_date." ".$detect_time, new DateTimeZone($timezone));
    if (!$status_date || !$status_time) {
        $statusdate = default_status_time();
    } else {
        $statusdate = new DateTime("$status_date $status_time", new DateTimeZone($timezone));
    }

    $event = array(
        "title" => $title,
        "summary" => "",
        "why_surprised" => "",
        "tldr" => "",
        "meeting_notes_link" => "",
        "facilitator" => "",
        "starttime" => $startdate->getTimeStamp(),
        "endtime" => $enddate->getTimeStamp(),
        "statustime" => $statusdate->getTimeStamp(),
        "detecttime" => $detectdate->getTimeStamp(),
        "severity" => $severity,
        "problem_type" => $problem_type,
        "impact_type" => $impact_type,
        "incident_cause" => $incident_cause,
        "subsystem" => $subsystem,
        "owner_team" => $owner_team
    );

    $event = Postmortem::save_event($event);
    if(isset($event['error'])) {
        error_log(print_r($event,true));
    }
    $app->redirect('/events/'.$event["id"]);
});

$app->get('/events/:id', function($id) use ($app) {

    $event = Postmortem::get_event($id);
    if (is_null($event["id"])) {
        echo "loooool";
        $app->response->status(404);
        return;
    }

    $page_title = sprintf("%s | Morgue", $event['title']);

    $starttime = $event["starttime"];
    $endtime = $event["endtime"];
    $detect_time = $event["detecttime"];
    $status_time = $event["statustime"];
    $timezone = getUserTimezone();
    $severity = $event["severity"];
    $problem_type = $event["problem_type"];
    $facilitator = $event["facilitator"];
    $subsystem = $event["subsystem"];
    $owner_team = $event["owner_team"];
    $impact_type = $event["impact_type"];
    $incident_cause = $event["incident_cause"];
    $gcal = $event["gcal"];
    $contact = $event["contact"];
    $summary = $event["summary"];
    $why_surprised = $event["why_surprised"];
    $tldr = $event["tldr"];
    $meeting_notes_link = $event["meeting_notes_link"];

    $tz = new DateTimeZone($timezone);
    $start_datetime = new DateTime("@$starttime");
    $start_datetime->setTimezone($tz);
    $end_datetime = new DateTime("@$endtime");
    $end_datetime->setTimezone($tz);
    if ($status_time) {
        $status_datetime = new DateTime("@$status_time");
        $status_datetime->setTimezone($tz);
    } else {
        $status_datetime = false;
    }
    $detect_datetime = new DateTime("@$detect_time");
    $detect_datetime->setTimezone($tz);
    $impacttime = getTimeString($endtime - $starttime);
    if ($endtime >= $detect_time) {
        $resolvetime = getTimeString($endtime - $detect_time);
    } else {
        $resolvetime = $impacttime;
    }
    $undetecttime = getTimeString($detect_time - $starttime);

    $edit_status = Postmortem::get_event_edit_status($event);

    $content = 'content/edit';

    $curl_client = new CurlClient();

    $show_sidebar = false;
    include 'views/page.php';
});

$app->delete('/events/:id', function($id) use ($app) {
    header("Content-Type: application/json");
    $res = Postmortem::delete_event($id);
    if ($res["status"] == Postmortem::ERROR) {
        $app->response->status(500);
        echo json_encode($res["error"]);
    } else {
        $app->response->status(204);
    }
});

$app->get('/events/:id/undelete', function($id) use ($app) {
    $res = Postmortem::undelete_event($id);
    if ($res["status"] == Postmortem::ERROR) {
        $app->response->status(500);
        $app->response->body($res["error"]);
    } else {
        $app->redirect("/events/$id");
    }
});

$app->get('/events/:id/summary', function($id) use ($app) {
    $event = Postmortem::get_event($id);

    if (is_null($event["id"])) {
        $app->response->status(404);
        return;
    }
    header("Content-Type: application/json");
    echo json_encode(array("summary" => $event["summary"]));

});

$app->get('/events/:id/lock', function($id) use ($app) {
    header("Content-Type: application/json");
    $event = Postmortem::get_event($id);
    
    $status = Postmortem::get_event_edit_status($event);
    
    if($status === Postmortem::EDIT_UNLOCKED) {
        Postmortem::set_event_edit_status($id);
    } 

    $return = ["status" => $status, "modifier" => $event["modifier"]];

    echo json_encode($return);
});

$app->put('/events/:id', function ($id) use ($app) {
    // get the base event data
    $old_event = Postmortem::get_event($id);
    if (is_null($old_event["id"])) {
        $app->response->status(500);
        return;
    }

    $event = ["title" => $old_event["title"], "id" => $id];

    $params = $app->request->params();
    foreach ($params as $key => $value) {
        switch($key) {
        case "title":
            $event["title"] = $value;
            break;
        case "summary":
            $event["summary"] = $value;
            break;
        case "why_surprised":
            $event["why_surprised"] = $value;
            break;
        case "tldr":
            $event["tldr"] = $value;
            break;
        case "meeting_notes_link":
            $event["meeting_notes_link"] = $value;
            break;
        case "start_date":
        case "start_time":
            if (!isset($params["timezone"])) {
                $app->response->status(400);
                return;
            }
            $timezone = new DateTimeZone($params["timezone"]);
            $starttime = isset($event["starttime"]) ? $event["starttime"] : $old_event["starttime"];
            $edate = new DateTime("@$starttime");
            $edate->setTimezone($timezone);
            $new_date = date_parse($value);
            if ($key == "start_time") {
                $edate->setTime($new_date["hour"], $new_date["minute"]);
            } elseif ($key == "start_date") {
                $edate->setDate($new_date["year"], $new_date["month"], $new_date["day"]);
            }
            $event["starttime"] = $edate->getTimeStamp();
            break;
        case "end_date":
        case "end_time":
            if (! isset($params["timezone"])) {
                $app->response->status(400);
                return;
            }
            $timezone = new DateTimeZone($params["timezone"]);
            $endtime = isset($event["endtime"]) ? $event["endtime"] : $old_event["endtime"];
            $edate = new DateTime("@$endtime");
            $edate->setTimezone($timezone);
            $new_date = date_parse($value);
            if ($key == "end_time") {
                $edate->setTime($new_date["hour"], $new_date["minute"]);
            } elseif ($key == "end_date") {
                $edate->setDate($new_date["year"], $new_date["month"], $new_date["day"]);
            }
            $event["endtime"] = $edate->getTimeStamp();
            break;
        case "detect_date":
        case "detect_time":
            if (! isset($params["timezone"])) {
                $app->response->status(400);
                return;
            }
            $timezone = new DateTimeZone($params["timezone"]);
            $detecttime = isset($event["detecttime"]) ? $event["detecttime"] : $old_event["detecttime"];
            $edate = new DateTime("@$detecttime");
            $edate->setTimezone($timezone);
            $new_date = date_parse($value);
            if ($key == "detect_time") {
                $edate->setTime($new_date["hour"], $new_date["minute"]);
            } elseif ($key == "detect_date") {
                $edate->setDate($new_date["year"], $new_date["month"], $new_date["day"]);
            }
            $event["detecttime"] = $edate->getTimeStamp();
            break;
        case "status_datetime":
            if (!$value) {
                $event["statustime"] = 0;
                break;
            }

            if (! isset($params["timezone"])) {
                $app->response->status(400);
                return;
            }
            $timezone = new DateTimeZone($params["timezone"]);
            $statustime = $old_event["statustime"];
            $edate = new DateTime("@$statustime");
            $edate->setTimezone($timezone);
            $new_date = date_parse($value);
            $edate->setTime($new_date["hour"], $new_date["minute"]);
            $edate->setDate($new_date["year"], $new_date["month"], $new_date["day"]);
            $event["statustime"] = $edate->getTimeStamp();
            break;
        case "severity":
            $event["severity"] = $value;
            break;
        case "contact":
            $event["contact"] = $value;
            break;
        case "facilitator":
            $event["facilitator"] = $value;
            break;
        case "gcal":
            $event["gcal"] = $value;
            break;
        case "problem_type":
            $event["problem_type"] = $value;
            break;
        case "impact_type":
            $event["impact_type"] = $value;
            break;
        case "incident_cause":
            $event["incident_cause"] = $value;
            break;
        case "subsystem":
            $event["subsystem"] = $value;
            break;
        case "owner_team":
            $event["owner_team"] = $value;
            break;
        }
    }
    $event = Postmortem::save_event($event);
    if (is_null($event["id"])) {
        var_dump($event);
        $app->response->status(500);
        return;
    }

    $app->redirect('/events/'.$event["id"], 201);
});

$app->post('/events/:id/history', function($id) use ($app) {
    header("Content-Type: application/json");
    $action = $app->request->post('action');
    
    $event = array(
        "id" => $id,
        "summary" => $app->request->post('summary'),
        "why_surprised" => $app->request->post('why_surprised')
        );

    // store history
    $env = $app->environment;
    $admin = $env['admin']['username'];
    $result = Postmortem::add_history($event, $admin, $action);
    
    echo json_encode($result);
});

$app->post('/events/:id/tags', function($id) use ($app) {
    header("Content-Type: application/json");
    $tags = $app->request->post('tags');
    $tags = explode(",", $tags);
    $tags = array_map('trim', $tags);
    $tags = array_map('strtolower', $tags);
    $res = Postmortem::save_tags_for_event($id, $tags);
    if ($res["status"] == Postmortem::ERROR) {
        $app->response->status(400);
    } else {
        $app->response->status(201);
        $tags = Postmortem::get_tags_for_event($id);
        if ($tags["status"] == Postmortem::ERROR) {
            $app->response->status(404);
            return;
        } else {
            $output = json_encode($tags["values"]);
            echo str_replace("\\/", "/", $output);
        }
    }
});
$app->delete('/events/:event_id/tags/:tag_id', function($event_id, $tag_id) use ($app) {
    header("Content-Type: application/json");
    $res = Postmortem::delete_tag($tag_id, $event_id);
    if ($res["status"] == Postmortem::ERROR) {
        $app->response->status(500);
        echo json_encode($res["error"]);
    } else {
        $app->response->status(204);
    }

});


$app->get('/ping', function () use ($app) {
    header("Content-Type: application/json");
    echo json_encode(array('status' => 'ok'));
});


// Handle custom static assets.
// Javascript first then CSS.
$app->get('/features/:feature/js/:path' , function ($feature, $path) use ($app) {
        // read the file if it exists. Then serve it back.
        $file = stream_resolve_include_path("features/{$feature}/assets/js/{$path}");
        if (!$file) {
            $app->response->status(404);
            $app->log->error("couldn't file custom js asset at $path");
            return;
        }
        $thru_file = file_get_contents($file);
        $app->response->header("Content-Type", "application/javascript");
        print $thru_file;
        return;
});

$app->get('/features/:feature/css/:path' , function ($feature, $path) use ($app) {
        // read the file if it exists. Then serve it back.
        $file = stream_resolve_include_path("features/{$feature}/assets/css/{$path}");
        if (!$file) {
            $app->response->status(404);
            $app->log->error("couldn't file custom css asset at $path");
            return;
        }
        $thru_file = file_get_contents($file);
        $app->response->header("Content-Type", "text/css");
        print $thru_file;
        return;
});

$app->run();