--- /dev/null
+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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+<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
--- /dev/null
+<?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
--- /dev/null
+<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
--- /dev/null
+<?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
--- /dev/null
+<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>
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+/*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
--- /dev/null
+/*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
--- /dev/null
+<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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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
--- /dev/null
+<?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