Updated to the new People model

This commit is contained in:
Cliff Ingham 2014-08-18 16:17:49 -04:00
parent baad9f58c1
commit c278305473
26 changed files with 1111 additions and 230 deletions

View File

@ -0,0 +1,15 @@
<?php
/**
* @copyright 2012-2013 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
*/
namespace Application\Controllers;
use Blossom\Classes\Controller;
class IndexController extends Controller
{
public function index()
{
}
}

View File

@ -0,0 +1,88 @@
<?php
/**
* @copyright 2012-2013 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
*/
namespace Application\Controllers;
use Blossom\Classes\Controller;
use Blossom\Classes\Template;
use Blossom\Classes\Block;
use Application\Models\Person;
class LoginController extends Controller
{
private $return_url;
public function __construct(Template $template)
{
parent::__construct($template);
$this->return_url = !empty($_REQUEST['return_url']) ? $_REQUEST['return_url'] : BASE_URL;
}
/**
* Attempts to authenticate users via CAS
*/
public function index()
{
// If they don't have CAS configured, send them onto the application's
// internal authentication system
if (!defined('CAS')) {
header('Location: '.BASE_URL.'/login/login?return_url='.$this->return_url);
exit();
}
require_once CAS.'/CAS.php';
\phpCAS::client(CAS_VERSION_2_0, CAS_SERVER, 443, CAS_URI, false);
\phpCAS::setNoCasServerValidation();
\phpCAS::forceAuthentication();
// at this step, the user has been authenticated by the CAS server
// and the user's login name can be read with phpCAS::getUser().
// They may be authenticated according to CAS,
// but that doesn't mean they have person record
// and even if they have a person record, they may not
// have a user account for that person record.
try {
$_SESSION['USER'] = new Person(\phpCAS::getUser());
header("Location: {$this->return_url}");
exit();
}
catch (\Exception $e) {
$_SESSION['errorMessages'][] = $e;
}
$this->template->blocks[] = new Block('loginForm.inc',array('return_url'=>$this->return_url));
}
/**
* Attempts to authenticate users based on AuthenticationMethod
*/
public function login()
{
if (isset($_POST['username'])) {
try {
$person = new Person($_POST['username']);
if ($person->authenticate($_POST['password'])) {
$_SESSION['USER'] = $person;
header('Location: '.$this->return_url);
exit();
}
else {
throw new \Exception('invalidLogin');
}
}
catch (\Exception $e) {
$_SESSION['errorMessages'][] = $e;
}
}
$this->template->blocks[] = new Block('loginForm.inc',array('return_url'=>$this->return_url));
}
public function logout()
{
session_destroy();
header('Location: '.$this->return_url);
exit();
}
}

View File

@ -0,0 +1,70 @@
<?php
/**
* @copyright 2012-2014 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
*/
namespace Application\Controllers;
use Application\Models\Person;
use Application\Models\PeopleTable;
use Blossom\Classes\Controller;
use Blossom\Classes\Block;
class PeopleController extends Controller
{
public function index()
{
$table = new PeopleTable();
$people = $table->find(null, null, true);
$page = !empty($_GET['page']) ? (int)$_GET['page'] : 1;
$people->setCurrentPageNumber($page);
$people->setItemCountPerPage(20);
$this->template->blocks[] = new Block('people/list.inc', ['people' =>$people]);
$this->template->blocks[] = new Block('pageNavigation.inc', ['paginator'=>$people]);
}
public function view()
{
try {
$person = new Person($_REQUEST['person_id']);
$this->template->blocks[] = new Block('people/info.inc',array('person'=>$person));
}
catch (\Exception $e) {
$_SESSION['errorMessages'][] = $e;
}
}
public function update()
{
if (isset($_REQUEST['person_id']) && $_REQUEST['person_id']) {
try {
$person = new Person($_REQUEST['person_id']);
}
catch (\Exception $e) {
$_SESSION['errorMessages'][] = $e;
header('Location: '.BASE_URL.'/people');
exit();
}
}
else {
$person = new Person();
}
if (isset($_POST['firstname'])) {
$person->handleUpdate($_POST);
try {
$person->save();
header('Location: '.BASE_URL.'/people');
exit();
}
catch (\Exception $e) {
$_SESSION['errorMessages'][] = $e;
}
}
$this->template->blocks[] = new Block('people/updateForm.inc',array('person'=>$person));
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* @copyright 2012-2013 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
*/
namespace Application\Controllers;
use Application\Models\Person;
use Application\Models\PeopleTable;
use Blossom\Classes\Controller;
use Blossom\Classes\Block;
use Blossom\Classes\Database;
use Zend\Paginator\Paginator;
use Zend\Paginator\Adapter\DbSelect;
class UsersController extends Controller
{
public function index()
{
$people = new PeopleTable();
$users = $people->find(['user_account'=>true], null, true);
$page = !empty($_GET['page']) ? (int)$_GET['page'] : 1;
$users->setCurrentPageNumber($page);
$users->setItemCountPerPage(20);
$this->template->blocks[] = new Block('users/list.inc',array('users'=>$users));
$this->template->blocks[] = new Block('pageNavigation.inc', ['paginator'=>$users]);
}
public function update()
{
$person = isset($_REQUEST['user_id']) ? new Person($_REQUEST['user_id']) : new Person();
if (isset($_POST['username'])) {
try {
$person->handleUpdateUserAccount($_POST);
$person->save();
header('Location: '.BASE_URL.'/users');
exit();
}
catch (\Exception $e) {
$_SESSION['errorMessages'][] = $e;
}
}
if ($person->getId()) {
$this->template->blocks[] = new Block('people/info.inc',array('person'=>$person));
}
$this->template->blocks[] = new Block('users/updateForm.inc',array('user'=>$person));
}
public function delete()
{
try {
$person = new Person($_REQUEST['user_id']);
$person->deleteUserAccount();
$person->save();
}
catch (\Exception $e) {
$_SESSION['errorMessages'][] = $e;
}
header('Location: '.BASE_URL.'/users');
exit();
}
}

44
Models/PeopleTable.php Normal file
View File

@ -0,0 +1,44 @@
<?php
/**
* @copyright 2013 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
*/
namespace Application\Models;
use Blossom\Classes\TableGateway;
use Zend\Db\Sql\Select;
class PeopleTable extends TableGateway
{
public function __construct() { parent::__construct('people', __namespace__.'\Person'); }
/**
* @param array $fields
* @param string|array $order Multi-column sort should be given as an array
* @param bool $paginated Whether to return a paginator or a raw resultSet
* @param int $limit
*/
public function find($fields=null, $order='lastname', $paginated=false, $limit=null)
{
$select = new Select('people');
if (count($fields)) {
foreach ($fields as $key=>$value) {
switch ($key) {
case 'user_account':
if ($value) {
$select->where('username is not null');
}
else {
$select->where('username is null');
}
break;
default:
$select->where([$key=>$value]);
}
}
}
return parent::performSelect($select, $order, $paginated, $limit);
}
}

258
Models/Person.php Normal file
View File

@ -0,0 +1,258 @@
<?php
/**
* @copyright 2009-2013 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
*/
namespace Application\Models;
use Blossom\Classes\ActiveRecord;
use Blossom\Classes\Database;
use Blossom\Classes\ExternalIdentity;
class Person extends ActiveRecord
{
protected $tablename = 'people';
/**
* Populates the object with data
*
* Passing in an associative array of data will populate this object without
* hitting the database.
*
* Passing in a scalar will load the data from the database.
* This will load all fields in the table as properties of this class.
* You may want to replace this with, or add your own extra, custom loading
*
* @param int|string|array $id (ID, email, username)
*/
public function __construct($id=null)
{
if ($id) {
if (is_array($id)) {
$this->exchangeArray($id);
}
else {
$zend_db = Database::getConnection();
if (ActiveRecord::isId($id)) {
$sql = 'select * from people where id=?';
}
elseif (false !== strpos($id,'@')) {
$sql = 'select * from people where email=?';
}
else {
$sql = 'select * from people where username=?';
}
$result = $zend_db->createStatement($sql)->execute([$id]);
if (count($result)) {
$this->exchangeArray($result->current());
}
else {
throw new \Exception('people/unknownPerson');
}
}
}
else {
// This is where the code goes to generate a new, empty instance.
// Set any default values for properties that need it here
$this->setAuthenticationMethod('local');
}
}
/**
* Throws an exception if anything's wrong
* @throws Exception $e
*/
public function validate()
{
// Check for required fields here. Throw an exception if anything is missing.
if (!$this->getFirstname() || !$this->getEmail()) {
throw new \Exception('missingRequiredFields');
}
if ($this->getUsername() && !$this->getAuthenticationMethod()) {
$this->setAuthenticationMethod('local');
}
}
public function save()
{
parent::save();
}
/**
* Removes all the user account related fields from this Person
*/
public function deleteUserAccount()
{
$userAccountFields = array(
'username', 'password', 'authenticationMethod', 'role'
);
foreach ($userAccountFields as $f) {
$this->data[$f] = null;
}
}
//----------------------------------------------------------------
// Generic Getters & Setters
//----------------------------------------------------------------
public function getId() { return parent::get('id'); }
public function getFirstname() { return parent::get('firstname'); }
public function getLastname() { return parent::get('lastname'); }
public function getEmail() { return parent::get('email'); }
public function setFirstname ($s) { parent::set('firstname', $s); }
public function setLastname ($s) { parent::set('lastname', $s); }
public function setEmail ($s) { parent::set('email', $s); }
public function getUsername() { return parent::get('username'); }
public function getPassword() { return parent::get('password'); } # Encrypted
public function getRole() { return parent::get('role'); }
public function getAuthenticationMethod() { return parent::get('authenticationMethod'); }
public function setUsername ($s) { parent::set('username', $s); }
public function setRole ($s) { parent::set('role', $s); }
public function setAuthenticationMethod($s) { parent::set('authenticationMethod', $s); }
public function setPassword($s)
{
$s = trim($s);
if ($s) { $this->data['password'] = sha1($s); }
else { $this->data['password'] = null; }
}
/**
* @param array $post
*/
public function handleUpdate($post)
{
$fields = array( 'firstname', 'middlename', 'lastname', 'email' );
foreach ($fields as $field) {
if (isset($post[$field])) {
$set = 'set'.ucfirst($field);
$this->$set($post[$field]);
}
}
}
/**
* @param array $post
*/
public function handleUpdateUserAccount($post)
{
$fields = array(
'firstname','lastname','email',
'username','authenticationMethod','role'
);
foreach ($fields as $f) {
if (isset($post[$f])) {
$set = 'set'.ucfirst($f);
$this->$set($post[$f]);
}
if (!empty($post['password'])) {
$this->setPassword($post['password']);
}
}
$method = $this->getAuthenticationMethod();
if ($this->getUsername() && $method && $method != 'local') {
$class = "Blossom\\Classes\\$method";
$identity = new $class($this->getUsername());
$this->populateFromExternalIdentity($identity);
}
}
//----------------------------------------------------------------
// User Authentication
//----------------------------------------------------------------
/**
* Should provide the list of methods supported
*
* There should always be at least one method, called "local"
* Additional methods must match classes that implement External Identities
* See: ExternalIdentity.php
*
* @return array
*/
public static function getAuthenticationMethods()
{
global $DIRECTORY_CONFIG;
return array_merge(array('local'), array_keys($DIRECTORY_CONFIG));
}
/**
* Determines which authentication scheme to use for the user and calls the appropriate method
*
* Local users will get authenticated against the database
* Other authenticationMethods will need to write a class implementing ExternalIdentity
* See: /libraries/framework/classes/ExternalIdentity.php
*
* @param string $password
* @return boolean
*/
public function authenticate($password)
{
if ($this->getUsername()) {
switch($this->getAuthenticationMethod()) {
case "local":
return $this->getPassword()==sha1($password);
break;
default:
$method = $this->getAuthenticationMethod();
$class = "Blossom\\Classes\\$method";
return $class::authenticate($this->getUsername(),$password);
}
}
}
/**
* Checks if the user is supposed to have acces to the resource
*
* This is implemented by checking against a Zend_Acl object
* The Zend_Acl should be created in configuration.inc
*
* @param string $resource
* @param string $action
* @return boolean
*/
public static function isAllowed($resource, $action=null)
{
global $ZEND_ACL;
$role = 'Anonymous';
if (isset($_SESSION['USER']) && $_SESSION['USER']->getRole()) {
$role = $_SESSION['USER']->getRole();
}
return $ZEND_ACL->isAllowed($role, $resource, $action);
}
//----------------------------------------------------------------
// Custom Functions
//----------------------------------------------------------------
public function getUrl() { return BASE_URL.'/people/view?person_id='.$this->getId(); }
public function getUri() { return BASE_URI.'/people/view?person_id='.$this->getId(); }
/**
* @return string
*/
public function getFullname()
{
return "{$this->getFirstname()} {$this->getLastname()}";
}
/**
* @param ExternalIdentity $identity An object implementing ExternalIdentity
*/
public function populateFromExternalIdentity(ExternalIdentity $identity)
{
if (!$this->getFirstname() && $identity->getFirstname()) {
$this->setFirstname($identity->getFirstname());
}
if (!$this->getLastname() && $identity->getLastname()) {
$this->setLastname($identity->getLastname());
}
if (!$this->getEmail() && $identity->getEmail()) {
$this->setEmail($identity->getEmail());
}
}
}

View File

@ -4,31 +4,44 @@
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
*/
$ZEND_ACL = new Zend_Acl();
/**
* Load the roles from the database
*/
$roles = new RoleList();
$roles->find();
foreach ($roles as $role) {
$ZEND_ACL = $ZEND_ACL->addRole(new Zend_Acl_Role($role->getName()));
}
use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Role\GenericRole as Role;
use Zend\Permissions\Acl\Resource\GenericResource as Resource;
$ZEND_ACL = new Acl();
$ZEND_ACL->addRole(new Role('Anonymous'))
->addRole(new Role('Public'), 'Anonymous')
->addRole(new Role('Staff'), 'Public')
->addRole(new Role('Administrator'), 'Staff');
/**
* Declare all the resources
*/
$ZEND_ACL->add(new Zend_Acl_Resource('Users'));
$ZEND_ACL->add(new Zend_Acl_Resource('Deeds'));
$ZEND_ACL->add(new Zend_Acl_Resource('Interments'));
$ZEND_ACL->add(new Zend_Acl_Resource('Cemeteries'));
$ZEND_ACL->add(new Zend_Acl_Resource('Sections'));
$ZEND_ACL->addResource(new Resource('index'));
$ZEND_ACL->addResource(new Resource('people'));
$ZEND_ACL->addResource(new Resource('users'));
$ZEND_ACL->addResource(new Resource('login'));
$ZEND_ACL->addResource(new Resource('deeds'));
$ZEND_ACL->addResource(new Resource('interments'));
$ZEND_ACL->addResource(new Resource('cemeteries'));
$ZEND_ACL->addResource(new Resource('sections'));
/**
* Assign permissions to the resources
*/
$ZEND_ACL->allow(null,'login');
// Permissions for unauthenticated browsing
$ZEND_ACL->allow(null,
array('index'),
array('index'));
// Allow Staff to do stuff
$ZEND_ACL->allow('Staff',['deeds', 'interments']);
$ZEND_ACL->allow('Staff',
['people', 'cemeteries', 'sections'],
['index','view']);
// Administrator is allowed access to everything
$ZEND_ACL->allow('Administrator');
$ZEND_ACL->allow('Editor','Deeds');
$ZEND_ACL->allow('Editor','Interments');

View File

@ -1,35 +0,0 @@
<?php
/**
* @copyright 2009 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
*/
?>
<h2>Add Person</h2>
<form method="post" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>">
<fieldset><legend>Personal Info</legend>
<table>
<tr><td><label for="person-firstname" class="required">Firstname</label></td>
<td><input name="person[firstname]" id="person-firstname" />
</td>
</tr>
<tr><td><label for="person-lastname" class="required">Lastname</label></td>
<td><input name="person[lastname]" id="person-lastname" />
</td>
</tr>
<tr><td><label for="person-email">Email</label></td>
<td><input name="person[email]" id="person-email" />
</td>
</tr>
</table>
<button type="submit" class="submit">Submit</button>
<button type="button" class="cancel" onclick="document.location.href='<?php echo BASE_URL; ?>/people';">
Cancel
</button>
</fieldset>
</form>

View File

@ -0,0 +1,34 @@
<?php
/**
* @copyright 2009-2014 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
* @param Person $this->person
*/
use Application\Models\Person;
use Blossom\Classes\View;
$username = $this->person->getUsername();
if (!$username && Person::isAllowed('users')) {
$h = $this->template->getHelper('buttonLink');
$username = $h->buttonLink(
BASE_URI.'/users/update?user_id='.$this->person->getId(),
$this->translate('create_account'),
'add'
);
}
$name = View::escape($this->person->getFullname());
echo <<<EOT
<div class="personInfo">
<h2>$name</h2>
<table>
<tr><th>Email</th>
<td>{$this->person->getEmail()}</td>
</tr>
<tr><th>Username</th>
<td>$username</td>
</tr>
</table>
</div>
EOT;

View File

@ -0,0 +1,58 @@
<?php
/**
* @copyright 2009-2014 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
* @param Zend\Db\ResultSet $people
*/
use Application\Models\Person;
use Blossom\Classes\View;
?>
<div class="interfaceBox">
<h2><?php
echo $this->translate(['person','people',2]);
$helper = $this->template->getHelper('buttonLink');
if (Person::isAllowed('people')) {
echo $helper->buttonLink(
BASE_URI.'/people/update',
$this->translate('add_person'),
'add'
);
}
?>
</h2>
<table>
<thead>
<tr><th></th>
<th><?php echo $this->_('username'); ?></th>
<th><?php echo $this->_('name'); ?></th>
<th><?php echo $this->_(['email','emails',1]); ?></th>
</tr>
</thead>
<tbody>
<?php
foreach ($this->people as $person) {
$editButton = '';
if (Person::isAllowed('people')) {
$editButton = $helper->buttonLink(
BASE_URI.'/people/update?person_id='.$person->getId(),
$this->translate('edit'),
'edit'
);
}
$name = View::escape($person->getFullname());
echo "
<tr><td>$editButton</td>
<td>{$person->getUsername()}</td>
<td><a href=\"{$person->getURL()}\">$name</a></td>
<td>{$person->getEmail()}</td>
</tr>
";
}
?>
</tbody>
</table>
</div>

View File

@ -1,29 +0,0 @@
<?php
/**
* @copyright 2009 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
* @param Person $this->person
*/
$username = $this->person->getUsername();
if (!$username && userIsAllowed('Users')) {
$url = BASE_URL.'/users/addUser.php?person_id='.$this->person->getId();
$username = "
<button type=\"button\" onclick=\"document.location.href='$url';\">
Create Account
</button>";
}
$name = View::escape($this->person->getFullname());
echo "
<h2>$name</h2>
<table>
<tr><th>Username</th>
<td>$username</td>
</tr>
<tr><th>Email</th>
<td>{$this->person->getEmail()}</td>
</tr>
</table>
";

View File

@ -1,46 +0,0 @@
<?php
/**
* @copyright 2009 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
* @param PersonList $this->personList
*/
?>
<div class="interfaceBox">
<h2><?php
if (userIsAllowed('Users')) {
echo "
<button type=\"button\" class=\"add\" onclick=\"document.location.href='".BASE_URL."/people/addPerson.php';\">
Add A Person
</button>
";
}
?>
People
</h2>
<table>
<?php
foreach ($this->personList as $person) {
$editButton = '';
if (userIsAllowed('Users')) {
$editButton = "
<button type=\"button\" class=\"edit\" onclick=\"document.location.href='".BASE_URL."/people/updatePerson.php?person_id={$person->getId()}';\">
Edit
</button>
";
}
$name = View::escape($person->getFullname());
echo "
<tr><td>$editButton</td>
<td>{$person->getUsername()}</td>
<td><a href=\"{$person->getURL()}\">$name</a></td>
<td>{$person->getEmail()}</td>
</tr>
";
}
?>
</table>
</div>

View File

@ -0,0 +1,42 @@
<?php
/**
* @copyright 2009-2014 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
* @param Person $this->person
*/
use Blossom\Classes\View;
$fields = array('firstname','lastname','email');
foreach ($fields as $field) {
$get = 'get'.ucfirst($field);
$$field = View::escape($this->person->$get());
}
$title = $this->person->getId() ? View::escape('Edit '.$this->person->getFirstname()) : 'Add Someone';
?>
<div class="updatePersonForm">
<h2><?php echo $title; ?></h2>
<form method="post" action="<?php echo BASE_URI; ?>/people/update">
<fieldset><legend><?php echo $this->_('info_person'); ?></legend>
<input name="person_id" type="hidden" value="<?php echo $this->person->getId(); ?>" />
<table>
<tr><td><label for="firstname" class="required"><?php echo $this->_('firstname'); ?></label></td>
<td><input name="firstname" id="firstname" value="<?php echo $firstname; ?>" /></td>
</tr>
<tr><td><label for="lastname" class="required"><?php echo $this->_('lastname'); ?></label></td>
<td><input name="lastname" id="lastname" value="<?php echo $lastname; ?>" /></td>
</tr>
<tr><td><label for="email"><?php echo $this->_(['labels.email', 'emails', 1]); ?></label></td>
<td><input name="email" id="email" value="<?php echo $email; ?>" /></td>
</tr>
</table>
<?php
$h = $this->template->getHelper('saveAndCancelButtons');
echo $h->saveAndCancelButtons(BASE_URI.'/people');
?>
</fieldset>
</form>
</div>

View File

@ -1,37 +0,0 @@
<?php
/**
* @copyright 2009 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
* @param Person $this->person
*/
?>
<h2>Update Person</h2>
<form method="post" action="<?php echo $_SERVER['SCRIPT_NAME']; ?>">
<fieldset><legend>Person Info</legend>
<input name="person_id" type="hidden" value="<?php echo $this->person->getId(); ?>" />
<table>
<tr><td><label for="person-firstname" class="required">Firstname</label></td>
<td><input name="person[firstname]" id="person-firstname" value="<?php echo View::escape($this->person->getFirstname()); ?>" />
</td>
</tr>
<tr><td><label for="person-lastname" class="required">Lastname</label></td>
<td><input name="person[lastname]" id="person-lastname" value="<?php echo View::escape($this->person->getLastname()); ?>" />
</td>
</tr>
<tr><td><label for="person-email">Email</label></td>
<td><input name="person[email]" id="person-email" value="<?php echo View::escape($this->person->getEmail()); ?>" />
</td>
</tr>
</table>
<button type="submit" class="submit">Submit</button>
<button type="button" class="cancel" onclick="document.location.href='<?php echo BASE_URL; ?>/people';">
Cancel
</button>
</fieldset>
</form>

BIN
language/en_US.mo Normal file

Binary file not shown.

126
language/en_US.po Normal file
View File

@ -0,0 +1,126 @@
# Translation
# Copyright (C) 2013 City of Bloomington
# This file is distributed under the same license as the Blossom package.
#
# Cliff Ingham, 2013.
msgid ""
msgstr ""
"Project-Id-Version: uReport 1.9\n"
"Report-Msgid-Bugs-To: dev@bloomington.in.gov\n"
"POT-Creation-Date: 2013-09-20 16:33-0400\n"
"PO-Revision-Date: 2014-08-11 13:43-0500\n"
"Last-Translator: Cliff Ingham <inghamn@bloomington.in.gov>\n"
"Language-Team: City of Bloomington <dev@bloomington.in.gov>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.5.4\n"
"Language: en_US\n"
"X-Poedit-SourceCharset: UTF-8\n"
msgid "home"
msgstr "Home"
msgid "add"
msgstr "Add"
msgid "edit"
msgstr "Edit"
msgid "delete"
msgstr "Delete"
msgid "submit"
msgstr "Submit"
msgid "save"
msgstr "Save"
msgid "cancel"
msgstr "Cancel"
#. This is the short form for "Administrator". This should be an abbreviation
msgid "admin"
msgstr "Admin"
msgid "login"
msgstr "Login"
msgid "logout"
msgstr "Logout"
msgid "messages.signed_in_as"
msgstr "Signed in as %s"
msgid "help"
msgstr "Help"
msgid "name"
msgstr "Name"
msgid "firstname"
msgstr "First Name"
msgid "middlename"
msgstr "Middle Name"
msgid "lastname"
msgstr "Last Name"
msgid "username"
msgstr "Username"
msgid "password"
msgstr "Password"
msgid "authenticationMethod"
msgstr "Authentication"
msgid "role"
msgstr "Role"
msgid "email"
msgid_plural "emails"
msgstr[0] "Email"
msgstr[1] "Email Addresses"
msgid "person"
msgid_plural "people"
msgstr[0] "Person"
msgstr[1] "People"
msgid "user"
msgid_plural "users"
msgstr[0] "User Account"
msgstr[1] "User Accounts"
msgid "info_person"
msgstr "Person Info"
msgid "add_person"
msgstr "Add a Person"
msgid "create_account"
msgstr "Create Account"
msgid "edit_account"
msgstr "Edit Account"
msgid "delete_account"
msgstr "Delete Account"
msgid "interment"
msgid_plural "interments"
msgstr[0] "Interment"
msgstr[1] "Interments"
msgid "cemetery"
msgid_plural "cemeteries"
msgstr[0] "Cemetery"
msgstr[1] "Cemeteries"
msgid "deed"
msgid_plural "deeds"
msgstr[0] "Deed"
msgstr[1] "Deeds"

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,6 @@
#panel-container { overflow:auto; }
#panel-one { float:left; width:180px; }
#panel-two { float:right; width:180px; }
#content-panel { margin-left: 190px; margin-right: 190px; }
#footer { clear:both; }

View File

@ -0,0 +1,6 @@
#panel-container { width:100%; overflow:auto; padding-left:10px; }
#panel-one { width:490px; float:right; padding-left:10px; }
#content-panel { margin-right:495px; border-right:1px solid #999999; width:470px; }
#panel-two { clear:right; width:100%; }
#footer { clear:both; }

View File

@ -0,0 +1,50 @@
/*
CSS Reset technique copied from Eric Meyer
http://meyerweb.com/eric/thoughts/2007/05/01/reset-reloaded/
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
margin: 0;
padding: 0;
border: 0;
outline: 0;
font-weight: inherit;
font-style: inherit;
font-size: 100%;
font-family: inherit;
vertical-align: baseline;
}
/* Links should not be styled to start out with */
a { text-decoration:none; }
/* remember to define focus styles! */
:focus {
outline: 0;
}
body {
line-height: 1;
color: black;
background: white;
}
ol, ul {
}
/* tables still need 'cellspacing="0"' in the markup */
table {
border-collapse: separate;
border-spacing: 0;
}
caption, th, td {
text-align: left;
font-weight: normal;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: "";
}
blockquote, q {
quotes: "" "";
}

101
public/css/local/screen.css Normal file
View File

@ -0,0 +1,101 @@
/**
* Basic HTML element styles
*/
body { font-family:sans-serif; }
h2 { font-size:131%; font-weight:bold; margin:.5em 0; color:#036; }
h3 { font-size:123.1%; font-weight:bold; margin:.5em 0 .25em 0; }
h4 { font-size:100%; font-weight:bold; }
a { text-decoration:none; color:#00f; }
p { margin:0 .5em .5em .5em; }
table { margin:0 .5em .5em .5em; }
th { color:#333; font-weight:bold; font-size:90%; padding:0.25em; }
td { padding:.25em; }
thead th { text-align:left; }
tbody th { text-align:right; }
ul { list-style-type:none; margin-left:1em; padding:0; }
li { margin:0; }
fieldset { padding:.25em 0; }
legend { font-weight:bold; }
label { font-weight:bold; }
/**
* Base styles for buttons and button links
*/
button, .button {
border:1px solid black; border-radius:6px;
display:inline-block;
padding:0 10px;
font-weight:bold; color:black;
background: linear-gradient(to bottom, #f5f6f6 0%,#dbdce2 21%,#b8bac6 49%,#dddfe3 80%,#f5f6f6 100%);
}
/**
* Banner
*/
header { color:#fff; background-color:#036; margin:0; }
header a { color:#fff; font-weight:bold; }
header h1 { margin:0 4px; font-size:174%; }
header div { margin:4px; }
header nav {
margin-top:5px; padding:5px;
background-color:#fff;
border-top:2px solid #000; border-bottom:2px solid #000;
}
header nav a { font-weight:bold; color:black; margin-right:1em; }
#utilityBar { float:right; width:50%; text-align:right; }
#utilityBar li { display:inline; margin-right:1em; color:white; }
/**
* Side Panel
*/
#panel-one nav {
border:2px solid #000; margin-bottom:0.5em;
border-radius:10px;
overflow:hidden;
}
#panel-one h2 {
background-color:#36c; color:#fff;
border-bottom:2px solid #000;
margin:0; padding:.25em;
}
#panel-one ul { margin:.25em; padding:0; }
#panel-one a { font-weight:bold; color:#000; }
/**
* Main Content
*/
#errorMessages { background-color:#fcc; padding:4px; border-radius:10px; }
#errorMessages h2 { }
#burialLinks td { vertical-align:middle; }
#aboutCemeteries { float:left; width:205px; }
#aboutCemeteries ul {padding:0;}
#cemeteryImages { float:right; width:200px; }
.pageNavigation ul { list-style-type:none; }
.pageNavigation li { display:inline; margin-right:1em; }
.pageNavigation .current { border:2px solid green; }
.searchInterment th { text-align:left; width:75px; }
table { margin:5px 0; overflow:auto; }
tr { }
td label { font-weight:normal; color:#666666; }
td { padding:4px 4px 2px 4px; vertical-align:top; border:0; }
thead th {
font-weight:normal; color:#666666; text-align:left;
vertical-align:bottom; height:8pt; padding:4px 4px 2px 2px;
}
tbody th {
font-weight:normal; color:#666666; text-align:right;
height:8pt; padding:4px 4px 2px 2px;
}
th a:link,th a:visited { color:#3366cc; background-color:inherit; }
form table { border:none; }
a.sorting { }

40
public/index.php Normal file
View File

@ -0,0 +1,40 @@
<?php
/**
* @copyright 2012-2013 City of Bloomington, Indiana
* @license http://www.gnu.org/licenses/agpl.txt GNU/AGPL, see LICENSE.txt
* @author Cliff Ingham <inghamn@bloomington.in.gov>
*/
include '../configuration.inc';
use Blossom\Classes\Template;
use Blossom\Classes\Block;
// Check for routes
if (preg_match('|'.BASE_URI.'(/([a-zA-Z0-9]+))?(/([a-zA-Z0-9]+))?|',$_SERVER['REQUEST_URI'],$matches)) {
$resource = isset($matches[2]) ? $matches[2] : 'index';
$action = isset($matches[4]) ? $matches[4] : 'index';
}
// Create the default Template
$template = !empty($_REQUEST['format'])
? new Template('default',$_REQUEST['format'])
: new Template('default');
// Execute the Controller::action()
if (isset($resource) && isset($action) && $ZEND_ACL->hasResource($resource)) {
$USER_ROLE = isset($_SESSION['USER']) ? $_SESSION['USER']->getRole() : 'Anonymous';
if ($ZEND_ACL->isAllowed($USER_ROLE, $resource, $action)) {
$controller = 'Application\Controllers\\'.ucfirst($resource).'Controller';
$c = new $controller($template);
$c->$action();
}
else {
header('HTTP/1.1 403 Forbidden', true, 403);
$_SESSION['errorMessages'][] = new \Exception('noAccessAllowed');
}
}
else {
header('HTTP/1.1 404 Not Found', true, 404);
$template->blocks[] = new Block('404.inc');
}
echo $template->render();

View File

@ -1,18 +1,32 @@
<div id="banner">
<div id="application_name">
<a href="<?php echo BASE_URL; ?>/">Rose Hill &amp; White Oak Cemeteries</a>
</div>
<?php
use Blossom\Classes\View;
?>
<header>
<ul id="utilityBar">
<?php
$li = '<li><a href="%s">%s</a></li>';
if (isset($_SESSION['USER'])) {
echo sprintf($li, BASE_URI.'/login/logout', $this->translate('labels.logout'));
$name = View::escape($_SESSION['USER']->getFullname());
echo sprintf('<li>'.sprintf($this->translate('messages.signed_in_as'), $name).'</li>');
}
else {
$return_url = View::escape($_SERVER['REQUEST_URI']);
echo sprintf($li, BASE_URI.'/login?return_url='.$return_url, $this->translate('labels.login'));
}
?>
</ul>
<h1>
<a href="<?php echo BASE_URI; ?>">Rose Hill &amp; White Oak Cemeteries</a>
</h1>
<div id="location_name">
<a href="<?php echo BASE_URL; ?>">City of Bloomington, Mark Kruzan, Mayor</a>
<a href="<?php echo BASE_URI; ?>">City of Bloomington, Mark Kruzan, Mayor</a>
</div>
<div class="loginForm">
<?php
if (isset($_SESSION['USER'])) {
echo "<a href=\"".BASE_URL."/login/logout.php\">Logout</a>";
}
else {
echo "<a href=\"".BASE_URL."/login\">Login</a>";
}
?>
</div>
</div>
<?php
if (isset($_SESSION['USER'])) {
include __DIR__.'/menubar.inc';
}
?>
</header>

View File

@ -3,11 +3,8 @@
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<link rel="SHORTCUT ICON" href="/favicon.ico" />
<style type="text/css" media="screen">
@import url('<?php echo BASE_URL; ?>/skins/origin-external/reset.css');
@import url('<?php echo BASE_URL; ?>/skins/local/layouts/<?php echo $this->layout; ?>.css');
@import url('<?php echo BASE_URL; ?>/skins/origin-external/screen.css');
@import url('<?php echo BASE_URL; ?>/skins/local/screen.css');
</style>
<title>Cemeteries</title>
<link rel="stylesheet" type="text/css" href="<?php echo BASE_URL; ?>/css/local/reset.css" />
<link rel="stylesheet" type="text/css" href="<?php echo BASE_URL; ?>/css/local/layouts/<?php echo $this->layout; ?>.css" />
<link rel="stylesheet" type="text/css" href="<?php echo BASE_URL; ?>/css/local/screen.css" />
<title><?php echo isset($this->title) ? self::escape($this->title) : 'Cemeteries'; ?></title>
</head>

View File

@ -1,18 +1,21 @@
<div class="menuBar">
<ul><li><a href="<?php echo BASE_URL; ?>/interments">Interments</a></li>
<?php
if (userIsAllowed('Cemeteries')) {
echo "<li><a href=\"".BASE_URL."/cemeteries\">Cemeteries</a></li>";
}
if (userIsAllowed('Deeds')) {
echo "<li><a href=\"".BASE_URL."/deeds\">Deeds</a></li>";
}
if (userIsAllowed('Users')) {
echo "
<li><a href=\"".BASE_URL."/people\">People</a></li>
<li><a href=\"".BASE_URL."/users\">Users</a></li>
";
}
?>
</ul>
</div>
<?php
use Application\Models\Person;
?>
<nav>
<?php
$a = '<a href="%s">%s</a>';
$routes = [
'interment' => 'interments',
'cemetery' => 'cemeteries',
'deed' => 'deeds',
'person' => 'people',
'user' => 'users'
];
foreach ($routes as $singular=>$plural) {
if (Person::isAllowed($plural, 'index')) {
echo sprintf($a, BASE_URI.'/'.$plural, $this->_(["labels.$singular", "labels.$plural", 2]));
}
}
?>
</nav>

View File

@ -1,34 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<?php
$this->layout = 'two-column';
$dir = dirname(__FILE__);
include $dir.'/partials/header.inc';
$this->layout = 'two-column';
include __DIR__.'/partials/header.inc';
?>
<body>
<?php
include $dir.'/partials/banner.inc';
if (isset($_SESSION['USER'])) {
include $dir.'/partials/menubar.inc';
}
?>
<div id="panel-container">
<?php
include $dir.'/partials/panel-one.inc';
?>
<div id="content-panel">
<?php
include FRAMEWORK.'/errorMessages.php';
echo $this->includeBlocks();
?>
</div>
</div>
<?php include $dir.'/partials/footer.inc'; ?>
<?php
include __DIR__.'/partials/banner.inc';
?>
<div id="panel-container">
<?php
include __DIR__.'/partials/panel-one.inc';
?>
<div id="content-panel">
<?php
include BLOSSOM.'/errorMessages.php';
echo $this->includeBlocks();
?>
</div>
</div>
<?php
include __DIR__.'/partials/footer.inc';
?>
</body>
</html>
<?php
include $dir.'/partials/garbageCollection.inc';
include __DIR__.'/partials/garbageCollection.inc';
?>