Forráskód Böngészése

Database schema. Basic classes. Framework

Nathaniel van Diepen 8 éve
szülő
commit
c503375b96
12 módosított fájl, 538 hozzáadás és 32 törlés
  1. 3 2
      index.php
  2. 238 22
      install/db.sql
  3. 68 0
      lib/bugs.class.php
  4. 59 0
      lib/issue.class.php
  5. 58 0
      lib/project.class.php
  6. 4 1
      lib/router.class.php
  7. 31 0
      lib/template.class.php
  8. 34 6
      lib/user.class.php
  9. 13 0
      paths/issue.php
  10. 16 0
      paths/project.php
  11. 10 1
      paths/user.php
  12. 4 0
      templates/user.php

+ 3 - 2
index.php

@@ -42,13 +42,14 @@
 		$_DATA = array();
 	}
 	require_once('lib/router.class.php');
-	require_once('lib/sql.class.php');
+	require_once('lib/bugs.class.php');
 	foreach(glob("paths/*.php") as $filename){
 		require_once($filename);
 	}
+	Bugs::connect();
 	Router::base('/bugs/');
 	Router::handle(rtrim($_SERVER['REDIRECT_URL'],'/'),null,function($res,$url){
-		header('Content-Type: application/json');
+		$res->header('Content-Type','application/json');
 		$res->json(array(
 			'error'=> 'Not implemented',
 			'url'=> $url,

+ 238 - 22
install/db.sql

@@ -3,7 +3,7 @@
 -- http://www.phpmyadmin.net
 --
 -- Host: 127.0.0.1
--- Generation Time: Aug 12, 2015 at 01:15 AM
+-- Generation Time: Aug 12, 2015 at 10:25 PM
 -- Server version: 5.6.25
 -- PHP Version: 5.6.11
 
@@ -61,10 +61,30 @@ CREATE TABLE IF NOT EXISTS `activities` (
 
 -- --------------------------------------------------------
 
+--
+-- Table structure for table `emails`
+--
+-- Creation: Aug 12, 2015 at 07:34 PM
+--
+
+DROP TABLE IF EXISTS `emails`;
+CREATE TABLE IF NOT EXISTS `emails` (
+  `u_id` int(10) NOT NULL,
+  `subject` varchar(77) NOT NULL,
+  `body` text NOT NULL,
+  `date` date NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- RELATIONS FOR TABLE `emails`:
+--
+
+-- --------------------------------------------------------
+
 --
 -- Table structure for table `issues`
 --
--- Creation: Aug 11, 2015 at 10:52 PM
+-- Creation: Aug 12, 2015 at 07:42 PM
 --
 
 DROP TABLE IF EXISTS `issues`;
@@ -75,7 +95,9 @@ CREATE TABLE IF NOT EXISTS `issues` (
   `pr_id` int(10) NOT NULL,
   `s_id` int(10) NOT NULL,
   `name` varchar(50) NOT NULL,
-  `description` varchar(100) NOT NULL
+  `description` varchar(100) NOT NULL,
+  `date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `date_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
 
 --
@@ -98,6 +120,85 @@ CREATE TABLE IF NOT EXISTS `issues` (
 --       `statuses` -> `id`
 --
 
+--
+-- Triggers `issues`
+--
+DROP TRIGGER IF EXISTS `issue_update_date_modified`;
+DELIMITER $$
+CREATE TRIGGER `issue_update_date_modified` BEFORE UPDATE ON `issues`
+ FOR EACH ROW SET new.date_modified = NOW()
+$$
+DELIMITER ;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `issue_roles`
+--
+-- Creation: Aug 12, 2015 at 05:21 PM
+--
+
+DROP TABLE IF EXISTS `issue_roles`;
+CREATE TABLE IF NOT EXISTS `issue_roles` (
+  `id` int(10) NOT NULL,
+  `name` varchar(50) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- RELATIONS FOR TABLE `issue_roles`:
+--
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `messages`
+--
+-- Creation: Aug 12, 2015 at 07:45 PM
+--
+
+DROP TABLE IF EXISTS `messages`;
+CREATE TABLE IF NOT EXISTS `messages` (
+  `id` int(10) NOT NULL,
+  `u_id` int(10) NOT NULL,
+  `i_id` int(10) DEFAULT NULL,
+  `p_id` int(10) DEFAULT NULL,
+  `subject` varchar(50) NOT NULL,
+  `message` text NOT NULL,
+  `date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `date_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- RELATIONS FOR TABLE `messages`:
+--   `i_id`
+--       `issues` -> `id`
+--   `p_id`
+--       `projects` -> `id`
+--   `u_id`
+--       `users` -> `id`
+--   `u_id`
+--       `users` -> `id`
+--   `i_id`
+--       `issues` -> `id`
+--   `p_id`
+--       `projects` -> `id`
+--
+
+--
+-- Triggers `messages`
+--
+DROP TRIGGER IF EXISTS `message_update`;
+DELIMITER $$
+CREATE TRIGGER `message_update` BEFORE UPDATE ON `messages`
+ FOR EACH ROW IF new.i_id IS NOT NULL AND new.p_id IS NOT NULL THEN
+  SIGNAL SQLSTATE '45000'
+  SET MESSAGE_TEXT = 'Messages can only be related to one thing';
+ELSE
+  SET new.date_modified = NOW();
+END IF
+$$
+DELIMITER ;
+
 -- --------------------------------------------------------
 
 --
@@ -131,7 +232,7 @@ INSERT INTO `priorities` (`id`, `name`) VALUES
 --
 -- Table structure for table `projects`
 --
--- Creation: Aug 11, 2015 at 10:48 PM
+-- Creation: Aug 12, 2015 at 07:43 PM
 --
 
 DROP TABLE IF EXISTS `projects`;
@@ -139,9 +240,11 @@ CREATE TABLE IF NOT EXISTS `projects` (
   `id` int(10) NOT NULL,
   `p_id` int(10) NOT NULL,
   `s_id` int(10) NOT NULL,
+  `u_id` int(10) NOT NULL,
   `name` varchar(50) NOT NULL,
   `description` varchar(100) DEFAULT NULL,
-  `u_id` int(10) NOT NULL
+  `date_created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `date_modified` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00'
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
 
 --
@@ -160,29 +263,39 @@ CREATE TABLE IF NOT EXISTS `projects` (
 --       `statuses` -> `id`
 --
 
+--
+-- Triggers `projects`
+--
+DROP TRIGGER IF EXISTS `project_update_date_modified`;
+DELIMITER $$
+CREATE TRIGGER `project_update_date_modified` BEFORE UPDATE ON `projects`
+ FOR EACH ROW SET new.date_modified = NOW()
+$$
+DELIMITER ;
+
 -- --------------------------------------------------------
 
 --
--- Table structure for table `roles`
+-- Table structure for table `project_roles`
 --
 -- Creation: Aug 11, 2015 at 10:16 PM
 --
 
-DROP TABLE IF EXISTS `roles`;
-CREATE TABLE IF NOT EXISTS `roles` (
+DROP TABLE IF EXISTS `project_roles`;
+CREATE TABLE IF NOT EXISTS `project_roles` (
   `id` int(10) NOT NULL,
   `name` varchar(10) NOT NULL
 ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;
 
 --
--- RELATIONS FOR TABLE `roles`:
+-- RELATIONS FOR TABLE `project_roles`:
 --
 
 --
--- Dumping data for table `roles`
+-- Dumping data for table `project_roles`
 --
 
-INSERT INTO `roles` (`id`, `name`) VALUES
+INSERT INTO `project_roles` (`id`, `name`) VALUES
 (1, 'Developer'),
 (2, 'Tester');
 
@@ -191,21 +304,56 @@ INSERT INTO `roles` (`id`, `name`) VALUES
 --
 -- Table structure for table `r_issue_user`
 --
--- Creation: Aug 11, 2015 at 10:27 PM
+-- Creation: Aug 12, 2015 at 05:20 PM
 --
 
 DROP TABLE IF EXISTS `r_issue_user`;
 CREATE TABLE IF NOT EXISTS `r_issue_user` (
   `i_id` int(10) NOT NULL,
-  `u_id` int(10) NOT NULL
+  `u_id` int(10) NOT NULL,
+  `r_id` int(10) NOT NULL
 ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
 
 --
 -- RELATIONS FOR TABLE `r_issue_user`:
 --   `i_id`
 --       `issues` -> `id`
+--   `r_id`
+--       `issue_roles` -> `id`
+--   `u_id`
+--       `users` -> `id`
+--   `i_id`
+--       `issues` -> `id`
 --   `u_id`
 --       `users` -> `id`
+--   `r_id`
+--       `issue_roles` -> `id`
+--
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `r_message_user`
+--
+-- Creation: Aug 12, 2015 at 05:39 PM
+--
+
+DROP TABLE IF EXISTS `r_message_user`;
+CREATE TABLE IF NOT EXISTS `r_message_user` (
+  `m_id` int(10) NOT NULL,
+  `u_id` int(11) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+
+--
+-- RELATIONS FOR TABLE `r_message_user`:
+--   `m_id`
+--       `messages` -> `id`
+--   `u_id`
+--       `users` -> `id`
+--   `u_id`
+--       `users` -> `id`
+--   `m_id`
+--       `messages` -> `id`
 --
 
 -- --------------------------------------------------------
@@ -228,7 +376,7 @@ CREATE TABLE IF NOT EXISTS `r_project_user` (
 --   `p_id`
 --       `projects` -> `id`
 --   `r_id`
---       `roles` -> `id`
+--       `project_roles` -> `id`
 --   `u_id`
 --       `users` -> `id`
 --   `p_id`
@@ -236,7 +384,7 @@ CREATE TABLE IF NOT EXISTS `r_project_user` (
 --   `u_id`
 --       `users` -> `id`
 --   `r_id`
---       `roles` -> `id`
+--       `project_roles` -> `id`
 --
 
 -- --------------------------------------------------------
@@ -280,6 +428,16 @@ CREATE TABLE IF NOT EXISTS `users` (
 -- RELATIONS FOR TABLE `users`:
 --
 
+--
+-- Triggers `users`
+--
+DROP TRIGGER IF EXISTS `user_update_date_modified`;
+DELIMITER $$
+CREATE TRIGGER `user_update_date_modified` BEFORE UPDATE ON `users`
+ FOR EACH ROW SET new.date_modified = NOW()
+$$
+DELIMITER ;
+
 --
 -- Indexes for dumped tables
 --
@@ -298,6 +456,13 @@ ALTER TABLE `activities`
   ADD PRIMARY KEY (`date`),
   ADD KEY `a_id` (`a_id`);
 
+--
+-- Indexes for table `emails`
+--
+ALTER TABLE `emails`
+  ADD KEY `u_id` (`u_id`),
+  ADD KEY `date` (`date`);
+
 --
 -- Indexes for table `issues`
 --
@@ -309,6 +474,22 @@ ALTER TABLE `issues`
   ADD KEY `i_id` (`pr_id`),
   ADD KEY `s_id` (`s_id`);
 
+--
+-- Indexes for table `issue_roles`
+--
+ALTER TABLE `issue_roles`
+  ADD PRIMARY KEY (`id`),
+  ADD UNIQUE KEY `name` (`name`);
+
+--
+-- Indexes for table `messages`
+--
+ALTER TABLE `messages`
+  ADD PRIMARY KEY (`id`),
+  ADD KEY `u_id` (`u_id`),
+  ADD KEY `i_id` (`i_id`),
+  ADD KEY `p_id` (`p_id`);
+
 --
 -- Indexes for table `priorities`
 --
@@ -322,12 +503,13 @@ ALTER TABLE `projects`
   ADD PRIMARY KEY (`id`),
   ADD KEY `u_id` (`u_id`),
   ADD KEY `p_id` (`p_id`),
-  ADD KEY `s_id` (`s_id`);
+  ADD KEY `s_id` (`s_id`),
+  ADD UNIQUE KEY `name` (`name`);
 
 --
--- Indexes for table `roles`
+-- Indexes for table `project_roles`
 --
-ALTER TABLE `roles`
+ALTER TABLE `project_roles`
   ADD PRIMARY KEY (`id`);
 
 --
@@ -335,6 +517,14 @@ ALTER TABLE `roles`
 --
 ALTER TABLE `r_issue_user`
   ADD UNIQUE KEY `i_id` (`i_id`,`u_id`),
+  ADD KEY `u_id` (`u_id`),
+  ADD KEY `r_id` (`r_id`);
+
+--
+-- Indexes for table `r_message_user`
+--
+ALTER TABLE `r_message_user`
+  ADD UNIQUE KEY `m_id` (`m_id`,`u_id`),
   ADD KEY `u_id` (`u_id`);
 
 --
@@ -375,6 +565,16 @@ ALTER TABLE `actions`
 ALTER TABLE `issues`
   MODIFY `id` int(10) NOT NULL AUTO_INCREMENT;
 --
+-- AUTO_INCREMENT for table `issue_roles`
+--
+ALTER TABLE `issue_roles`
+  MODIFY `id` int(10) NOT NULL AUTO_INCREMENT;
+--
+-- AUTO_INCREMENT for table `messages`
+--
+ALTER TABLE `messages`
+  MODIFY `id` int(10) NOT NULL AUTO_INCREMENT;
+--
 -- AUTO_INCREMENT for table `priorities`
 --
 ALTER TABLE `priorities`
@@ -385,9 +585,9 @@ ALTER TABLE `priorities`
 ALTER TABLE `projects`
   MODIFY `id` int(10) NOT NULL AUTO_INCREMENT;
 --
--- AUTO_INCREMENT for table `roles`
+-- AUTO_INCREMENT for table `project_roles`
 --
-ALTER TABLE `roles`
+ALTER TABLE `project_roles`
   MODIFY `id` int(10) NOT NULL AUTO_INCREMENT,AUTO_INCREMENT=3;
 --
 -- AUTO_INCREMENT for table `statuses`
@@ -418,6 +618,14 @@ ALTER TABLE `issues`
   ADD CONSTRAINT `issues_ibfk_3` FOREIGN KEY (`pr_id`) REFERENCES `priorities` (`id`),
   ADD CONSTRAINT `issues_ibfk_4` FOREIGN KEY (`s_id`) REFERENCES `statuses` (`id`);
 
+--
+-- Constraints for table `messages`
+--
+ALTER TABLE `messages`
+  ADD CONSTRAINT `messages_ibfk_1` FOREIGN KEY (`u_id`) REFERENCES `users` (`id`),
+  ADD CONSTRAINT `messages_ibfk_2` FOREIGN KEY (`i_id`) REFERENCES `issues` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+  ADD CONSTRAINT `messages_ibfk_3` FOREIGN KEY (`p_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
+
 --
 -- Constraints for table `projects`
 --
@@ -431,7 +639,15 @@ ALTER TABLE `projects`
 --
 ALTER TABLE `r_issue_user`
   ADD CONSTRAINT `r_issue_user_ibfk_1` FOREIGN KEY (`i_id`) REFERENCES `issues` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
-  ADD CONSTRAINT `r_issue_user_ibfk_2` FOREIGN KEY (`u_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
+  ADD CONSTRAINT `r_issue_user_ibfk_2` FOREIGN KEY (`u_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+  ADD CONSTRAINT `r_issue_user_ibfk_3` FOREIGN KEY (`r_id`) REFERENCES `issue_roles` (`id`);
+
+--
+-- Constraints for table `r_message_user`
+--
+ALTER TABLE `r_message_user`
+  ADD CONSTRAINT `r_message_user_ibfk_1` FOREIGN KEY (`u_id`) REFERENCES `users` (`id`),
+  ADD CONSTRAINT `r_message_user_ibfk_2` FOREIGN KEY (`m_id`) REFERENCES `messages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
 
 --
 -- Constraints for table `r_project_user`
@@ -439,5 +655,5 @@ ALTER TABLE `r_issue_user`
 ALTER TABLE `r_project_user`
   ADD CONSTRAINT `r_project_user_ibfk_1` FOREIGN KEY (`p_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
   ADD CONSTRAINT `r_project_user_ibfk_2` FOREIGN KEY (`u_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
-  ADD CONSTRAINT `r_project_user_ibfk_3` FOREIGN KEY (`r_id`) REFERENCES `roles` (`id`);
+  ADD CONSTRAINT `r_project_user_ibfk_3` FOREIGN KEY (`r_id`) REFERENCES `project_roles` (`id`);
 COMMIT;

+ 68 - 0
lib/bugs.class.php

@@ -0,0 +1,68 @@
+<?php
+	require_once('sql.class.php');
+	require_once('template.class.php');
+	require_once('user.class.php');
+	require_once('project.class.php');
+	class Bugs {
+		public static $sql;
+		public static $cache = array(
+			'users'=>array(),
+			'issue'=>array()
+		);
+		public function __construct(){
+
+		}
+		public function __set($name,$value){
+			switch($name){
+				default:
+					static::$cache[$name] = $value;
+			}
+		}
+		public function __get($name){
+			switch($name){
+				default:
+					if(isset(static::$cache)){
+						return static::$cache[$name];
+					}
+			}
+		}
+		static function connect($server='localhost',$user='bugs',$pass='bugs',$db='bugs'){
+			static::$sql = new SQL($server,$user,$pass,$db);
+		}
+		static function user($id){
+			if(is_string($id)){
+				$id = static::$sql->query("
+					SELECT id
+					FROM users
+					WHERE name = ?;
+				",'s',$id)->assoc_result['id'];
+			}
+			if(!isset(static::$cache['users'][$id])){
+				static::$cache['users'][$id] = new User($id);
+			}
+			return static::$cache['users'][$id];
+		}
+		static function issue($id){
+			if(!isset(static::$cache['issues'][$id])){
+				static::$cache['issues'][$id] = new Issue($id);
+			}
+			return static::$cache['issues'][$id];
+		}
+		static function project($id){
+			if(is_string($id)){
+				$id = static::$sql->query("
+					SELECT id
+					FROM projects
+					WHERE name = ?;
+				",'s',$id)->assoc_result['id'];
+			}
+			if(!isset(static::$cache['projects'][$id])){
+				static::$cache['projects'][$id] = new Project($id);
+			}
+			return static::$cache['projects'][$id];
+		}
+		static function template($name){
+			return new Template($name);
+		}
+	}
+?>

+ 59 - 0
lib/issue.class.php

@@ -0,0 +1,59 @@
+<?php
+	class Issue implements JsonSerializable{
+		public $id;
+		public $cache = array(
+			'p_id'=>null,
+			'u_id'=>null,
+			'pr_id'=>null,
+			's_id'=>null,
+			'name'=>null,
+			'description'=>null,
+			'date_created'=>null,
+			'date_modified'=>null
+		);
+		public function __construct($id){
+			$this->id = intval($id);
+			$cache = Bugs::$sql->query("
+				SELECT	p_id,
+						u_id,
+						pr_id,
+						s_id,
+						name,
+						description,
+						date_created,
+						date_modified
+				FROM issues
+				WHERE id = ?;
+			",'i',$this->id)->assoc_result;
+			foreach($cache as $key => $value){
+				$this->cache[$key] = $value;
+			}
+		}
+		public function jsonSerialize(){
+			return array(
+				'id'=> $this->id,
+				'name'=> $this->name,
+				'description'=> $this->description,
+				'date_created'=> $this->date_created,
+				'date_modified'=> $this->date_modified
+			);
+		}
+		public function __toString(){
+			return $this->path;
+		}
+		public function __set($name,$value){
+			switch($name){
+				default:
+					$this->cache[$name] = $value;
+			}
+		}
+		public function __get($name){
+			switch($name){
+				default:
+					if(isset($this->cache)){
+						return $this->cache[$name];
+					}
+			}
+		}
+	}
+?>

+ 58 - 0
lib/project.class.php

@@ -0,0 +1,58 @@
+<?php
+	require_once('issue.class.php');
+	class Project implements JsonSerializable{
+		public $id;
+		public $cache = array(
+			'p_id'=>null,
+			's_id'=>null,
+			'u_id'=>null,
+			'name'=>null,
+			'description'=>null,
+			'date_created'=>null,
+			'date_modified'=>null
+		);
+		public function __construct($id){
+			$this->id = intval($id);
+			$cache = Bugs::$sql->query("
+				SELECT	p_id,
+						s_id,
+						u_id,
+						name,
+						description,
+						date_created,
+						date_modified
+				FROM issues
+				WHERE id = ?;
+			",'i',$this->id)->assoc_result;
+			foreach($cache as $key => $value){
+				$this->cache[$key] = $value;
+			}
+		}
+		public function jsonSerialize(){
+			return array(
+				'id'=> $this->id,
+				'name'=> $this->name,
+				'description'=> $this->description,
+				'date_created'=> $this->date_created,
+				'date_modified'=> $this->date_modified
+			);
+		}
+		public function __toString(){
+			return $this->path;
+		}
+		public function __set($name,$value){
+			switch($name){
+				default:
+					$this->cache[$name] = $value;
+			}
+		}
+		public function __get($name){
+			switch($name){
+				default:
+					if(isset($this->cache)){
+						return $this->cache[$name];
+					}
+			}
+		}
+	}
+?>

+ 4 - 1
lib/router.class.php

@@ -3,7 +3,7 @@
 	require_once('response.class.php');
 	class Router {
 		public static $paths = array();
-		private static $base = '/';
+		public static $base = '/';
 		private static $responses = array();
 		public function __construct(){
 			// No Constructing
@@ -20,6 +20,9 @@
 		public static function base($base){
 			static::$base = $base;
 		}
+		public static function url($url){
+			return preg_replace('/(\/+)/','/',$url);
+		}
 		// fn = function(response,args){}
 		public static function path($path,$fn){
 			$obj = false;

+ 31 - 0
lib/template.class.php

@@ -0,0 +1,31 @@
+<?php
+	class Template {
+		public $name;
+		public function __construct($name){
+			$this->name = $name;
+		}
+		public function __get($name){
+			switch($name){
+				case 'path':
+					return realpath(dirname(__FILE__)).'/../templates/'.$this->name.'.php';
+				break;
+			}
+		}
+		public function __invoke($context){
+			ob_start();
+			$GLOBALS['context'] = $context;
+			if(file_exists($this->path) && is_file($this->path)){
+				include($this->path);
+			}
+			$ret = ob_get_contents();
+			ob_end_clean();
+			return $ret;
+		}
+		public function __toString(){
+			return $this(array());
+		}
+		public function run($context){
+			return $this($context);
+		}
+	}
+?>

+ 34 - 6
lib/user.class.php

@@ -1,22 +1,50 @@
 <?php
 	class User implements JsonSerializable{
-		public $sql;
 		public $id;
-		public function __construct($id,$sql){
-			$this->id = $id;
-			$this->sql = $sql;
+		public $cache = array(
+			'name'=>null,
+			'email'=>null,
+			'date_registered'=>null,
+			'date_modified'=>null
+		);
+		public function __construct($id){
+			$this->id = intval($id);
+			$cache = Bugs::$sql->query("
+				SELECT	name,
+						email,
+						date_registered,
+						date_modified
+				FROM users
+				WHERE id = ?;
+			",'i',$this->id)->assoc_result;
+			foreach($cache as $key => $value){
+				$this->cache[$key] = $value;
+			}
 		}
 		public function jsonSerialize(){
 			return array(
-				'id'=> $this->id
+				'id'=> $this->id,
+				'name'=> $this->name,
+				'email'=> $this->email,
+				'date_registered'=> $this->date_registered,
+				'date_modified'=> $this->date_modified
 			);
 		}
 		public function __toString(){
 			return $this->path;
 		}
+		public function __set($name,$value){
+			switch($name){
+				default:
+					$this->cache[$name] = $value;
+			}
+		}
 		public function __get($name){
 			switch($name){
-				
+				default:
+					if(isset($this->cache)){
+						return $this->cache[$name];
+					}
 			}
 		}
 	}

+ 13 - 0
paths/issue.php

@@ -0,0 +1,13 @@
+<?php
+	Router::paths(array(
+		'/!{issue}'=>function($res,$args){
+			$res->header('Content-Type','application/json');
+			$res->json(array(
+				'issue'=> Bugs::issue($args->issue)
+			));
+		},
+		'/issue/{issue}'=>function($res,$args){
+			$res->header('Location',Router::url(Router::$base.'/!'.$args->issue));
+		}
+	));
+?>

+ 16 - 0
paths/project.php

@@ -0,0 +1,16 @@
+<?php
+	Router::paths(array(
+		'/project/{project}'=>function($res,$args){
+			$res->header('Content-Type','application/json');
+			$res->json(array(
+				'project'=> Bugs::project($args->project)
+			));
+		},
+		'/project/{project}/issue/{issue}'=>function($res,$args){
+			$res->header('Location',Router::url(Router::$base."/!{$args->issue}"));
+		},
+		'/project/{project}/!{issue}'=>function($res,$args){
+			$res->header('Location',Router::url(Router::$base."!{$args->issue}"));
+		}
+	));
+?>

+ 10 - 1
paths/user.php

@@ -1,7 +1,16 @@
 <?php
 	Router::paths(array(
 		'/~{user}'=>function($res,$args){
-			$res->write($args->user);
+			$res->write(
+				Bugs::template('user')
+					->run(Bugs::user($args->user))
+			);
+		},
+		'/user/{user}'=>function($res,$args){
+			$res->header(
+				'Location',
+				Router::url(Router::$base.'/~'.$args->user)
+			);
 		}
 	));
 ?>

+ 4 - 0
templates/user.php

@@ -0,0 +1,4 @@
+<?php
+	global $context;
+	echo $context->name;
+?>