]> infiniteadaptability.org Git - workouts/commitdiff
added old php files
authorAlex-Laptop <[email protected]>
Tue, 19 Dec 2017 22:06:37 +0000 (14:06 -0800)
committerAlex-Laptop <[email protected]>
Tue, 19 Dec 2017 22:06:37 +0000 (14:06 -0800)
28 files changed:
README.txt [new file with mode: 0644]
WorkoutManagementAttributesManager/AttributesManagerRow/AttributesManagerRow.php [new file with mode: 0644]
WorkoutManagementAttributesManager/WorkoutManagementAttributesManager.php [new file with mode: 0644]
WorkoutManagementDaysAgoTableData/WorkoutManagementDaysAgoTableData.php [new file with mode: 0644]
WorkoutManagementWorkoutTable/WorkoutManagementWorkoutTable.php [new file with mode: 0644]
WorkoutManagementWorkoutTable/WorkoutManagementWorkoutTableRows/WorkoutManagementWorkoutTableRows.php [new file with mode: 0644]
class.module_workout_management.php [new file with mode: 0644]
recent_workouts/RecentWorkoutsTableRow/RecentWorkoutsTableRow.php [new file with mode: 0644]
recent_workouts/class.module_recent_workouts.php [new file with mode: 0644]
recent_workouts/recent_workouts.php [new file with mode: 0644]
recent_workouts/recent_workouts_data.php [new file with mode: 0644]
recent_workouts/recent_workouts_delete.php [new file with mode: 0644]
recent_workouts/recent_workouts_edit.php [new file with mode: 0644]
recent_workouts/style.css [new file with mode: 0644]
style.css [new file with mode: 0644]
workout_management.php [new file with mode: 0644]
workout_management_add_attribute.php [new file with mode: 0644]
workout_management_add_new.php [new file with mode: 0644]
workout_management_attribute_delete.php [new file with mode: 0644]
workout_management_attribute_save.php [new file with mode: 0644]
workout_management_attributes_reorder.php [new file with mode: 0644]
workout_management_data.php [new file with mode: 0644]
workout_management_delete.php [new file with mode: 0644]
workout_management_edit.php [new file with mode: 0644]
workout_management_options_change.php [new file with mode: 0644]
workout_management_quick_tools_add_new_workout.php [new file with mode: 0644]
workout_management_quick_tools_data.php [new file with mode: 0644]
workout_management_table_data.php [new file with mode: 0644]

diff --git a/README.txt b/README.txt
new file mode 100644 (file)
index 0000000..0da445d
--- /dev/null
@@ -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 (file)
index 0000000..c113ad0
--- /dev/null
@@ -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 (file)
index 0000000..e89cc97
--- /dev/null
@@ -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 (file)
index 0000000..e31bf55
--- /dev/null
@@ -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 (file)
index 0000000..8785b46
--- /dev/null
@@ -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 (file)
index 0000000..34cad12
--- /dev/null
@@ -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 (file)
index 0000000..d5257e1
--- /dev/null
@@ -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 (file)
index 0000000..cc21535
--- /dev/null
@@ -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 (file)
index 0000000..b78bd75
--- /dev/null
@@ -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 (file)
index 0000000..d95d197
--- /dev/null
@@ -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 (file)
index 0000000..820b951
--- /dev/null
@@ -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 (file)
index 0000000..9e5e253
--- /dev/null
@@ -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 (file)
index 0000000..ef88063
--- /dev/null
@@ -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 (file)
index 0000000..7d891fb
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..02aa150
--- /dev/null
@@ -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 (file)
index 0000000..6f6df60
--- /dev/null
@@ -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 (file)
index 0000000..c915ee1
--- /dev/null
@@ -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 (file)
index 0000000..f371d8d
--- /dev/null
@@ -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 (file)
index 0000000..7dc41df
--- /dev/null
@@ -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 (file)
index 0000000..2c4496f
--- /dev/null
@@ -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 (file)
index 0000000..d85a847
--- /dev/null
@@ -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 (file)
index 0000000..2f548a5
--- /dev/null
@@ -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 (file)
index 0000000..94b976a
--- /dev/null
@@ -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 (file)
index 0000000..8c37432
--- /dev/null
@@ -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 (file)
index 0000000..ba6ed13
--- /dev/null
@@ -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 (file)
index 0000000..6ca838f
--- /dev/null
@@ -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 (file)
index 0000000..ff3cff5
--- /dev/null
@@ -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