From: Alex-Laptop <alexmjoss2413@gmail.com>
Date: Tue, 19 Dec 2017 22:06:37 +0000 (-0800)
Subject: added old php files
X-Git-Tag: v1.0.0~90
X-Git-Url: http://git.infiniteadaptability.org/?a=commitdiff_plain;h=798713a7114c9f2a7c18c4d7d468ab4feb9f238f;p=workouts

added old php files
---

diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..0da445d
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,15 @@
+Created by Alex Joss
+Version: 0.0.0
+Publish Date: 0000-00-00
+Last Updated: 2017-07-05
+
+Currently Working On:
+
+Bugs:
+
+Plan:
+-consider adding add workout button
+
+Tests:
+
+Summary:
\ No newline at end of file
diff --git a/WorkoutManagementAttributesManager/AttributesManagerRow/AttributesManagerRow.php b/WorkoutManagementAttributesManager/AttributesManagerRow/AttributesManagerRow.php
new file mode 100644
index 0000000..c113ad0
--- /dev/null
+++ b/WorkoutManagementAttributesManager/AttributesManagerRow/AttributesManagerRow.php
@@ -0,0 +1,133 @@
+<script type="text/javascript">
+var AttributesManagerRow = React.createClass({
+	getInitialState: function () {
+		return ({dragEnd:Number(this.props.data.attribute_num)*5,edit:0,name:this.props.data.attribute_name,label:this.props.data.attribute_label,comments:this.props.data.comments});
+	},
+	componentDidUpdate: function (prevProps,prevState) {
+		if((this.state.dragging)&&(!prevState.dragging)&&(this.state.edit===0)){
+			document.addEventListener('mousemove',this.onMouseMove);
+			document.addEventListener('mouseup',this.onMouseUp);
+		}
+		else if((!this.state.dragging)&&(prevState.dragging)){
+			document.removeEventListener('mousemove',this.onMouseMove);
+			document.removeEventListener('mouseup',this.onMouseUp);
+		}
+		/*var attrnum = this.props.data.attribute_num;
+		$('#attribute-manager-row-'+this.props.data.id).draggable({
+			containment:'parent',
+			revert:true,
+			start: function () {
+				//console.log('start drag');
+			},
+			stop: function (e,ui) {
+				var vd = ui.position.top/$(this).outerHeight();
+				serverRequest({query_type:"workout_management_attributes_reorder",attrnum:attrnum,vd:vd})
+				.then(function(data) {
+					console.log(data);
+				})
+				.catch(function(error) {
+					console.error("AttributesManagerRow","workout_management_attributes_reorder",error);
+				});
+			
+			}
+		});*/
+	},
+	componentWillReceiveProps: function (nextProps) {
+		if(this.state.edit!==1){
+			if((!(Number.isInteger(this.state.dragEnd)))||(this.props.data.attribute_num!=nextProps.data.attribute_num)){
+				this.setState({dragEnd:Number(nextProps.data.attribute_num)*5});
+			}
+		}
+	},
+	shouldComponentUpdate: function (nextProps,nextState) {
+		if((JSON.stringify(nextState)==JSON.stringify(this.state))&&(JSON.stringify(nextProps)==JSON.stringify(this.props))){
+			return false;
+		}
+		return true;
+	},
+	onMouseDown: function (event) {
+		var target = event.target;
+		var targettype = target.tagName.toLowerCase();
+		if((targettype!="i")&&(targettype!="input")){
+			this.setState({dragging:true,dragStart:event.pageY});
+			event.stopPropagation();
+			event.preventDefault();
+		}
+	},
+	onMouseUp: function (event) {
+		this.setState({dragging:false},function () {
+			this.handleDragEnd();
+		});
+		event.stopPropagation();
+		event.preventDefault();
+	},
+	onMouseMove: function (event) {
+		if(!(this.state.dragging)){
+			return false;
+		}
+		this.setState({dragEnd:(100*(event.pageY-this.state.dragStart)/document.documentElement.clientHeight)+this.props.data.attribute_num*5});
+	},
+	handleDragEnd: function () {
+		var vd = (this.state.dragEnd - this.props.data.attribute_num*5)/5;
+		serverRequest({query_type:"workout_management_attributes_reorder",attrnum:this.props.data.attribute_num,vd:vd})
+		.then(function(data) {
+			console.log(data);
+		})
+		.catch(function(error) {
+			console.error("AttributeManagerRow.php","workout_management_attributes_reorder",error);
+		});
+	},
+	handleSetEdit: function () {
+		this.setState({edit:(this.state.edit==0)?1:0});
+	},
+	handleChange: function (type,event) {
+		if(this.state.edit==1){
+			switch(type){
+				case "name":
+					this.setState({name:event.target.value});
+					break;
+				case "label":
+					this.setState({label:event.target.value});
+					break;
+				case "comments":
+					this.setState({comments:event.target.value});
+					break;
+				default:
+					console.log('error');
+					break;
+			}
+		}
+	},
+	handleAcceptChanges: function () {
+		serverRequest({query_type:"workout_management_attribute_save",id:this.props.data.id,name:this.state.name,label:this.state.label,comments:this.state.comments})
+		.then(function(data) {
+			console.log(data);
+			this.setState({edit:0});
+		}.bind(this))
+		.catch(function(error) {
+			console.error("AttributesManagerRow","workout_management_attribute_save",error);
+		});
+	},
+	handleDelete: function () {
+		var c = confirm("Are you sure you wish to delete this attribute [name=\""+this.state.name+"\"] ?");
+		if(c==true){
+			serverRequest({query_type:"workout_management_attribute_delete",id:this.props.data.id})
+			.then(function(data) {
+				console.log(data);
+				this.setState({edit:"deleted"});
+			}.bind(this))
+			.catch(function(error) {
+				console.error("AttributesManagerRow","workout_management_attribute_delete",error);
+			});
+		}
+	},
+	render: function () {
+		return React.createElement("div",{style:{top:this.state.dragEnd+"vh"},className:((this.state.edit==1)?"editable":"")+" wm-attributes-manager-row",onMouseDown:this.onMouseDown},
+			(this.state.edit===1)?React.createElement("input",{type:"text",value:this.state.name,onChange:this.handleChange.bind(this,"name")}):React.createElement("span",null,this.state.name),
+			(this.state.edit===1)?React.createElement("i",{className:"fa fa-check",onClick:this.handleAcceptChanges}):"",
+			React.createElement("i",{className:"fa fa-pencil",onClick:this.handleSetEdit}),
+			React.createElement("i",{className:"fa fa-trash",onClick:this.handleDelete})
+		);
+	}
+});
+</script>
\ No newline at end of file
diff --git a/WorkoutManagementAttributesManager/WorkoutManagementAttributesManager.php b/WorkoutManagementAttributesManager/WorkoutManagementAttributesManager.php
new file mode 100644
index 0000000..e89cc97
--- /dev/null
+++ b/WorkoutManagementAttributesManager/WorkoutManagementAttributesManager.php
@@ -0,0 +1,27 @@
+<script type="text/javascript">
+var WorkoutManagementAttributesManager = React.createClass({
+	handleAddNewAttr: function () {
+		serverRequest({query_type:"workout_management_add_attribute"})
+		.then(function(data) {
+			console.log(data);
+		})
+		.catch(function(error) {
+			console.error("WorkoutManagementAttributesManager","workout_management_add_attribute",error);
+		});
+	},
+	render: function () {
+		var rows = [];
+		for(var attr in this.props.data){
+			rows.push(React.createElement(AttributesManagerRow,{key:"wm-attr-manager-row-"+this.props.data[attr].id,data:this.props.data[attr]}));
+		}
+		return React.createElement("div",{className:"wm-attr-manager-div"},
+			React.createElement("h3",null,"Manage Attributes"),
+			React.createElement("div",{style:{height:5*this.props.data.length+"vh"},className:"wm-attr-manager-container"},rows),
+			React.createElement("input",{type:"button",onClick:this.handleAddNewAttr,value:"Add New Attribute"})
+		);
+	}
+});
+</script>
+<?php
+require("AttributesManagerRow/AttributesManagerRow.php");
+?>
\ No newline at end of file
diff --git a/WorkoutManagementDaysAgoTableData/WorkoutManagementDaysAgoTableData.php b/WorkoutManagementDaysAgoTableData/WorkoutManagementDaysAgoTableData.php
new file mode 100644
index 0000000..e31bf55
--- /dev/null
+++ b/WorkoutManagementDaysAgoTableData/WorkoutManagementDaysAgoTableData.php
@@ -0,0 +1,26 @@
+<script type="text/javascript">
+var WorkoutManagementDaysAgoTableData = React.createClass({//Recent Workouts Table
+	render: function() {
+		var heads = [];
+		for(var attr in this.props.attributes){
+			heads.push(React.createElement("th",{key:"da-th-"+attr},this.props.attributes[attr]["attribute_name"]));
+		}
+		var rows=[];
+		rows.push(React.createElement("td",{key:"da"},"Days Ago"));
+		for(var key in this.props.data){
+			rows.push(React.createElement("td",{key:"da-"+key},this.props.data[key]));
+		}
+		return React.createElement("table",{className:(heads.length>0)?"":"hidden"},
+			React.createElement("thead",null,
+				React.createElement("tr",null,
+					React.createElement("th",null),
+					heads
+				)
+			),
+			React.createElement("tbody",null,
+				React.createElement("tr",null,rows)
+			)
+		);
+	}
+});
+</script>
\ No newline at end of file
diff --git a/WorkoutManagementWorkoutTable/WorkoutManagementWorkoutTable.php b/WorkoutManagementWorkoutTable/WorkoutManagementWorkoutTable.php
new file mode 100644
index 0000000..8785b46
--- /dev/null
+++ b/WorkoutManagementWorkoutTable/WorkoutManagementWorkoutTable.php
@@ -0,0 +1,107 @@
+<script type="text/javascript">
+var WorkoutManagementWorkoutTable = React.createClass({
+	loadDataFromServer: function () {
+		serverRequest({query_type:"workout_management_table_data",sorting:this.state.sorting,order:this.state.order},"json")
+		.then(function(data) {
+			//console.log(data);
+			if(data.hash!=this.state.hash){
+				this.setState({data:data.raw,hash:data.hash});
+			}
+		}.bind(this))
+		.catch(function(error) {
+			console.error("WorkoutManagementWorkoutTable.php","workout_management_table_data",error);
+		});
+	},
+	componentDidMount: function () {
+		this.loadDataFromServer();
+	},
+	shouldComponentUpdate: function (nextProps,nextState) {
+		if((this.props.hash!=nextProps.hash)||(JSON.stringify(this.state)!=JSON.stringify(nextState))){
+			return true;
+		}
+		return false;
+	},
+	componentWillReceiveProps: function (nextProps) {
+		if(this.props.hash!=nextProps.hash){
+			this.loadDataFromServer();
+		}
+	},
+	getInitialState: function() {
+		return {data:[],hash:""};
+	},
+	changeSorting: function (key,event) {
+		if(typeof this.state.sorting=="undefined"){
+			var toset = {};
+			toset[key]="ASC";
+			var order = [key];
+		}
+		else {
+			var toset = this.state.sorting;
+			var order = (typeof this.state.order!="undefined")?this.state.order:[];
+			if(typeof this.state.sorting[key]!="undefined"){
+				toset[key]=(this.state.sorting[key]=="ASC")?"DESC":"ASC";
+			}
+			else {
+				toset[key]="ASC";
+			}
+			if(event.shiftKey){
+				if(order.indexOf(key)<0){
+					order.push(key);
+				}
+			}
+			else {
+				for(var i in toset){
+					if(i!=key){
+						delete toset[i];
+					}
+				}
+				order = [key];
+			}
+		}
+		this.setState({sorting:toset,order:order},function () {
+			this.loadDataFromServer();
+		});
+	},
+	render: function() {
+		var rows = [];
+		var rowtype = "odd";
+		for(var i in this.state.data){
+			rows.push(React.createElement(WorkoutManagementWorkoutTableRows,{key:"wm-row-"+this.state.data[i].id,data:this.state.data[i],rowtype:rowtype}));
+			rowtype = (rowtype=="odd")?"even":"odd";
+		}
+		var heads = [];
+		for(var head in this.props.attributes){
+			heads.push(React.createElement("th",{key:"workout-table-head-"+head,className:(typeof this.state.sorting!="undefined")?(typeof this.state.sorting["workout_attributes_"+this.props.attributes[head]["attribute_num"]]!="undefined")?"sortedby":"":"",onClick:this.changeSorting.bind(this,"workout_attributes_"+this.props.attributes[head]["attribute_num"])},this.props.attributes[head]["attribute_name"],
+				React.createElement("i",{className:((typeof this.state.sorting!="undefined")&&(typeof this.state.sorting["workout_attributes_"+this.props.attributes[head]["attribute_num"]]!="undefined"))?"fa fa-sort-"+this.state.sorting["workout_attributes_"+this.props.attributes[head]["attribute_num"]].toLowerCase():"fa fa-sort"})
+			));
+		}
+		return React.createElement("div",null,
+			React.createElement("div",{className:"workout-management-table-div"},
+				React.createElement("table",{className:"workout-management-table"},
+					React.createElement("thead",null,
+						React.createElement("tr",null,
+							React.createElement("th",{className:(typeof this.state.sorting!="undefined")?(typeof this.state.sorting.workout_name!="undefined")?"sortedby":"":"",onClick:this.changeSorting.bind(this,"workout_name")},"Workout Name",
+								React.createElement("i",{className:((typeof this.state.sorting!="undefined")&&(typeof this.state.sorting.workout_name!="undefined"))?"fa fa-sort-"+this.state.sorting.workout_name.toLowerCase():"fa fa-sort"})
+							),
+							heads,
+							React.createElement("th",{className:(typeof this.state.sorting!="undefined")?(typeof this.state.sorting.times!="undefined")?"sortedby":"":"",onClick:this.changeSorting.bind(this,"times")},"Times Done",
+								React.createElement("i",{className:((typeof this.state.sorting!="undefined")&&(typeof this.state.sorting.times!="undefined"))?"fa fa-sort-"+this.state.sorting.times.toLowerCase():"fa fa-sort"})
+							),
+							React.createElement("th",{className:(typeof this.state.sorting!="undefined")?(typeof this.state.sorting.last!="undefined")?"sortedby":"":"",onClick:this.changeSorting.bind(this,"last")},"Last Done",
+								React.createElement("i",{className:((typeof this.state.sorting!="undefined")&&(typeof this.state.sorting.last!="undefined"))?"fa fa-sort-"+this.state.sorting.last.toLowerCase():"fa fa-sort"})
+							),
+							React.createElement("th",{className:(typeof this.state.sorting!="undefined")?(typeof this.state.sorting.comments!="undefined")?"sortedby":"":"",onClick:this.changeSorting.bind(this,"comments")},"Comments",
+								React.createElement("i",{className:((typeof this.state.sorting!="undefined")&&(typeof this.state.sorting.comments!="undefined"))?"fa fa-sort-"+this.state.sorting.comments.toLowerCase():"fa fa-sort"})
+							)
+						)
+					),
+					React.createElement("tbody",null,rows)
+				)
+			)
+		);
+	}
+});
+</script>
+<?php
+require("WorkoutManagementWorkoutTableRows/WorkoutManagementWorkoutTableRows.php");
+?>
\ No newline at end of file
diff --git a/WorkoutManagementWorkoutTable/WorkoutManagementWorkoutTableRows/WorkoutManagementWorkoutTableRows.php b/WorkoutManagementWorkoutTable/WorkoutManagementWorkoutTableRows/WorkoutManagementWorkoutTableRows.php
new file mode 100644
index 0000000..34cad12
--- /dev/null
+++ b/WorkoutManagementWorkoutTable/WorkoutManagementWorkoutTableRows/WorkoutManagementWorkoutTableRows.php
@@ -0,0 +1,81 @@
+<script type="text/javascript">
+var WorkoutManagementWorkoutTableRows = React.createClass({//Workout Management Table Rows
+	getInitialState: function () {
+		var attrs = JSON.parse(JSON.stringify(this.props.data.attrs));
+		return {workout_name:this.props.data.name,comments:this.props.data.comments,attributes:attrs,view:""};
+	},
+	handleValueChange: function(type,event) {
+		switch(type){
+			case "workout_name":
+				this.setState({workout_name:event.target.value});
+				break;
+			case "comments":
+				this.setState({comments:event.target.value});
+				break;
+			default:
+				console.log('error');
+		}
+	},
+	sendValues: function () {
+		serverRequest({query_type:'workout_management_edit',id:this.props.data.id,workout_name:this.state.workout_name,attributes:this.state.attributes,comments:this.state.comments})
+		.then(function(data) {
+			console.log(data);
+		})
+		.catch(function(error) {
+			console.error("WorkoutManagementWorkoutTableRows.php","workout_management_edit",error);
+		});
+	},
+	handleAttrToggle: function (attr) {
+		var attrs = JSON.parse(JSON.stringify(this.state.attributes));
+		attrs[attr]=(attrs[attr]=="No")?"Yes":"No";
+		this.setState({attributes:attrs},function () {
+			this.sendValues();
+		});
+	},
+	deleteToggle: function (type) {
+		this.setState({deletetd:type});
+	},
+	deleteEntry: function () {
+		var c = confirm("Are you sure you wish to delete this workout? This will also delete all data of completion of this workout.");
+		if(c==true){
+			this.setState({view:"hidden"},function () {
+				serverRequest({query_type:'workout_management_delete',id:this.props.data.id})
+				.then(function(data) {
+					console.log(data);
+				})
+				.catch(function(error) {
+					console.error("WorkoutManagementWorkoutTableRows.php","workout_management_delete",error);
+				});
+			});
+		}
+	},
+	render: function () {
+		var cells = [];
+		for(var attr in this.state.attributes){
+			cells.push(React.createElement("td",{key:"wm-row-"+this.props.data.id+"-attr-"+attr,onClick:this.handleAttrToggle.bind(this,attr)},
+				React.createElement("span",null,this.state.attributes[attr])
+			));
+		}
+		return React.createElement("tr",{className:this.props.rowtype+" "+this.state.view},
+			React.createElement("td",{className:"inputok"},
+				React.createElement("input",{className:"workout-management-table-input",value:this.state.workout_name,type:"text",onChange:this.handleValueChange.bind(this,"workout_name"),onBlur:this.sendValues})
+			),
+			cells,
+			React.createElement("td",null,
+				React.createElement("span",null,this.props.data['times_done'])
+			),
+			React.createElement("td",null,
+				React.createElement("span",null,this.props.data['last_done'])
+			),
+			React.createElement("td",{className:"inputok"},
+				React.createElement("textarea",{className:"workout-management-table-comments",value:this.state.comments,onChange:this.handleValueChange.bind(this,"comments"),onBlur:this.sendValues}),
+				React.createElement("div",{className:this.state.deletetd},
+					React.createElement("div",{className:"workout-management-table-delete-container"},
+						React.createElement("i",{className:"fa fa-trash-o",onClick:this.deleteEntry})
+					)
+				)
+			)
+		);
+	}
+});
+</script>
\ No newline at end of file
diff --git a/class.module_workout_management.php b/class.module_workout_management.php
new file mode 100644
index 0000000..d5257e1
--- /dev/null
+++ b/class.module_workout_management.php
@@ -0,0 +1,232 @@
+<?php
+
+/**
+ * Workout Management Module
+ * Required methods, then custom methods, then variables
+ * Three variables are required (header, stylesheets, module_file)
+ * Add to required_table_structure variable and uncomment the table_verify function in the __construct method to check table structure before loading class
+ */
+
+class module_workout_management extends module implements imodule {
+	
+	const MODULE_NAME = "Workout Management";
+	
+	public function __construct() {
+		parent::__construct();
+		if(!($this->table_verify($this->get_required_module_table_data()))){
+			self::log_error(self::MODULE_NAME." required tables verification failed.");
+		}
+		//Options: Module Name,linked module path,quick_tool_icon (font awesome icon class)
+		//Default is array ("",NULL,NULL)
+		$options = array (self::MODULE_NAME,NULL,"fa fa-bolt fa-lg");
+		if(!($this->module_check(__FILE__,$options))){
+			self::log_error(self::MODULE_NAME." module not loaded correctly.");
+		}
+		if(!($this->public_check($this->fields_marked_public,__FILE__))){
+			self::log_error(self::MODULE_NAME." public fields not correctly implemented.");
+		}
+	}
+	
+	//Start required methods
+	public function cron_tasks () {
+		//Insert any methods here to run every 30 minutes, make sure to return TRUE
+		return TRUE;
+	}
+	
+	public function get_header () {
+		//Add any extra javascript scripts/css required to header
+		$return = $this->get_default_header();
+		if((stripos($return,"</HEAD>")!==FALSE)&&(stripos($return,"</HEAD>")==strlen($return)-7)){
+			if((is_array($this->header_scripts))&&(count($this->header_scripts)>0)){
+				$return = rtrim($return,"</HEAD>");
+				foreach($this->header_scripts as $i=>$v){
+					if($v['sri']!=""){
+						$return .= "<script type='".$v['type']."' src='".$v['src']."' integrity='".$v['sri']."' crossorigin='anonymous'></script>\n";
+					}
+					else {
+						$return .= "<script type='".$v['type']."' src='".$v['src']."'></script>\n";
+					}
+				}
+				$return .= "</HEAD>";
+			}
+			$stylesheets = $this->get_stylesheets();
+			if($stylesheets!=""){
+				$return = rtrim($return,"</HEAD>");
+				$return .= $stylesheets;
+				$return .= "</HEAD>";
+			}
+		}
+		return $return;
+	}
+	
+	public function get_required_module_table_data ($type = "all") {
+		$tables = $this->required_table_structure;
+		foreach($tables as $name=>$rows){
+			//Double check blockchain tracking row and delete once found
+			if(!((isset($rows[0]))&&(($rows[0]===0)||($rows[0]===1)))){
+				unset($tables[$name]);
+				self::log_error(sprintf("Table structure data for `%s` not formatted correctly.%s",$name));
+				continue;
+			}
+			if(($rows[0]===0)&&($type!="all")){
+				unset($tables[$name]);
+			}
+			else {
+				unset($rows[0]);
+				$tables[$name] = array_values($rows);
+			}
+		}
+		return $tables;
+	}
+	
+	public function get_stylesheets () {
+		//Generate stylesheet <link>(s)
+		$return = "";
+		if(file_exists(__DIR__.DIRECTORY_SEPARATOR."style.css")){
+			$return .= "<LINK href='".ltrim(substr(__DIR__.DIRECTORY_SEPARATOR."style.css",strlen(dirname(dirname(dirname(__DIR__))))),"/")."' rel='stylesheet' type='text/css'>\n";
+		}
+		foreach($this->stylesheets as $i=>$v){
+			$return .= "<LINK href='".$v['href']."' rel='".$v['rel']."' type='".$v['type']."'>\n";
+		}
+		return $return;
+	}
+	
+	public function load_module ($get_header = NULL,$module_file = NULL,$generate_menu = FALSE) {
+		//Add 3rd parameter bool (TRUE to generate menu, FALSE or omit to not)
+		//Check to see if this has been called from child module
+		if(($get_header===NULL)||($module_file===NULL)){
+			if(!(parent::load_module($this->get_header(),__DIR__.DIRECTORY_SEPARATOR.$this->module_file,TRUE))){
+				return FALSE;
+			}
+		}
+		else {
+			if(!(parent::load_module($get_header,$module_file,$generate_menu))){
+				return FALSE;
+			}
+		}
+		return TRUE;
+	}
+	//End required methods
+	
+	//Start custom methods
+	protected function build_sortby ($sort = array(),$order = array()) {
+		$return = " ORDER BY";
+		$valid_fields = array(
+			"c" => array (//Fields in/derived from workout_count (always abbreviated `c` in SQL)
+				"comments_count",
+				"date",
+				"last_done",
+				"times_done"
+			),
+			"w" => array (//Fields in/derived from `workouts` (always abbreviated `w` in SQL)
+				"workout_name",
+				"workout_attributes",
+				"comments"
+			),
+			"a" => array (//Fields in `worktous_attributes` (always abbreviated `a` in SQL)
+				"attribute_num",
+				"attribute_name",
+				"attribute_label",
+			)
+		);
+		foreach($sort as $f=>$type) {
+			if(strpos($f,"workout_attributes")!==FALSE){
+				continue;
+			}
+			if(!((in_array($f,$valid_fields["c"]))||(in_array($f,$valid_fields["w"]))||(in_array($f,$valid_fields["a"])))){
+				unset($sort[$f]);
+			}
+		}
+		foreach($order as $i=>$f){
+			if(in_array($f,$valid_fields["c"])){
+				if($f=="comments_count"){
+					$f="comments";
+					$sort[$f]=$sort["comments_count"];
+				}
+				$return .= sprintf(" `c`.`%s` %s,",$f,($sort[$f]=="ASC")?"ASC":"DESC");
+			}
+			else if(strpos($f,"workout_attributes")!==FALSE){
+				$return .= sprintf(" SUBSTRING(`w`.`workout_attributes`,%d,1) %s,",((int) substr($f,19))+1,($sort[$f]=="ASC")?"ASC":"DESC");
+			}
+			else if(in_array($f,$valid_fields["w"])){
+				$return .= sprintf(" `w`.`%s` %s,",$f,($sort[$f]=="ASC")?"ASC":"DESC");
+			}
+			else if(in_array($f,$valid_fields["a"])){
+				$return .= sprintf(" `a`.`%s` %s,",$f,($sort[$f]=="ASC")?"ASC":"DESC");
+			}
+		}
+		$return = rtrim($return,",");
+		if($return==" ORDER BY"){
+			return "";
+		}
+		else {
+			return $return;
+		}
+	}
+	//End custom methods
+	
+	//Start required table structure data
+	//Use the following code to generate table data
+	/*
+	$data = array(0 => 0);//Change to 1 if tracked by blockchain
+	$query = "DESCRIBE `%%TABLE_NAME%%`;";
+	$result = mysqli_query($db,$query) or die(mysqli_error($db));
+	while($row = mysqli_fetch_assoc($result)){
+		$data[]=$row;
+	}
+	exit("<pre>".var_export($data)."</pre>");
+	*/
+	private $required_table_structure = array (
+		"workouts" => array (
+			0 => 1,
+			1 => array ( 'Field' => 'id', 'Type' => 'int(11)', 'Null' => 'NO', 'Key' => 'PRI', 'Default' => NULL, 'Extra' => 'auto_increment', ), 
+			2 => array ( 'Field' => 'siid', 'Type' => 'int(11)', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			3 => array ( 'Field' => 'workout_name', 'Type' => 'varchar(50)', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			4 => array ( 'Field' => 'workout_attributes', 'Type' => 'varchar(30)', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			5 => array ( 'Field' => 'comments', 'Type' => 'text', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			6 => array ( 'Field' => 'modified', 'Type' => 'timestamp', 'Null' => 'NO', 'Key' => '', 'Default' => 'CURRENT_TIMESTAMP', 'Extra' => 'on update CURRENT_TIMESTAMP', ),
+		),
+		"workouts_attributes" => array (
+			0 => 1,
+			1 => array ( 'Field' => 'id', 'Type' => 'int(11)', 'Null' => 'NO', 'Key' => 'PRI', 'Default' => NULL, 'Extra' => 'auto_increment', ), 
+			2 => array ( 'Field' => 'attribute_num', 'Type' => 'int(11)', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			3 => array ( 'Field' => 'attribute_name', 'Type' => 'varchar(255)', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			4 => array ( 'Field' => 'attribute_label', 'Type' => 'varchar(255)', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			5 => array ( 'Field' => 'comments', 'Type' => 'text', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			6 => array ( 'Field' => 'modified', 'Type' => 'timestamp', 'Null' => 'NO', 'Key' => '', 'Default' => 'CURRENT_TIMESTAMP', 'Extra' => 'on update CURRENT_TIMESTAMP', ),
+		),
+		"workout_count" => array (
+			0 => 1,
+			1 => array ( 'Field' => 'id', 'Type' => 'int(11)', 'Null' => 'NO', 'Key' => 'PRI', 'Default' => NULL, 'Extra' => 'auto_increment', ),  
+			2 => array ( 'Field' => 'wid', 'Type' => 'int(11)', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			3 => array ( 'Field' => 'date', 'Type' => 'date', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			4 => array ( 'Field' => 'comments', 'Type' => 'text', 'Null' => 'NO', 'Key' => '', 'Default' => NULL, 'Extra' => '', ), 
+			5 => array ( 'Field' => 'modified', 'Type' => 'timestamp', 'Null' => 'NO', 'Key' => '', 'Default' => 'CURRENT_TIMESTAMP', 'Extra' => 'on update CURRENT_TIMESTAMP', ),
+		)
+	);
+	//End required table structure data
+	
+	//Start public fields
+	//Fields in this area will be added to the public table so that they will be displayed in the public feed area
+	//Format of each row: 0 => array ("table" => "", "title_field" => "", "description_field" => "","denotes_public_field" => "","denotes_public_value"=>"" )
+	private $fields_marked_public = array (
+		
+	);
+	//End public fields
+	
+	//Header Variable (each row contains info linking to a javascript script used by the module and inserted into the header)
+	//Format of each row: 0 => array ("type" => "", "src" => "", "sri" => "")
+	private $header_scripts = array (
+		
+	);
+	
+	//Extra stylesheet information (each row contains a stylesheet to be loaded with the module
+	//Format for each row: 0 => array ( "href" => "", "rel"=> "", "type"=>"" )
+	private $stylesheets = array (
+		
+	);
+	
+	//Main file to load -- fill in and update
+	private $module_file = "workout_management.php";
+	
+}
\ No newline at end of file
diff --git a/recent_workouts/RecentWorkoutsTableRow/RecentWorkoutsTableRow.php b/recent_workouts/RecentWorkoutsTableRow/RecentWorkoutsTableRow.php
new file mode 100644
index 0000000..cc21535
--- /dev/null
+++ b/recent_workouts/RecentWorkoutsTableRow/RecentWorkoutsTableRow.php
@@ -0,0 +1,82 @@
+<script type="text/javascript">
+var RecentWorkoutsTableRow = React.createClass({
+	getInitialState: function () {
+		return {wid:this.props.data.wid,date:this.props.data.date,comments:this.props.data.comments,deletetd:"hidden",view:""};
+	},
+	handleValueChange: function(event,type) {
+		switch(type){
+			case "wid":
+				this.setState({wid:event.target.value},function () {
+					this.sendValues();
+				});
+				break;
+			case "comments":
+				this.setState({comments:event.target.value});
+				break;
+			default:
+				console.log('error');
+		}
+	},
+	setDate: function (newdate) {
+		this.setState({date:newdate},function () {
+			this.sendValues();
+		});
+	},
+	handleDatePicker: function (event) {
+		generateDatePicker(event.target.value)
+		.then(function(result) {
+			if(result!==false){
+				this.setDate(result);
+			}
+		}.bind(this));
+	},
+	sendValues: function () {
+		serverRequest({query_type:'recent_workouts_edit',wid:this.state.wid,comments:this.state.comments,date:this.state.date,id:this.props.data.id})
+		.then(function(data) {
+			console.log(data);
+		})
+		.catch(function(error) {
+			console.error("RecentWorkoutsTableRow.php","recent_workouts_edit",error);
+		});
+	},
+	deleteToggle: function (type) {
+		this.setState({deletetd:type});
+	},
+	deleteEntry: function () {
+		var c = confirm("Are you sure you wish to delete this workout?");
+		if(c==true){
+			this.setState({view:"hidden"},function () {
+				serverRequest({query_type:'recent_workouts_delete',id:this.props.data.id})
+				.then(function(data) {
+					console.log(data);
+				})
+				.catch(function(error) {
+					console.error("RecentWorkoutsTableRow.php","recent_workouts_delete",error);
+				});
+			});
+		}
+	},
+	render: function() {
+		var options = [];
+		for (var workout in this.props.workouts){
+			options.push(React.createElement("option",{value:this.props.workouts[workout].id,key:this.props.data.id+'-'+workout},this.props.workouts[workout].workout_name));
+		}
+		return React.createElement("tr",{onMouseEnter:this.deleteToggle.bind(this,""),onMouseLeave:this.deleteToggle.bind(this,"hidden"),className:this.props.rowtype+" "+this.state.view},
+			React.createElement("td",{className:"inputok"},
+				React.createElement("select",{className:"recent-workouts-table-select",value:this.state.wid,onChange:this.handleValueChange.bind(this,"wid")},options)
+			),
+			React.createElement("td",{className:"inputok"},
+				React.createElement("input",{readOnly:true,className:"recent-workouts-table-input",value:this.state.date,onClick:this.handleDatePicker})
+			),
+			React.createElement("td",{className:"inputok"},
+				React.createElement("textarea",{className:"recent-workouts-table-textarea",value:this.state.comments,onChange:this.handleValueChange.bind(this,"comments"),onBlur:this.sendValues}),
+				React.createElement("div",{className:this.state.deletetd},
+					React.createElement("div",{className:"recent-workouts-table-delete-container"},
+						React.createElement("i",{className:"fa fa-trash-o",onClick:this.deleteEntry})
+					)
+				)
+			)
+		);
+	}
+});
+</script>
\ No newline at end of file
diff --git a/recent_workouts/class.module_recent_workouts.php b/recent_workouts/class.module_recent_workouts.php
new file mode 100644
index 0000000..b78bd75
--- /dev/null
+++ b/recent_workouts/class.module_recent_workouts.php
@@ -0,0 +1,154 @@
+<?php
+
+/**
+ * Recent Workouts Module Class
+ * Required methods, then custom methods, then variables
+ * Three variables are required (header, stylesheets, module_file)
+ * Add to required_table_structure variable and uncomment the table_verify function in the __construct method to check table structure before loading class
+ */
+
+class module_recent_workouts extends module_workout_management implements imodule {
+	
+	const MODULE_NAME = "Recent Workouts";
+	
+	public function __construct() {
+		parent::__construct();
+		if(!($this->table_verify($this->get_required_module_table_data()))){
+			self::log_error(self::MODULE_NAME." required tables verification failed.");
+		}
+		//Options: Module Name,linked module path,quick_tool_icon (font awesome icon class)
+		//Default is array ("",NULL,NULL)
+		$options = array (self::MODULE_NAME,"workout_management/",NULL);
+		if(!($this->module_check(__FILE__,$options))){
+			self::log_error(self::MODULE_NAME." module not loaded correctly.");
+		}
+		if(!($this->public_check($this->fields_marked_public,__FILE__))){
+			self::log_error(self::MODULE_NAME." public fields not correctly implemented.");
+		}
+	}
+	
+	//Start required methods
+	public function cron_tasks () {
+		//Insert any methods here to run every 30 minutes, make sure to return TRUE
+		return TRUE;
+	}
+	
+	public function get_header () {
+		//Add any extra javascript scripts/css required to header
+		$return = $this->get_default_header();
+		if((stripos($return,"</HEAD>")!==FALSE)&&(stripos($return,"</HEAD>")==strlen($return)-7)){
+			if((is_array($this->header_scripts))&&(count($this->header_scripts)>0)){
+				$return = rtrim($return,"</HEAD>");
+				foreach($this->header_scripts as $i=>$v){
+					if($v['sri']!=""){
+						$return .= "<script type='".$v['type']."' src='".$v['src']."' integrity='".$v['sri']."' crossorigin='anonymous'></script>\n";
+					}
+					else {
+						$return .= "<script type='".$v['type']."' src='".$v['src']."'></script>\n";
+					}
+				}
+				$return .= "</HEAD>";
+			}
+			$stylesheets = $this->get_stylesheets();
+			if($stylesheets!=""){
+				$return = rtrim($return,"</HEAD>");
+				$return .= $stylesheets;
+				$return .= "</HEAD>";
+			}
+		}
+		return $return;
+	}
+	
+	public function get_required_module_table_data ($type = "all") {
+		$tables = $this->required_table_structure;
+		foreach($tables as $name=>$rows){
+			//Double check blockchain tracking row and delete once found
+			if(!((isset($rows[0]))&&(($rows[0]===0)||($rows[0]===1)))){
+				unset($tables[$name]);
+				self::log_error(sprintf("Table structure data for `%s` not formatted correctly.%s",$name));
+				continue;
+			}
+			if(($rows[0]===0)&&($type!="all")){
+				unset($tables[$name]);
+			}
+			else {
+				unset($rows[0]);
+				$tables[$name] = array_values($rows);
+			}
+		}
+		return $tables;
+	}
+	
+	public function get_stylesheets () {
+		//Generate stylesheet <link>(s)
+		$return = "";
+		if(file_exists(__DIR__.DIRECTORY_SEPARATOR."style.css")){
+			$return .= "<LINK href='".ltrim(substr(__DIR__.DIRECTORY_SEPARATOR."style.css",strlen(dirname(dirname(dirname(__DIR__))))),"/")."' rel='stylesheet' type='text/css'>\n";
+		}
+		foreach($this->stylesheets as $i=>$v){
+			$return .= "<LINK href='".$v['href']."' rel='".$v['rel']."' type='".$v['type']."'>\n";
+		}
+		return $return;
+	}
+	
+	public function load_module ($get_header = NULL,$module_file = NULL,$generate_menu = FALSE) {
+		//Add 3rd parameter bool (TRUE to generate menu, FALSE or omit to not)
+		//Check to see if this has been called from child module
+		if(($get_header===NULL)||($module_file===NULL)){
+			if(!(parent::load_module($this->get_header(),__DIR__.DIRECTORY_SEPARATOR.$this->module_file,TRUE))){
+				return FALSE;
+			}
+		}
+		else {
+			if(!(parent::load_module($get_header,$module_file,$generate_menu))){
+				return FALSE;
+			}
+		}
+		return TRUE;
+	}
+	//End required methods
+	
+	//Start custom methods
+	
+	//End custom methods
+	
+	//Start required table structure data
+	//Use the following code to generate table data
+	/*
+	$data = array(0 => 0);//Change to 1 if tracked by blockchain
+	$query = "DESCRIBE `%%TABLE_NAME%%`;";
+	$result = mysqli_query($db,$query) or die(mysqli_error($db));
+	while($row = mysqli_fetch_assoc($result)){
+		$data[]=$row;
+	}
+	exit("<pre>".var_export($data)."</pre>");
+	*/
+	private $required_table_structure = array (
+		
+	);
+	//End required table structure data
+	
+	//Start public fields
+	//Fields in this area will be added to the public table so that they will be displayed in the public feed area
+	//Format of each row: 0 => array ("table" => "", "title_field" => "", "description_field" => "","denotes_public_field" => "","denotes_public_value"=>"" )
+	private $fields_marked_public = array (
+		
+	);
+	//End public fields
+	
+	//Header Variable (each row contains info linking to a javascript script used by the module and inserted into the header)
+	//Format of each row: 0 => array ("type" => "", "src" => "", "sri" => "")
+	private $header_scripts = array (
+		0 => array ("type" => "text/javascript", "src" => "js/custom/datepicker.js", "sri" => "sha384-AbJvjf2C2pgqlpImUmOmuW/mmbyLeKADFVtJynfREH6mETlFGqjxUyXLbDcVy7RY")
+	);
+	
+	//Extra stylesheet information (each row contains a stylesheet to be loaded with the module
+	//Format for each row: 0 => array ( "href" => "", "rel"=> "", "type"=>"" )
+	private $stylesheets = array (
+		0 => array ("href" => "js/custom/datepicker.css", "rel" => "stylesheet", "type" => "text/css")
+	);
+	
+	//Main file to load -- fill in and update
+	private $module_file = "recent_workouts.php";
+	
+}
\ No newline at end of file
diff --git a/recent_workouts/recent_workouts.php b/recent_workouts/recent_workouts.php
new file mode 100644
index 0000000..d95d197
--- /dev/null
+++ b/recent_workouts/recent_workouts.php
@@ -0,0 +1,106 @@
+<div class='top-bar'>
+<h1>Recent Workouts</h1>
+</div>
+<div id='content'></div>
+<script type="text/javascript">//Recent Workouts Table and Functionality
+var RecentWorkoutsTable = React.createClass({
+	loadDataFromServer: function () {
+		serverRequest({query_type:"recent_workouts_data",sorting:this.state.sorting,order:this.state.order,filter:this.state.filter},"json")
+		.then(function(data) {
+			console.log(data);
+			this.setState({workouts:data.workouts,data:data.raw});
+		}.bind(this))
+		.catch(function(error) {
+			console.error("recent_workouts.php","recent_workouts_data",error);
+		});
+	},
+	getInitialState: function() {
+		return {hash:"",workouts:[],data:[],filter:""};
+	},
+	componentDidMount: function() {
+		this.loadDataFromServer();
+		setInterval(this.loadDataFromServer, 2000);
+	},
+	shouldComponentUpdate: function (nextProps,nextState) {
+		if(JSON.stringify(nextState)==JSON.stringify(this.state)){
+			return false;
+		}
+		return true;
+	},
+	changeSorting: function (key,event) {
+		if(typeof this.state.sorting=="undefined"){
+			var toset = {};
+			toset[key]="ASC";
+			var order = [key];
+		}
+		else {
+			var toset = this.state.sorting;
+			var order = (typeof this.state.order!="undefined")?this.state.order:[];
+			if(typeof this.state.sorting[key]!="undefined"){
+				toset[key]=(this.state.sorting[key]=="ASC")?"DESC":"ASC";
+			}
+			else {
+				toset[key]="ASC";
+			}
+			if(event.shiftKey){
+				if(order.indexOf(key)<0){
+					order.push(key);
+				}
+			}
+			else {
+				for(var i in toset){
+					if(i!=key){
+						delete toset[i];
+					}
+				}
+				order = [key];
+			}
+		}
+		this.setState({sorting:toset,order:order},function () {
+			this.loadDataFromServer();
+		});
+	},
+	handleFilterChange: function (event) {
+		this.setState({filter:event.target.value});
+	},
+	render: function() {
+		var rows = [];
+		var rowtype = "odd";
+		for( var i in this.state.data){
+			rows.push(React.createElement(RecentWorkoutsTableRow,{data:this.state.data[i],key:"rtr-"+this.state.data[i].id,workouts:this.state.workouts,rowtype:rowtype}));
+			rowtype = (rowtype=="odd")?"even":"odd";
+		};
+		return React.createElement("div",null,
+			React.createElement("div",{className:"recent-workouts-table-div"},
+				React.createElement("div",{className:"recent-workouts-filter-div"},
+					React.createElement("input",{placeholder:"Search workouts...",value:this.state.filter,onChange:this.handleFilterChange})
+				),
+				(rows.length==0)?React.createElement("div",null,
+					React.createElement("span",null,"No workouts logged.")
+				):React.createElement("table",{className:"recent-workouts-table"},
+					React.createElement("thead",null,
+						React.createElement("tr",null,
+							React.createElement("th",{className:(typeof this.state.sorting!="undefined")?(typeof this.state.sorting.workout_name!="undefined")?"sortedby":"":"",onClick:this.changeSorting.bind(this,"workout_name")},"Workout Name",
+								React.createElement("i",{className:((typeof this.state.sorting!="undefined")&&(typeof this.state.sorting.workout_name!="undefined"))?"fa fa-sort-"+this.state.sorting.workout_name.toLowerCase():"fa fa-sort"})
+							),
+							React.createElement("th",{className:(typeof this.state.sorting!="undefined")?(typeof this.state.sorting.date!="undefined")?"sortedby":"":"",onClick:this.changeSorting.bind(this,"date")},"Date",
+								React.createElement("i",{className:((typeof this.state.sorting!="undefined")&&(typeof this.state.sorting.date!="undefined"))?"fa fa-sort-"+this.state.sorting.date.toLowerCase():"fa fa-sort"})
+							),
+							React.createElement("th",{className:(typeof this.state.sorting!="undefined")?(typeof this.state.sorting.comments_count!="undefined")?"sortedby":"":"",onClick:this.changeSorting.bind(this,"comments_count")},"Comments",
+								React.createElement("i",{className:((typeof this.state.sorting!="undefined")&&(typeof this.state.sorting.comments_count!="undefined"))?"fa fa-sort-"+this.state.sorting.comments_count.toLowerCase():"fa fa-sort"})
+							)
+						)
+					),
+					React.createElement("tbody",null,rows)
+				)
+			)
+		);
+	}
+});
+</script>
+<?php
+require("RecentWorkoutsTableRow/RecentWorkoutsTableRow.php");
+?>
+<script type="text/javascript">
+var ReactTable = ReactDOM.render(React.createElement(RecentWorkoutsTable,null),document.getElementById('content'));
+</script>
diff --git a/recent_workouts/recent_workouts_data.php b/recent_workouts/recent_workouts_data.php
new file mode 100644
index 0000000..820b951
--- /dev/null
+++ b/recent_workouts/recent_workouts_data.php
@@ -0,0 +1,49 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class recent_workouts_data extends module_recent_workouts implements imodquery {
+	
+	//Query: recent_workouts_data
+	
+	public function execute () {
+		$db = $this->db;
+		$tosend = array("raw"=>array(),"workouts"=>array());
+		$_POST['filter']="%".$_POST['filter']."%";
+		$sortby = ((isset($_POST['sorting']))&&(isset($_POST['order'])))?$this->build_sortby($_POST['sorting'],$_POST['order']):"";//build_sortby is from class_workout_management.php
+		$stmt = $db->prepare(sprintf("SELECT `c`.`id`,`c`.`wid`,`c`.`date`,`c`.`comments` as `comments_count`,`w`.`workout_name` FROM `workout_count` as `c` INNER JOIN `workouts` as `w` ON `c`.`wid`=`w`.`siid` WHERE `workout_name` LIKE ? OR `c`.`comments` LIKE ? %s LIMIT 100",$sortby));
+		$stmt->bind_param('ss',$_POST['filter'],$_POST['filter']);
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($id,$wid,$date,$comments,$name);
+		while($stmt->fetch()){
+			$tosend["raw"][]=array("id"=>$id,"wid"=>$wid,"date"=>$date,"comments"=>$comments,"workout_name"=>$name);
+		}
+		$stmt->close();
+		$stmt = $db->prepare("SELECT `id`,`workout_name`,`comments` FROM `workouts` WHERE 1");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($id,$name,$comments);
+		while($stmt->fetch()){
+			$tosend["workouts"][$id]=array("id"=>$id,"workout_name"=>$name,"comments"=>$comments);
+		}
+		$stmt->close();
+		echo json_encode($tosend);
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/recent_workouts/recent_workouts_delete.php b/recent_workouts/recent_workouts_delete.php
new file mode 100644
index 0000000..9e5e253
--- /dev/null
+++ b/recent_workouts/recent_workouts_delete.php
@@ -0,0 +1,32 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class recent_workouts_delete extends module_recent_workouts implements imodquery {
+	
+	//Query: recent_workouts_delete
+	
+	public function execute () {
+		$db = $this->db;
+		$stmt = $db->prepare("DELETE FROM `workout_count` WHERE `id`=? LIMIT 1");
+		$stmt->bind_param('i',$_POST['id']);
+		$content = array("DELETE","workout_count",array($_POST['id']));
+		if(!($this->database_query($stmt,$content,"DELETE",$_POST['id']))){
+			self::log_error("Database query failed.");
+			return FALSE;
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/recent_workouts/recent_workouts_edit.php b/recent_workouts/recent_workouts_edit.php
new file mode 100644
index 0000000..ef88063
--- /dev/null
+++ b/recent_workouts/recent_workouts_edit.php
@@ -0,0 +1,32 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class recent_workouts_edit extends module_recent_workouts implements imodquery {
+	
+	//Query: recent_workouts_edit
+	
+	public function execute () {
+		$db = $this->db;
+		$stmt = $db->prepare("UPDATE `workout_count` SET `wid`=?,`comments`=?,`date`=? WHERE `id`=? LIMIT 1;");
+		$stmt->bind_param('issi',$_POST['wid'],$_POST['comments'],$_POST['date'],$_POST['id']);
+		$content = array("UPDATE","workout_count",array("wid","comments","date"),array($_POST['wid'],$_POST['comments'],$_POST['date']));
+		if(!($this->database_query($stmt,$content,"UPDATE",$_POST['id']))){
+			self::log_error("Database query failed.");
+			return FALSE;
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/recent_workouts/style.css b/recent_workouts/style.css
new file mode 100644
index 0000000..7d891fb
--- /dev/null
+++ b/recent_workouts/style.css
@@ -0,0 +1,87 @@
+/*Page - Recent Workouts*/
+table.recent-workouts-table {
+	width:100%;
+	font:12px/18px Arial, Sans-serif;
+	color:#333;
+	background-color:#fff;
+	border-spacing:0;
+	margin:10px 0 15px;
+	text-align:left;
+}
+
+table.recent-workouts-table > thead > tr > th {
+	-webkit-user-select:none;
+	font:bold 12px/18px Arial, Sans-serif;
+	color:#000;
+	background-color:#fff;
+	border-collapse:collapse;
+	border-bottom:#ccc 2px solid;
+	cursor:pointer;
+	white-space:normal;
+}
+
+table.recent-workouts-table > thead > tr > th.sortedby {
+	border-bottom:#000 2px solid;
+}
+
+table.recent-workouts-table > thead > tr > th > i {
+	padding:4px;
+	float:right;
+}
+
+table.recent-workouts-table > tbody > tr > td {
+	border-bottom:#ccc 1px solid;
+	padding:4px;
+	vertical-align:top;
+}
+
+table.recent-workouts-table > tbody > tr.odd > td {
+	background-color:#dfdfdf;
+}
+
+table.recent-workouts-table > tbody > tr.even > td {
+	background-color:#efefef;
+}
+
+table.recent-workouts-table > tbody > tr:hover > td,
+table.recent-workouts-table > tbody > tr.odd > td:hover,
+table.recent-workouts-table > tbody > tr.even > td:hover {
+	background: #fff;
+	color: #000;
+}
+
+select.recent-workouts-table-select,input.recent-workouts-table-input {
+	padding:4px;
+	width:100%;
+	background-color:inherit;
+	border:none;
+	font-size:inherit;
+	font:inherit;
+	display:block;
+	box-sizing:border-box;
+}
+
+textarea.recent-workouts-table-textarea {
+	background-color:inherit;
+	border:none;
+	font-size:inherit;
+	font:inherit;
+	display:block;
+	box-sizing:border-box;
+	width:100%;
+	resize:none;
+}
+
+div.recent-workouts-table-delete-container {
+	background-color:inherit;
+	position:absolute;
+	right:-1.5px;
+	top:-46px;
+}
+
+div.recent-workouts-loading-div {
+	width:100%;
+	box-sizing:border-box;
+	padding:5%;
+	text-align:center;
+}
\ No newline at end of file
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..2bd4b6b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,157 @@
+/*Page - Workout Management */
+table#daysago {
+	border:1px solid black;
+	text-align:center;
+	border-collapse:collapse;
+	margin-top:4px;
+}
+
+div.wm-attr-manager-div {
+	
+}
+
+div.wm-attr-manager-container {
+	width:50vw;
+	position:relative;
+}
+
+div.wm-attr-manager-container > div {
+	display:block;
+	border:1px solid black;
+	box-sizing:border-box;
+	width:70%;
+	height:5vh;
+	line-height:5vh;
+	position:absolute;
+	cursor:move;
+}
+
+div.wm-attr-manager-container > div.editable > input {
+	pointer-events:auto;
+	border:2px ridge lightgreen;
+}
+
+div.wm-attr-manager-container > div > input {
+	pointer-events:none;
+	border:none;
+}
+
+div.wm-attr-manager-container > div > i {
+	display:none;
+}
+
+div.wm-attr-manager-container > div:hover > i,div.wm-attr-manager-container > div.editable > i {
+	float:right;
+	display:inline-block;
+	margin-right:5px;
+	cursor:pointer;
+}
+
+table.workout-management-table {
+	width:100%;
+	font:12px/18px Arial, Sans-serif;
+	color:#333;
+	background-color:#fff;
+	border-spacing:0;
+	margin:10px 0 15px;
+	text-align:left;
+}
+
+table.workout-management-table > thead > tr > th {
+	-webkit-user-select:none;
+	font:bold 12px/18px Arial, Sans-serif;
+	color:#000;
+	background-color:#fff;
+	border-collapse:collapse;
+	border-bottom:#ccc 2px solid;
+	cursor:pointer;
+	white-space:normal;
+}
+
+table.workout-management-table > thead > tr > th.sortedby {
+	border-bottom:#000 2px solid;
+}
+
+table.workout-management-table > thead > tr > th > i {
+	padding:4px;
+	float:right;
+}
+
+table.workout-management-table > tbody > tr > td {
+	border-bottom:#ccc 1px solid;
+	padding:4px;
+	vertical-align:top;
+}
+
+table.workout-management-table > tbody > tr.odd > td {
+	background-color:#dfdfdf;
+}
+
+table.workout-management-table > tbody > tr.even > td {
+	background-color:#efefef;
+}
+
+table.workout-management-table > tbody > tr:hover > td,
+table.workout-management-table > tbody > tr.odd > td:hover,
+table.workout-management-table > tbody > tr.even > td:hover {
+	background: #fff;
+	color: #000;
+}
+
+input.workout-management-table-input,select.workout-management-table-select {
+	padding:4px;
+	width:100%;
+	background-color:inherit;
+	border:none;
+	font-size:inherit;
+	font:inherit;
+	display:block;
+	box-sizing:border-box;
+}
+
+input.workout-management-table-input-toggles {
+	padding:4px;
+	width:30px;
+	background-color:inherit;
+	border:none;
+	font-size:inherit;
+	font:inherit;
+	display:block;
+	box-sizing:border-box;
+}
+
+textarea.workout-management-table-comments {
+	background-color:inherit;
+	border:none;
+	resize:none;
+	width:100%;
+	padding:0;
+	margin:0;
+	font-size:inherit;
+	font:inherit;
+	box-sizing:border-box;
+}
+
+td.inputok {
+	position:relative;
+}
+
+tr:hover > td.inputok > div > div.workout-management-table-delete-container {
+	display:block;
+}
+
+div.workout-management-table-delete-container {
+	display:none;
+	background-color:inherit;
+	position:absolute;
+	right:0;
+	top:0;
+	cursor:pointer;
+}
+
+div.workout-management-loading-div {
+	width:100%;
+	box-sizing:border-box;
+	padding:5%;
+	text-align:center;
+}
\ No newline at end of file
diff --git a/workout_management.php b/workout_management.php
new file mode 100644
index 0000000..02aa150
--- /dev/null
+++ b/workout_management.php
@@ -0,0 +1,57 @@
+<div class='top-bar'><h1>Workout Management</h1></div>
+<div id='content'></div>
+<script type="text/javascript">
+var WorkoutManagementContent = React.createClass({//Main React Component
+	loadDataFromServer: function () {
+		serverRequest({query_type:'workout_management_data'},"json")
+		.then(function(data) {
+			console.log(data);
+			this.setState({hash:data.hash,recent:data.recent,attributes:data.attributes});
+		}.bind(this))
+		.catch(function(error) {
+			console.error("workout_management.php","workout_management_data",error);
+		});
+	},
+	getInitialState: function() {
+		return {hash:"",recent:[],attributes:[],view:""};
+	},
+	componentDidMount: function() {
+		this.loadDataFromServer();
+		setInterval(this.loadDataFromServer, 2000);	
+	},
+	handleView: function (type) {
+		this.setState({view:type});
+	},
+	handleAddNew: function () {
+		serverRequest({query_type:'workout_management_add_new'})
+		.then(function(data) {
+			console.log(data);
+		})
+		.catch(function(error) {
+			console.error("workout_management.php","workout_management_add_new",error);
+		});
+	},
+	render: function() {
+		return React.createElement("div",null,
+			(this.state.attributes.length==0)?React.createElement("div",null,"No Workout Attributes defined. Click the 'Manage Attributes' button below."):"",
+			React.createElement(WorkoutManagementDaysAgoTableData,{data:this.state.recent,attributes:this.state.attributes}),
+			React.createElement("input",{type:"button",value:(this.state.view=="")?"Manage Attributes":"Save Attributes",onClick:this.handleView.bind(this,(this.state.view=="")?"edit":"")}),
+			(this.state.view=="edit")?React.createElement(WorkoutManagementAttributesManager,{data:this.state.attributes}):"",
+			React.createElement(WorkoutManagementWorkoutTable,{hash:this.state.hash,attributes:this.state.attributes}),
+			React.createElement("input",{type:"button",value:"Add new Workout",onClick:this.handleAddNew}),
+			React.createElement("form",{className:"link-to-page-form",method:"post"},
+				React.createElement("input",{type:"hidden",name:"page",value:"recent_workouts"}),
+				React.createElement("input",{type:"submit",className:"link-to-page-button",value:"Recent Workouts"})
+			)
+		);
+	}
+});
+</script>
+<?php
+require("WorkoutManagementDaysAgoTableData/WorkoutManagementDaysAgoTableData.php");
+require("WorkoutManagementAttributesManager/WorkoutManagementAttributesManager.php");
+require("WorkoutManagementWorkoutTable/WorkoutManagementWorkoutTable.php");
+?>
+<script type="text/javascript">
+var ReactTable = ReactDOM.render(React.createElement(WorkoutManagementContent,null),document.getElementById('content'));
+</script>
\ No newline at end of file
diff --git a/workout_management_add_attribute.php b/workout_management_add_attribute.php
new file mode 100644
index 0000000..6f6df60
--- /dev/null
+++ b/workout_management_add_attribute.php
@@ -0,0 +1,40 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_add_attribute extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_add_attribute
+	
+	public function execute () {
+		$db = $this->db;
+		$stmt = $db->prepare("SELECT IFNULL(MAX(`attribute_num`)+1,0) FROM `workouts_attributes` WHERE 1");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($max);
+		$stmt->fetch();
+		$stmt->close();
+		$stmt = $db->prepare("INSERT INTO `workouts_attributes` (attribute_num,attribute_name,attribute_label,comments) VALUES (?,'New Attribute','Attribute Label','New Attribute')");
+		$stmt->bind_param('i',$max);
+		$content = array("INSERT","workouts_attributes",array("attribute_num","attribute_name","attribute_label","comments"),array($max,"New Attribute","Attribute Label","New Attribute"));
+		if(!($this->database_query($stmt,$content,"INSERT",NULL))){
+			self::log_error("Database query failed.");
+			return FALSE;
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_add_new.php b/workout_management_add_new.php
new file mode 100644
index 0000000..c915ee1
--- /dev/null
+++ b/workout_management_add_new.php
@@ -0,0 +1,48 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_add_new extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_add_new
+	
+	public function execute () {
+		$db = $this->db;
+		$stmt = $db->prepare("SELECT RPAD('',(SELECT COUNT(*) as `count` FROM `workouts_attributes` WHERE 1),'0') FROM `workouts_attributes` WHERE 1 LIMIT 1");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($attr);
+		$stmt->fetch();
+		$stmt->close();
+		$stmt = $db->prepare("SELECT IFNULL(MAX(`siid`)+1,0) FROM `workouts` WHERE 1");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($max);
+		$stmt->fetch();
+		$stmt->close();
+		$stmt = $db->prepare("INSERT INTO `workouts` (`siid`,`workout_name`,`workout_attributes`,`comments`) VALUES (?,'New Workout',?,'New Workout')");
+		$stmt->bind_param('is',$max,$attr);
+		$content = array("INSERT","workouts",array("siid","workout_name","workout_attributes","comments"),array($max,"New Workout",$attr,"New Workout"));
+		if(!($this->database_query($stmt,$content,"INSERT",NULL))){
+			self::log_error("Database query failed.");
+			return FALSE;
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_attribute_delete.php b/workout_management_attribute_delete.php
new file mode 100644
index 0000000..f371d8d
--- /dev/null
+++ b/workout_management_attribute_delete.php
@@ -0,0 +1,86 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_attribute_delete extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_attribute_delete
+	
+	public function execute () {
+		$db = $this->db;
+		$stmt = $db->prepare("SELECT `attribute_num` FROM `workouts_attributes` WHERE `id`=?");
+		$stmt->bind_param('i',$_POST['id']);
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($num);
+		$stmt->fetch();
+		$stmt->close();
+		$stmt = $db->prepare("DELETE FROM `workouts_attributes` WHERE `id`=? LIMIT 1");
+		$stmt->bind_param('i',$_POST['id']);
+		$content = array("DELETE","workouts_attributes",$_POST['id']);
+		if(!($this->database_query($stmt,$content,"DELETE",$_POST['id']))){
+			self::log_error("Database query failed.");
+			return FALSE;
+		}
+		$stmt = $db->prepare("SELECT `id`,`attribute_num` FROM `workouts_attributes` WHERE `attribute_num`>?");
+		$stmt->bind_param('i',$num);
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($id,$attrnum);
+		$data = array();
+		while($stmt->fetch()){
+			$data[$id]=$attrnum-1;
+		}
+		$stmt->close();
+		foreach($data as $id=>$newnum){
+			$stmt = $db->prepare("UPDATE `workouts_attributes` SET `attribute_num`=? WHERE `id`=? LIMIT 1");
+			$stmt->bind_param('ii',$newnum,$id);
+			$content = array("UPDATE","workouts_attributes",array("attribute_num"),array($newnum));
+			if(!($this->database_query($stmt,$content,"UPDATE",$id))){
+				self::log_error("Database query failed.");
+				return FALSE;
+			}
+		}
+		$stmt = $db->prepare("SELECT `id`,`workout_attributes` FROM `workouts` WHERE 1");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($id,$attrs);
+		$toset = array();
+		while($stmt->fetch()){
+			$toset[$id]=str_split($attrs);
+		}
+		$stmt->close();
+		foreach($toset as $k=>$attrs){
+			unset($attrs[$num]);
+			$toset[$k]=implode("",$attrs);
+		}
+		foreach($toset as $k=>$attrs){
+			$stmt = $db->prepare("UPDATE `workouts` SET `workout_attributes`=? WHERE `id`=? LIMIT 1");
+			$stmt->bind_param('si',$attrs,$k);
+			$content = array("UPDATE","workouts",array("workout_attributes"),array($attrs));
+			if(!($this->database_query($stmt,$content,"UPDATE",$k))){
+				self::log_error("Database query failed.");
+				return FALSE;
+			}
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_attribute_save.php b/workout_management_attribute_save.php
new file mode 100644
index 0000000..7dc41df
--- /dev/null
+++ b/workout_management_attribute_save.php
@@ -0,0 +1,32 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_attribute_save extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_attribute_save
+	
+	public function execute () {
+		$db = $this->db;
+		$stmt = $db->prepare("UPDATE `workouts_attributes` SET `attribute_name`=?,`attribute_label`=?,`comments`=? WHERE `id`=? LIMIT 1");
+		$stmt->bind_param('sssi',$_POST['name'],$_POST['label'],$_POST['comments'],$_POST['id']);
+		$content = array("UPDATE","workouts_attributes",array("attribute_name","attribute_label","comments"),array($_POST['name'],$_POST['label'],$_POST['comments']));
+		if(!($this->database_query($stmt,$content,"UPDATE",$_POST['id']))){
+			self::log_error("Database query failed.");
+			return FALSE;
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_attributes_reorder.php b/workout_management_attributes_reorder.php
new file mode 100644
index 0000000..2c4496f
--- /dev/null
+++ b/workout_management_attributes_reorder.php
@@ -0,0 +1,61 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_attributes_reorder extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_attributes_reorder
+	
+	public function execute () {
+		$db = $this->db;
+		$stmt = $db->prepare("SELECT `id`,`attribute_num` FROM `workouts_attributes` WHERE 1 ORDER BY `attribute_num` ASC");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($id,$num);
+		$data = array();
+		while($stmt->fetch()){
+			$data[$num]=$id;
+		}
+		$stmt->close();
+		$initial=$data;
+		$key=$data[$_POST['attrnum']];
+		unset($data[$_POST['attrnum']]);
+		$data = array_values($data);
+		if($_POST['vd']<0){
+			$diff = ceil($_POST['vd']);
+			$pos = (($_POST['attrnum']+$diff)<0)?0:$_POST['attrnum']+$diff;
+		}
+		else {
+			$diff = floor($_POST['vd']);
+			$pos = $_POST['attrnum']+$diff;
+		}
+		array_splice($data,$pos,0,$key);
+		$data = array_values($data);
+		foreach($initial as $key=>$value){
+			if($value!=$data[$key]){
+				$stmt = $db->prepare("UPDATE `workouts_attributes` SET `attribute_num`=? WHERE `id`=? LIMIT 1");
+				$stmt->bind_param('ii',$key,$data[$key]);
+				$content = array("UPDATE","workouts_attributes",array("attribute_num"),array($key));
+				if(!($this->database_query($stmt,$content,"UPDATE",$data[$key]))){
+					self::log_error("Database query failed.");
+					return FALSE;
+				}
+			}
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_data.php b/workout_management_data.php
new file mode 100644
index 0000000..d85a847
--- /dev/null
+++ b/workout_management_data.php
@@ -0,0 +1,82 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_data extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_data
+	
+	public function execute () {
+		$db = $this->db;
+		$tosend = array("hash"=>"","recent"=>array(),"attributes"=>array());
+		$stmt = $db->prepare("SELECT md5(CONCAT_WS('%%',`c`.`id`,`c`.`wid`,`c`.`date`,`c`.`comments`,`c`.`modified`)) FROM `workout_count` as `c` WHERE 1 UNION SELECT md5(CONCAT_WS('%%',`w`.`id`,`w`.`siid`,`w`.`workout_name`,`w`.`workout_attributes`,`w`.`comments`,`w`.`modified`)) FROM `workouts` as `w` WHERE 1 UNION SELECT md5(CONCAT_WS('%%',`a`.`id`,`a`.`attribute_num`,`a`.`attribute_name`,`a`.`attribute_label`,`a`.`comments`,`a`.`modified`)) FROM `workouts_attributes` as `a` WHERE 1");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($hash);
+		while($stmt->fetch()){
+			$tosend['hash']=($tosend['hash']=="")?$hash:md5($hash.$tosend['hash']);
+		}
+		$stmt->close();
+		$stmt = $db->prepare("SELECT `id`,`attribute_num`,`attribute_name`,`attribute_label`,`comments` FROM `workouts_attributes` WHERE 1 ORDER BY `attribute_num` ASC;");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($id,$num,$name,$label,$comments);
+		while($stmt->fetch()){
+			$tosend['attributes'][$num]=array("id"=>$id,"attribute_num"=>$num,"attribute_name"=>$name,"attribute_label"=>$label,"comments"=>$comments);
+			$tosend['recent'][$num]=NULL;
+		}
+		$stmt->close();
+		$stmt = $db->prepare("SELECT `w`.`workout_attributes`,MAX(`c`.`date`) as `last_done` FROM `workouts` as `w` LEFT JOIN `workout_count` as `c` ON `w`.`siid`=`c`.`wid` WHERE 1 GROUP BY `w`.`siid` ORDER BY `last_done` DESC");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($attrs,$last);
+		while($stmt->fetch()){
+			$temp = str_split($attrs);
+			while(count($temp)<count($tosend['attributes'])){
+				$temp[]=0;
+			}
+			$i=0;
+			while($i<count($temp)){
+				if($temp[$i]==1){
+					if(isset($tosend['recent'][$i])){
+						if($tosend['recent'][$i]<$last){
+							$tosend['recent'][$i]=$last;
+						}
+				}
+					else {
+						$tosend['recent'][$i]=$last;
+					}
+				}
+				$i++;
+			}
+			unset($temp);
+		}
+		$stmt->close();
+		ksort($tosend['recent']);
+		$i=0;
+		foreach($tosend['recent'] as $key=>$val){
+			$now = time();
+			$tosend['recent'][$key] = ($val!=NULL)?floor(($now-strtotime($val))/(60*60*24)):"N/A";
+		}
+		$tosend["recent"]=$tosend['recent'];
+		echo json_encode($tosend);
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_delete.php b/workout_management_delete.php
new file mode 100644
index 0000000..2f548a5
--- /dev/null
+++ b/workout_management_delete.php
@@ -0,0 +1,53 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_delete extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_delete
+	
+	public function execute () {
+		$db = $this->db;
+		$stmt = $db->prepare("DELETE FROM `workouts` WHERE `id`=? LIMIT 1");
+		$stmt->bind_param('i',$_POST['id']);
+		$content = array("DELETE","workouts",array($_POST['id']));
+		if(!($this->database_query($stmt,$content,"DELETE",$_POST['id']))){
+			self::log_error("Database query failed.");
+			return FALSE;
+		}
+		$stmt = $db->prepare("SELECT `id` FROM `workout_count` WHERE `wid`=?");
+		$stmt->bind_param('i',$_POST['id']);
+		$todelete = array();
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($id);
+		while($stmt->fetch()){
+			$todelete[]=$id;
+		}
+		$stmt->close();
+		foreach($todelete as $i=>$id){
+			$stmt = $db->prepare("DELETE FROM `workout_count` WHERE `id`=? LIMIT 1");
+			$stmt->bind_param('i',$id);
+			$content = array("DELETE","workout_count",array($id));
+			if(!($this->database_query($stmt,$content,"DELETE",$id))){
+				self::log_error("Database query failed.");
+				return FALSE;
+			}
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_edit.php b/workout_management_edit.php
new file mode 100644
index 0000000..94b976a
--- /dev/null
+++ b/workout_management_edit.php
@@ -0,0 +1,36 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_edit extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_edit
+	
+	public function execute () {
+		$db = $this->db;
+		foreach($_POST['attributes'] as $k=>$v){
+			$_POST['attributes'][$k]=($v=="Yes")?"1":"0";
+		}
+		$_POST['attributes']=implode($_POST['attributes']);
+		$stmt = $db->prepare("UPDATE `workouts` SET `workout_attributes`=?,`workout_name`=?,`comments`=? WHERE `id`=? LIMIT 1");
+		$stmt->bind_param('sssi',$_POST['attributes'],$_POST['workout_name'],$_POST['comments'],$_POST['id']);
+		$content = array("UPDATE","workouts",array("workout_attributes","workout_name","comments"),array($_POST['attributes'],$_POST['workout_name'],$_POST['comments']));
+		if(!($this->database_query($stmt,$content,"UPDATE",$_POST['id']))){
+			self::log_error("Database query failed.");
+			return FALSE;
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_options_change.php b/workout_management_options_change.php
new file mode 100644
index 0000000..8c37432
--- /dev/null
+++ b/workout_management_options_change.php
@@ -0,0 +1,44 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_options_change extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_options_change
+	
+	public function execute () {
+		$db = $this->db;
+		if(($_POST['field']=="workout")&&($_POST['value']=="added")){
+			if(!($this->option_delete("date","workout_management"))){
+				self::log_error("Option delete failed.");
+				return FALSE;
+			}
+			if(!($this->option_delete("wid","workout_management"))){
+				self::log_error("Option delete failed.");
+				return FALSE;
+			}
+			if(!($this->option_delete("comments","workout_management"))){
+				self::log_error("Option delete failed.");
+				return FALSE;
+			}
+		}
+		else {
+			if((isset($_POST['value']))&&(isset($_POST['field']))){
+				$this->option_set($_POST['field'],$_POST['value'],"workout_management");
+			}
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_quick_tools_add_new_workout.php b/workout_management_quick_tools_add_new_workout.php
new file mode 100644
index 0000000..ba6ed13
--- /dev/null
+++ b/workout_management_quick_tools_add_new_workout.php
@@ -0,0 +1,32 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_quick_tools_add_new_workout extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_quick_tools_add_new_workout
+	
+	public function execute () {
+		$db = $this->db;
+		$stmt = $db->prepare("INSERT INTO `workout_count`(`wid`, `date`, `comments`) VALUES (?,?,?)");
+		$stmt->bind_param('iss',$_POST['wid'],$_POST['date'],$_POST['comments']);
+		$content=array("INSERT","workout_count",array("wid","date","comments"),array($_POST['wid'],$_POST['date'],$_POST['comments']));
+		if(!($this->database_query($stmt,$content,"INSERT",NULL))){
+			self::log_error("Database query failed.");
+			return FALSE;
+		}
+		echo "success";
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_quick_tools_data.php b/workout_management_quick_tools_data.php
new file mode 100644
index 0000000..6ca838f
--- /dev/null
+++ b/workout_management_quick_tools_data.php
@@ -0,0 +1,36 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_quick_tools_data extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_quick_tools_data
+	
+	public function execute () {
+		$db = $this->db;
+		$tosend = array("workouts"=>array(),"options"=>array());
+		$stmt = $db->prepare("SELECT `id`,`siid`,`workout_name`,`workout_attributes`,`comments` FROM `workouts` WHERE 1 ORDER BY `workout_name` ASC");
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($id,$siid,$name,$attrs,$comments);
+		while($stmt->fetch()){
+			$tosend["workouts"][]=array("id"=>$id,"siid"=>$siid,"workout_name"=>$name,"workout_attributes"=>$attrs,"comments"=>$comments);
+		}
+		$tosend['options']=$this->page_options_get("workout_management");
+		echo json_encode($tosend);
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file
diff --git a/workout_management_table_data.php b/workout_management_table_data.php
new file mode 100644
index 0000000..ff3cff5
--- /dev/null
+++ b/workout_management_table_data.php
@@ -0,0 +1,56 @@
+<?php
+
+$source = debug_backtrace();
+if($source[0]['file']!=dirname(dirname(__DIR__)).DIRECTORY_SEPARATOR."dbq.php"){
+	exit("Unauthorized");
+}
+
+/**
+ * Echo anything necessary from execute, but make sure it returns true otherwise it will log an error
+*/
+
+
+class workout_management_table_data extends module_workout_management implements imodquery {
+	
+	//Query: workout_management_table_data
+	
+	public function execute () {
+		$db = $this->db;
+		$tosend = array("raw"=>array(),"hash"=>"");
+		$sortby = ((isset($_POST['sorting']))&&(isset($_POST['order'])))?$this->build_sortby($_POST['sorting'],$_POST['order']):"";//build_sortby is from class_workout_management.php
+		$stmt = $db->prepare(sprintf("SELECT md5(CONCAT_WS('%%',`w`.`id`,`w`.`siid`,`w`.`workout_name`,RPAD(`w`.`workout_attributes`,(SELECT COUNT(*) FROM `workouts_attributes`),'0'),`w`.`comments`,MAX(`c`.`date`),COUNT(`c`.`wid`))) FROM `workouts` as `w` LEFT JOIN `workout_count` as `c` ON `w`.`siid`=`c`.`wid` WHERE 1 GROUP BY `w`.`siid`%s",$sortby));
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($hash);
+		while($stmt->fetch()){
+			$tosend['hash']=($tosend['hash']=="")?$hash:md5($hash.$tosend['hash']);
+		}
+		$stmt->close();
+		$stmt = $db->prepare(sprintf("SELECT `w`.`id`,`w`.`siid`,`w`.`workout_name`,RPAD(`w`.`workout_attributes`,(SELECT COUNT(*) FROM `workouts_attributes`),'0') as `workout_attributes`,`w`.`comments`,MAX(`c`.`date`) as `last_done`,COUNT(`c`.`wid`) as `times_done` FROM `workouts` as `w` LEFT JOIN `workout_count` as `c` ON `w`.`siid`=`c`.`wid` WHERE 1 GROUP BY `w`.`siid`%s",$sortby));
+		if(!($stmt->execute())){
+			self::log_error(mysqli_stmt_error($stmt));
+			return FALSE;
+		}
+		$stmt->bind_result($id,$siid,$name,$attrs,$comments,$last,$times);
+		while($stmt->fetch()){
+			$row = array("id"=>$id,"siid"=>$siid,"name"=>$name,"attrs"=>array(),"comments"=>$comments,"last"=>$last,"times"=>$times);
+			$temp = str_split($attrs);
+			for($i=0;$i<count($temp);$i++){
+				if($temp[$i]==1){
+					$row["attrs"][$i]="Yes";
+				}
+				else {
+					$row["attrs"][$i]="No";
+				}
+			}
+			$tosend['raw'][]=$row;
+		}
+		echo json_encode($tosend);
+		return TRUE;
+	}
+	
+}
+
+?>
\ No newline at end of file