├── js ├── index.html ├── includes │ ├── newNote.js │ ├── notes.js │ ├── dashboard.js │ ├── profile.js │ ├── viewNote.js │ ├── newTask.js │ ├── viewTask.js │ └── sign-in.js ├── custom.js ├── html5shiv.min.js ├── respond.min.js └── meowsa.min.js ├── css ├── index.html ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── meowsa.min.css ├── sign-in.css ├── datetimepicker.css └── custom.css ├── data ├── index.html ├── notes │ ├── index.html │ └── m1guelpiedrafita-13045301.txt.lock └── tasks │ └── index.html ├── images ├── index.html └── sorts │ ├── sort_asc.png │ ├── sort_both.png │ ├── sort_desc.png │ ├── sort_asc_disabled.png │ └── sort_desc_disabled.png ├── pages ├── index.html ├── newNote.php ├── newTask.php ├── notes.php ├── profile.php ├── viewNote.php ├── dashboard.php └── viewTask.php ├── includes ├── index.html ├── footer.php ├── header.php ├── sessions.php ├── config.php ├── navigation.php ├── flatfile_utils.php ├── functions.php └── flatfile.php ├── language ├── index.html ├── custom.php └── english.php ├── .eslintignore ├── .csslintrc ├── .github └── FUNDING.yml ├── .codeclimate.yml ├── ajax ├── notes_ajax.php ├── profile_ajax.php ├── tasks_ajax.php ├── viewnote_ajax.php ├── newnote_ajax.php ├── newtask_ajax.php ├── viewtask_ajax.php └── signin_ajax.php ├── index.php ├── README.md ├── .eslintrc ├── sign-in.php └── LICENSE /js/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/notes/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/tasks/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /includes/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /language/index.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | **/*{.,-}min.js 2 | -------------------------------------------------------------------------------- /data/notes/m1guelpiedrafita-13045301.txt.lock: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /css/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/css/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /images/sorts/sort_asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/images/sorts/sort_asc.png -------------------------------------------------------------------------------- /images/sorts/sort_both.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/images/sorts/sort_both.png -------------------------------------------------------------------------------- /images/sorts/sort_desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/images/sorts/sort_desc.png -------------------------------------------------------------------------------- /css/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/css/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /css/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/css/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /css/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/css/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /images/sorts/sort_asc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/images/sorts/sort_asc_disabled.png -------------------------------------------------------------------------------- /css/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/css/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /images/sorts/sort_desc_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1guelpf/Tasks/HEAD/images/sorts/sort_desc_disabled.png -------------------------------------------------------------------------------- /.csslintrc: -------------------------------------------------------------------------------- 1 | --exclude-exts=.min.css 2 | --ignore=adjoining-classes,box-model,ids,order-alphabetical,unqualified-attributes 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: m1guelpf 4 | patreon: m1guelpiedrafita 5 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | csslint: 4 | enabled: true 5 | duplication: 6 | enabled: true 7 | config: 8 | languages: 9 | - ruby 10 | - javascript 11 | - python 12 | - php 13 | eslint: 14 | enabled: true 15 | fixme: 16 | enabled: true 17 | phpmd: 18 | enabled: true 19 | ratings: 20 | paths: 21 | - "**.css" 22 | - "**.inc" 23 | - "**.js" 24 | - "**.jsx" 25 | - "**.module" 26 | - "**.php" 27 | - "**.py" 28 | - "**.rb" 29 | exclude_paths: [] 30 | -------------------------------------------------------------------------------- /includes/footer.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | '; 5 | } ?> 6 | '; 8 | } ?> 9 | 10 | '; 12 | } ?> 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /includes/header.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | <?php echo $siteName; ?> · <?php echo $pageTitle; ?> 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /ajax/notes_ajax.php: -------------------------------------------------------------------------------- 1 | datadir = '../data/notes/'; 12 | 13 | $noteid = htmlspecialchars($_POST['noteid']); 14 | 15 | define('USER_ID', 0); 16 | define('NOTE_ID', 1); 17 | define('NOTE_TITLE', 2); 18 | define('NOTE_DATE', 3); 19 | define('NOTE_TEXT', 4); 20 | define('UPDATE_DATE', 5); 21 | 22 | $db->deleteWhere('notes.txt', new AndWhereClause(new SimpleWhereClause(NOTE_ID, '=', $noteid, STRING_COMPARISON))); 23 | 24 | // Delete the Note File 25 | unlink('../data/notes/'.$noteid.'.txt'); 26 | unlink('../data/notes/'.$noteid.'.txt.lock'); 27 | 28 | // Check if the file was deleted 29 | $checkFile = '../data/notes/'.$noteid.'.txt'; 30 | 31 | if (file_exists($checkFile)) { 32 | echo '0'; // All is good! 33 | } else { 34 | echo '1'; // Nope, error... 35 | } 36 | -------------------------------------------------------------------------------- /includes/sessions.php: -------------------------------------------------------------------------------- 1 | datadir = '../data/'; 12 | 13 | define('USER_ID', 0); 14 | define('USERNAME', 1); 15 | define('PASSWORD', 2); 16 | define('USER_EMAIL', 3); 17 | define('DATE_CREATED', 4); 18 | 19 | $uid = $_SESSION['st']['userId']; 20 | $userEmail = htmlspecialchars($_POST['userEmail']); 21 | if (isset($_POST['password1']) && $_POST['password1'] != '') { 22 | $password = encodeIt($_POST['password1']); 23 | } else { 24 | $password = $_POST['old']; 25 | } 26 | $nowstamp = $_POST['now']; 27 | $now = date('Y-m-d H:i:s'); 28 | 29 | $db->updateSetWhere( 30 | 'users.txt', [ 31 | PASSWORD => $password, 32 | USER_EMAIL => $userEmail, 33 | ], 34 | new SimpleWhereClause( 35 | USER_ID, '=', $uid 36 | ) 37 | ); 38 | 39 | $checkDate = strtotime($now); 40 | 41 | if ($checkDate > $nowstamp) { 42 | echo '1'; // All is good! 43 | } else { 44 | echo '0'; // Nope, error... 45 | } 46 | -------------------------------------------------------------------------------- /ajax/tasks_ajax.php: -------------------------------------------------------------------------------- 1 | datadir = '../data/tasks/'; 12 | 13 | $taskid = htmlspecialchars($_POST['taskid']); 14 | 15 | define('USER_ID', 0); 16 | define('TASK_ID', 1); 17 | define('TASK_TITLE', 2); 18 | define('TASK_DATE', 3); 19 | define('DATE_DUE', 4); 20 | define('TASK_TYPE', 5); 21 | define('REFERENCE', 6); 22 | define('PERC_COMPLETE', 7); 23 | define('DATE_COMPLTED', 8); 24 | define('TASK_DESC', 9); 25 | define('TASK_NOTES', 10); 26 | define('TASK_STATUS', 11); 27 | define('UPDATE_DATE', 12); 28 | 29 | $db->deleteWhere('tasks.txt', new AndWhereClause(new SimpleWhereClause(TASK_ID, '=', $taskid, STRING_COMPARISON))); 30 | 31 | // Delete the Task File 32 | unlink('../data/tasks/'.$taskid.'.txt'); 33 | unlink('../data/tasks/'.$taskid.'.txt.lock'); 34 | 35 | // Check if the file was deleted 36 | $checkFile = '../data/tasks/'.$taskid.'.txt'; 37 | 38 | if (file_exists($checkFile)) { 39 | echo '0'; // All is good! 40 | } else { 41 | echo '1'; // Nope, error... 42 | } 43 | -------------------------------------------------------------------------------- /ajax/viewnote_ajax.php: -------------------------------------------------------------------------------- 1 | datadir = '../data/notes/'; 12 | 13 | define('USER_ID', 0); 14 | define('NOTE_ID', 1); 15 | define('NOTE_TITLE', 2); 16 | define('NOTE_DATE', 3); 17 | define('NOTE_TEXT', 4); 18 | define('UPDATE_DATE', 5); 19 | 20 | $noteTitle = htmlspecialchars($_POST['noteTitle']); 21 | $notesText = encodeIt($_POST['notesText']); 22 | $noteId = htmlspecialchars($_POST['noteId']); 23 | $updatDate = htmlspecialchars($_POST['updatDate']); 24 | $now = date('Y-m-d H:i:s'); 25 | 26 | $db->updateSetWhere( 27 | 'notes.txt', [ 28 | NOTE_TITLE => $noteTitle, 29 | UPDATE_DATE => $now, 30 | ], 31 | new SimpleWhereClause( 32 | NOTE_ID, '=', $noteId 33 | ) 34 | ); 35 | 36 | $db->updateSetWhere( 37 | $noteId.'.txt', [ 38 | NOTE_TEXT => $notesText, 39 | UPDATE_DATE => $now, 40 | ], 41 | new SimpleWhereClause( 42 | NOTE_ID, '=', $noteId 43 | ) 44 | ); 45 | 46 | $checkDate = strtotime($now); 47 | 48 | if ($checkDate > $updatDate) { 49 | echo '1'; // All is good! 50 | } else { 51 | echo '0'; // Nope, error... 52 | } 53 | -------------------------------------------------------------------------------- /includes/navigation.php: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /pages/newNote.php: -------------------------------------------------------------------------------- 1 | 7 |
8 | 9 | 10 |
11 |
12 |
13 |
14 |

15 |
16 |
17 | 18 |
19 |
20 |
21 | 22 |
23 |
24 |
25 | 26 | 27 | 28 |
29 |
30 | 31 | 32 |
33 | 34 | 35 |
36 |
37 |
38 |
-------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | Error — Page Not Found.'; 49 | } 50 | 51 | include 'includes/footer.php'; 52 | -------------------------------------------------------------------------------- /js/includes/newNote.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Localizations 3 | * Only translate the text between the single quotes on lines 7 through 10. 4 | * Do NOT modify any code below line 10. 5 | **/ 6 | 7 | var newNoteTitle = 'The New Note will need a Title.'; 8 | var newNoteEmpty = 'The Note can not be empty.'; 9 | var newNoteText = 'The New Note has been saved.'; 10 | var errorText = 'Looks like an unexpected error was encountered, and the New Note could not be saved at this time.'; 11 | 12 | /** END Localizations **/ 13 | 14 | jQuery(document).ready(function($) { 15 | 16 | $('#newNote').click(function(e) { 17 | e.preventDefault(); 18 | 19 | var noteTitle = $("#noteTitle").val(); 20 | var notesText = $("#notesText").val(); 21 | 22 | if (noteTitle == '') { 23 | Meowsa.addNotification({ 24 | color: 'warning', 25 | text: newNoteTitle, 26 | icon: '', 27 | timeout: 10000 28 | }); 29 | $("#noteTitle").focus(); 30 | return false; 31 | } 32 | 33 | if (notesText == '') { 34 | Meowsa.addNotification({ 35 | color: 'warning', 36 | text: newNoteEmpty, 37 | icon: '' 38 | }); 39 | $("#notesText").focus(); 40 | return false; 41 | } 42 | 43 | // Start the AJAX 44 | post_data = { 45 | 'noteTitle':noteTitle, 46 | 'notesText':notesText 47 | }; 48 | $.post('ajax/newnote_ajax.php', post_data, function(data) { 49 | if (data == '1') { 50 | // All is good! 51 | Meowsa.addNotification({ 52 | color: 'success', 53 | text: newNoteText, 54 | icon: '', 55 | timeout: 12000 56 | }); 57 | $("#noteTitle, #notesText").val(''); 58 | } else { 59 | // Unknown error 60 | Meowsa.addNotification({ 61 | color: 'danger', 62 | text: errorText, 63 | icon: '', 64 | timeout: 12000 65 | }); 66 | } 67 | }); 68 | 69 | }); 70 | 71 | }); -------------------------------------------------------------------------------- /ajax/newnote_ajax.php: -------------------------------------------------------------------------------- 1 | datadir = '../data/notes/'; 12 | 13 | define('USER_ID', 0); 14 | define('NOTE_ID', 1); 15 | define('NOTE_TITLE', 2); 16 | define('NOTE_DATE', 3); 17 | define('NOTE_TEXT', 4); 18 | define('UPDATE_DATE', 5); 19 | 20 | // Generate a RANDOM Hash 21 | $randomHash = uniqid(rand()); 22 | // Take the first 8 hash digits and use it as part of the Note's ID 23 | $randHash = substr($randomHash, 0, 8); 24 | 25 | $uid = $_SESSION['st']['userId']; 26 | $uname = $_SESSION['st']['userName']; 27 | 28 | $newnote[USER_ID] = $uid; 29 | $newnote[NOTE_ID] = $uname.'-'.$randHash; 30 | $newnote[NOTE_TITLE] = htmlspecialchars($_POST['noteTitle']); 31 | $newnote[NOTE_DATE] = date('Y-m-d H:i:s'); 32 | $newnote[NOTE_TEXT] = null; 33 | $newnote[UPDATE_DATE] = date('Y-m-d H:i:s'); 34 | 35 | $notedata[USER_ID] = $uid; 36 | $notedata[NOTE_ID] = $uname.'-'.$randHash; 37 | $notedata[NOTE_TITLE] = null; 38 | $notedata[NOTE_DATE] = null; 39 | $notedata[NOTE_TEXT] = encodeIt($_POST['notesText']); 40 | $notedata[UPDATE_DATE] = date('Y-m-d H:i:s'); 41 | 42 | // Create the Note File (username-hash.txt) 43 | $noteFile = $uname.'-'.$randHash; 44 | 45 | // Add the Note to the notes.txt file 46 | $new_task = $db->insert( 47 | 'notes.txt', 48 | $newnote 49 | ); 50 | 51 | // Create the Note file 52 | $task_data = $db->insert( 53 | $noteFile.'.txt', 54 | $notedata 55 | ); 56 | 57 | // Check if the file was created 58 | $checkFile = '../data/notes/'.$noteFile.'.txt'; 59 | 60 | if (file_exists($checkFile)) { 61 | echo '1'; // All is good! 62 | } else { 63 | echo '0'; // Nope, error... 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | # Tasks [![StyleCI Badge](https://styleci.io/repos/74145671/shield?style=flat-square&branch=master)](https://styleci.io/repos/74145671/) 4 | Simple tasks & notes manager written in PHP, jQuery and Bootstrap using a custom flat file database. 5 | 6 | ## What is Tasks? 7 | 8 | Tasks is an script that allows you to manage tasks and notes. 9 | 10 | ## Requirements: 11 | 12 | - PHP 5.5.9 or higher 13 | 14 | ## Installation: 15 | 16 | ### Deploy to Heroku: 17 | [![Deploy to Heroku](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/m1guelpf/Tasks/tree/heroku) 18 | 19 | ### Managed Install: 20 | 21 | You can purchase a managed install on [Gumroad](https://gum.co/tasks-installation). 22 | 23 | ### Manual install: 24 | - Download lastest release from [here](https://github.com/m1guelpf/Tasks/archive/master.zip). 25 | - Upload all the files to your server. 26 | - Edit site name, timezone, site URL, site email and language at includes/config.php 27 | - Access the script and create an account using the register form. 28 | - OPTIONAL: If you want a private install, change 29 | ```php 30 | $signupstatus = true; 31 | ``` 32 | to 33 | ```php 34 | $signupstatus = false; 35 | ``` 36 | to disable the signup form. 37 | - Enjoy 38 | 39 | ## Support: 40 | 41 | - If you have any problems when instaling/using the script, [open a ticket](https://support.miguelpiedrafita.com) at my support center. 42 | - If you find any error in the code, [open an issue](https://github.com/m1guelpiedrafita/Tasks/issues/new) or, if you know how to solve it, [make a pull request](https://github.com/m1guelpiedrafita/Tasks/compare). 43 | - If you have new ideas for this script, go ahead and post them in [MP Feedback](http://feedback.miguelpiedrafita.com), under the "Tasks" section. 44 | 45 | ## Credits: 46 | 47 | - [Miguel Piedrafita](https://projects.miguelpiedrafita.com) 48 | - [PHP](https://php.net) 49 | 50 | Copyright (C) Miguel Piedrafita. Use of this work is subject to Mozilla Public License 2.0 51 | -------------------------------------------------------------------------------- /js/includes/notes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Localizations 3 | * Only translate the text between the single quotes on lines 7 through 11. 4 | * Do NOT modify any code below line 11. 5 | **/ 6 | 7 | var deleteNoteTitle = 'Delete Note Confirmation'; 8 | var deleteNoteQuip = 'Are you sure you want to permentently DELETE this Note?'; 9 | var delYesOption = 'Yes, Delete It'; 10 | var cancelDelOption = 'Cancel'; 11 | var deleteError = 'An Error was encountered, and the Note could not be deleted at this time.'; 12 | 13 | /** END Localizations **/ 14 | 15 | jQuery(document).ready(function($) { 16 | 17 | $('#notes').dataTable({ 18 | "order": [1, 'desc'], 19 | "pageLength": 25 20 | }); 21 | 22 | $('#notes_wrapper').addClass('pt-5 pb-20'); 23 | $('#notes').addClass('pt-15 pb-10'); 24 | 25 | $('.deleteNote').click(function(e) { 26 | e.preventDefault(); 27 | 28 | var nid = $(this).closest("td").find("input").val(); 29 | 30 | var delNoteNotification = null; 31 | if (Meowsa.isDismissed(delNoteNotification)) { 32 | delNoteNotification = Meowsa.addNotification({ 33 | color: 'inverse', 34 | title: deleteNoteTitle, 35 | text: deleteNoteQuip, 36 | icon: '', 37 | button: ''+delYesOption+' '+cancelDelOption+'', 38 | timeout: null 39 | }); 40 | } 41 | 42 | $('.noteDelete').click(function(e) { 43 | e.preventDefault(); 44 | 45 | var noteid = $(this).closest("p").find("input").val(); 46 | 47 | // Start the AJAX 48 | post_data = { 49 | 'noteid':noteid 50 | }; 51 | $.post('ajax/notes_ajax.php', post_data, function(data) { 52 | if (data == '1') { 53 | // All is good! 54 | setTimeout(function(){ 55 | location.reload(); 56 | }, 250); 57 | } else { 58 | // Unknown error 59 | Meowsa.addNotification({ 60 | color: 'danger', 61 | text: deleteError, 62 | icon: '', 63 | timeout: 12000 64 | }); 65 | } 66 | }); 67 | }); 68 | }); 69 | 70 | }); -------------------------------------------------------------------------------- /js/includes/dashboard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Localizations 3 | * Only translate the text between the single quotes on lines 7 through 11. 4 | * Do NOT modify any code below line 11. 5 | **/ 6 | 7 | var deleteTaskTitle = 'Delete Task Confirmation'; 8 | var deleteTaskQuip = 'Are you sure you want to permentently DELETE this Task?'; 9 | var delYesOption = 'Yes, Delete It'; 10 | var cancelDelOption = 'Cancel'; 11 | var deleteError = 'An Error was encountered, and the Task could not be deleted at this time.'; 12 | 13 | /** END Localizations **/ 14 | 15 | jQuery(document).ready(function($) { 16 | 17 | $('#tasks').dataTable({ 18 | "order": [3, 'desc'], 19 | "pageLength": 25 20 | }); 21 | 22 | $('#tasks_wrapper').addClass('pt-5 pb-20'); 23 | $('#tasks').addClass('pt-15 pb-10'); 24 | 25 | $('.deleteTask').click(function(e) { 26 | e.preventDefault(); 27 | 28 | var tid = $(this).closest("td").find("input").val(); 29 | 30 | var delTaskNotification = null; 31 | if (Meowsa.isDismissed(delTaskNotification)) { 32 | delTaskNotification = Meowsa.addNotification({ 33 | color: 'inverse', 34 | title: deleteTaskTitle, 35 | text: deleteTaskQuip, 36 | icon: '', 37 | button: ''+delYesOption+' '+cancelDelOption+'', 38 | timeout: null 39 | }); 40 | } 41 | 42 | $('.taskDelete').click(function(e) { 43 | e.preventDefault(); 44 | 45 | var taskid = $(this).closest("p").find("input").val(); 46 | 47 | // Start the AJAX 48 | post_data = { 49 | 'taskid':taskid 50 | }; 51 | $.post('ajax/tasks_ajax.php', post_data, function(data) { 52 | if (data == '1') { 53 | // All is good! 54 | setTimeout(function(){ 55 | location.reload(); 56 | }, 250); 57 | } else { 58 | // Unknown error 59 | Meowsa.addNotification({ 60 | color: 'danger', 61 | text: deleteError, 62 | icon: '', 63 | timeout: 12000 64 | }); 65 | } 66 | }); 67 | }); 68 | }); 69 | 70 | }); -------------------------------------------------------------------------------- /js/includes/profile.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Localizations 3 | * Only translate the text between the single quotes on lines 7 through 10. 4 | * Do NOT modify any code below line 10. 5 | **/ 6 | 7 | var validEmailReq = 'A valid Email Address is required.'; 8 | var passNoMatch = 'New Account Passwords do not match'; 9 | var accUpdated = 'Cheer! Your Account Profile has been updated.'; 10 | var errorText = 'Whoops, looks like an unexpected error was encountered, and your Account Profile could not be updated at this time.'; 11 | 12 | /** END Localizations **/ 13 | 14 | jQuery(document).ready(function($) { 15 | 16 | $('#updProfile').click(function(e) { 17 | e.preventDefault(); 18 | 19 | var userEmail = $("#userEmail").val(); 20 | var password1 = $("#password1").val(); 21 | var password2 = $("#password2").val(); 22 | var old = $("#old").val(); 23 | var now = $("#now").val(); 24 | 25 | if (userEmail == '') { 26 | Meowsa.addNotification({ 27 | color: 'warning', 28 | text: validEmailReq, 29 | icon: '', 30 | timeout: 10000 31 | }); 32 | $("#userEmail").focus(); 33 | return false; 34 | } 35 | 36 | if (password1 != password2) { 37 | Meowsa.addNotification({ 38 | color: 'warning', 39 | text: passNoMatch, 40 | icon: '', 41 | timeout: 10000 42 | }); 43 | $("#password1, #password2").val('') 44 | return false; 45 | } 46 | 47 | // Start the AJAX 48 | post_data = { 49 | 'userEmail':userEmail, 50 | 'password1':password1, 51 | 'password2':password2, 52 | 'old':old, 53 | 'now':now 54 | }; 55 | $.post('ajax/profile_ajax.php', post_data, function(data) { 56 | if (data == '1') { 57 | // All is good! 58 | Meowsa.addNotification({ 59 | color: 'success', 60 | text: accUpdated, 61 | icon: '', 62 | timeout: 12000 63 | }); 64 | $("#password1, #password2").val('') 65 | } else { 66 | // Unknown error 67 | Meowsa.addNotification({ 68 | color: 'danger', 69 | text: errorText, 70 | icon: '', 71 | timeout: 12000 72 | }); 73 | $("#password1, #password2").val('') 74 | } 75 | }); 76 | 77 | }); 78 | 79 | }); -------------------------------------------------------------------------------- /js/custom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Localizations 3 | * Only translate the text between the single quotes on lines 7 through 10. 4 | * Do NOT modify any code below line 10. 5 | **/ 6 | 7 | var accountSignOutTitle = 'Account Sign Out'; 8 | var accountSignOutText = 'Are you sure you want to sign out of your account?'; 9 | var yesOption = 'Yes'; 10 | var cancelOption = 'Cancel'; 11 | 12 | /** END Localizations **/ 13 | 14 | function resizeTextArea($element) { 15 | $element.height("auto"); 16 | $element.height($element[0].scrollHeight); 17 | } 18 | 19 | jQuery(document).ready(function($) { 20 | /** ****************************** 21 | * Side Panel 22 | ****************************** **/ 23 | $('.side-panel-toggle').on('click', function() { 24 | $('.content').toggleClass('content-is-open'); 25 | }); 26 | 27 | /** ****************************** 28 | * Activate Tool-tips 29 | ****************************** **/ 30 | $("[data-toggle='tooltip']").tooltip(); 31 | 32 | /** ****************************** 33 | * Activate Popovers 34 | ****************************** **/ 35 | $("[data-toggle='popover']").popover(); 36 | 37 | /** ****************************** 38 | * Required Fields 39 | ****************************** **/ 40 | $("form :input[required='required']").blur(function() { 41 | if (!$(this).val()) { 42 | $(this).addClass('hasError'); 43 | } else { 44 | if ($(this).hasClass('hasError')) { 45 | $(this).removeClass('hasError'); 46 | } 47 | } 48 | }); 49 | $("form :input[required='required']").change(function() { 50 | if ($(this).hasClass('hasError')) { 51 | $(this).removeClass('hasError'); 52 | } 53 | }); 54 | 55 | /** ****************************** 56 | * Textarea Resize 57 | ****************************** **/ 58 | if ($(".autosize").length > 0) { 59 | $(".autosize").each(function () { 60 | resizeTextArea($(this)); 61 | }); 62 | } 63 | 64 | var signoutNotification = null; 65 | $('#signout').click(function(e) { 66 | e.preventDefault(); 67 | if (Meowsa.isDismissed(signoutNotification)) { 68 | signoutNotification = Meowsa.addNotification({ 69 | color: 'default', 70 | title: accountSignOutTitle, 71 | text: accountSignOutText, 72 | icon: '', 73 | button: ''+yesOption+' '+cancelOption+'', 74 | timeout: null 75 | }); 76 | } 77 | }); 78 | }); -------------------------------------------------------------------------------- /js/html5shiv.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve HTML5 Shiv 3.7.2 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed 3 | */ 4 | !function(a,b){function c(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function d(){var a=t.elements;return"string"==typeof a?a.split(" "):a}function e(a,b){var c=t.elements;"string"!=typeof c&&(c=c.join(" ")),"string"!=typeof a&&(a=a.join(" ")),t.elements=c+" "+a,j(b)}function f(a){var b=s[a[q]];return b||(b={},r++,a[q]=r,s[r]=b),b}function g(a,c,d){if(c||(c=b),l)return c.createElement(a);d||(d=f(c));var e;return e=d.cache[a]?d.cache[a].cloneNode():p.test(a)?(d.cache[a]=d.createElem(a)).cloneNode():d.createElem(a),!e.canHaveChildren||o.test(a)||e.tagUrn?e:d.frag.appendChild(e)}function h(a,c){if(a||(a=b),l)return a.createDocumentFragment();c=c||f(a);for(var e=c.frag.cloneNode(),g=0,h=d(),i=h.length;i>g;g++)e.createElement(h[g]);return e}function i(a,b){b.cache||(b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag()),a.createElement=function(c){return t.shivMethods?g(c,a,b):b.createElem(c)},a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+d().join().replace(/[\w\-:]+/g,function(a){return b.createElem(a),b.frag.createElement(a),'c("'+a+'")'})+");return n}")(t,b.frag)}function j(a){a||(a=b);var d=f(a);return!t.shivCSS||k||d.hasCSS||(d.hasCSS=!!c(a,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||i(a,d),a}var k,l,m="3.7.2",n=a.html5||{},o=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,q="_html5shiv",r=0,s={};!function(){try{var a=b.createElement("a");a.innerHTML="",k="hidden"in a,l=1==a.childNodes.length||function(){b.createElement("a");var a=b.createDocumentFragment();return"undefined"==typeof a.cloneNode||"undefined"==typeof a.createDocumentFragment||"undefined"==typeof a.createElement}()}catch(c){k=!0,l=!0}}();var t={elements:n.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output picture progress section summary template time video",version:m,shivCSS:n.shivCSS!==!1,supportsUnknownElements:l,shivMethods:n.shivMethods!==!1,type:"default",shivDocument:j,createElement:g,createDocumentFragment:h,addElements:e};a.html5=t,j(b)}(this,document); -------------------------------------------------------------------------------- /includes/flatfile_utils.php: -------------------------------------------------------------------------------- 1 | index = $index; 21 | $this->type = $type; 22 | } 23 | } 24 | 25 | class JoinColumn 26 | { 27 | public function JoinColumn($index, $tablename, $columnname) 28 | { 29 | $this->index = $index; 30 | $this->tablename = $tablename; 31 | $this->columnname = $columnname; 32 | } 33 | } 34 | 35 | class TableUtils 36 | { 37 | /* 38 | * Finds JoinColumns in an array of tables, and adds 'type' fields by looking up the columns 39 | */ 40 | public function resolveJoins(&$tables) 41 | { 42 | foreach ($tables as $tablename => $discard) { 43 | $tabledef = &$tables[$tablename]; 44 | foreach ($tabledef as $colname => $discard) { 45 | $coldef = &$tabledef[$colname]; 46 | if (is_a($coldef, 'JoinColumn') || is_subclass_of($coldef, 'JoinColumn')) { 47 | self::resolveColumnJoin($coldef, $tables); 48 | } 49 | } 50 | } 51 | } 52 | 53 | // Access private 54 | public function resolveColumnJoin(&$columndef, &$tables) 55 | { 56 | $columndef->type = $tables[$columndef->tablename][$columndef->columnname]->type; 57 | } 58 | 59 | public function createDefines(&$tables) 60 | { 61 | foreach ($tables as $tablename => $discard) { 62 | $tabledef = &$tables[$tablename]; 63 | foreach ($tabledef as $colname => $discard) { 64 | $coldef = &$tabledef[$colname]; 65 | define(strtoupper($tablename).'_'.$colname, $coldef->index); 66 | } 67 | } 68 | } 69 | 70 | /* 71 | * Creates a "row schema" for a given table definition. 72 | */ 73 | public function createRowSchema(&$tabledef) 74 | { 75 | $row_schema = []; 76 | foreach ($tabledef as $colname => $coldef) { 77 | $row_schema[$coldef->index] = $coldef->type; 78 | } 79 | 80 | return $row_schema; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /js/includes/viewNote.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Localizations 3 | * Only translate the text between the single quotes on lines 7 through 10. 4 | * Do NOT modify any code below line 10. 5 | **/ 6 | 7 | var noteTitleReq = "The Note's Title can not be empty."; 8 | var noteReq = 'The Note can not be empty.'; 9 | var noteSavedText = 'Cheer! The Note has been updated.'; 10 | var noteError = 'Whoops, looks like an unexpected error was encountered, and the Note could not be updated at this time.'; 11 | 12 | /** END Localizations **/ 13 | 14 | jQuery(document).ready(function($) { 15 | 16 | $('#pageBottom').on('click', function(e) { 17 | e.preventDefault(); 18 | $('html,body').animate({ 19 | scrollTop: $(document).height()-$(window).height() 20 | }, 500); 21 | }); 22 | 23 | $('#pageTop').on('click', function(e) { 24 | e.preventDefault(); 25 | $('html,body').animate({ 26 | scrollTop: 0 27 | }, 500); 28 | }); 29 | 30 | $('#noteSave, #saveNote').click(function(e) { 31 | e.preventDefault(); 32 | 33 | var noteTitle = $("#noteTitle").val(); 34 | var notesText = $("#notesText").val(); 35 | var nid = $("#nid").val(); 36 | var updatDate = $("#updatDate").val(); 37 | 38 | if (noteTitle == '') { 39 | Meowsa.addNotification({ 40 | color: 'warning', 41 | text: noteTitleReq, 42 | icon: '', 43 | timeout: 10000 44 | }); 45 | $("#noteTitle").focus(); 46 | return false; 47 | } 48 | 49 | if (notesText == '') { 50 | Meowsa.addNotification({ 51 | color: 'warning', 52 | text: noteReq, 53 | icon: '' 54 | }); 55 | $("#notesText").focus(); 56 | return false; 57 | } 58 | 59 | // Start the AJAX 60 | post_data = { 61 | 'noteTitle':noteTitle, 62 | 'notesText':notesText, 63 | 'noteId':nid, 64 | 'updatDate':updatDate 65 | }; 66 | $.post('ajax/viewnote_ajax.php', post_data, function(data) { 67 | if (data == '1') { 68 | // All is good! 69 | Meowsa.addNotification({ 70 | color: 'success', 71 | text: noteSavedText, 72 | icon: '', 73 | timeout: 12000 74 | }); 75 | 76 | // Resize the Text Boxes to fit the updated content 77 | $(".autosize").each(function () { 78 | resizeTextArea($(this)); 79 | }); 80 | 81 | $('html,body').animate({ 82 | scrollTop: 0 83 | }, 100); 84 | } else { 85 | // Unknown error 86 | Meowsa.addNotification({ 87 | color: 'danger', 88 | text: noteError, 89 | icon: '', 90 | timeout: 12000 91 | }); 92 | 93 | $('html,body').animate({ 94 | scrollTop: 0 95 | }, 100); 96 | } 97 | }); 98 | 99 | }); 100 | }); -------------------------------------------------------------------------------- /ajax/newtask_ajax.php: -------------------------------------------------------------------------------- 1 | datadir = '../data/tasks/'; 12 | 13 | define('USER_ID', 0); 14 | define('TASK_ID', 1); 15 | define('TASK_TITLE', 2); 16 | define('TASK_DATE', 3); 17 | define('DATE_DUE', 4); 18 | define('TASK_TYPE', 5); 19 | define('REFERENCE', 6); 20 | define('PERC_COMPLETE', 7); 21 | define('DATE_COMPLTED', 8); 22 | define('TASK_DESC', 9); 23 | define('TASK_NOTES', 10); 24 | define('TASK_STATUS', 11); 25 | define('UPDATE_DATE', 12); 26 | 27 | // Generate a RANDOM Hash 28 | $randomHash = uniqid(rand()); 29 | // Take the first 8 hash digits and use it as part of the Task's ID 30 | $randHash = substr($randomHash, 0, 8); 31 | 32 | $uid = $_SESSION['st']['userId']; 33 | $uname = $_SESSION['st']['userName']; 34 | 35 | $newtask[USER_ID] = $uid; 36 | $newtask[TASK_ID] = $uname.'-'.$randHash; 37 | $newtask[TASK_TITLE] = htmlspecialchars($_POST['taskTitle']); 38 | $newtask[TASK_DATE] = htmlspecialchars($_POST['dateAssigned']); 39 | $newtask[DATE_DUE] = htmlspecialchars($_POST['dateDue']); 40 | $newtask[TASK_TYPE] = encodeIt($_POST['taskType']); 41 | $newtask[REFERENCE] = encodeIt($_POST['taskRef']); 42 | $newtask[PERC_COMPLETE] = '0'; 43 | $newtask[DATE_COMPLTED] = null; 44 | $newtask[TASK_DESC] = null; 45 | $newtask[TASK_NOTES] = null; 46 | $newtask[TASK_STATUS] = null; 47 | $newtask[UPDATE_DATE] = date('Y-m-d H:i:s'); 48 | 49 | $taskdata[USER_ID] = $uid; 50 | $taskdata[TASK_ID] = $uname.'-'.$randHash; 51 | $taskdata[TASK_TITLE] = null; 52 | $taskdata[TASK_DATE] = null; 53 | $taskdata[DATE_DUE] = null; 54 | $taskdata[TASK_TYPE] = null; 55 | $taskdata[REFERENCE] = null; 56 | $taskdata[PERC_COMPLETE] = null; 57 | $taskdata[DATE_COMPLTED] = null; 58 | $taskdata[TASK_DESC] = encodeIt($_POST['taskDesc']); 59 | $taskdata[TASK_NOTES] = null; 60 | $taskdata[TASK_STATUS] = null; 61 | $taskdata[UPDATE_DATE] = date('Y-m-d H:i:s'); 62 | 63 | // Create the Task File (username-hash.txt) 64 | $taskFile = $uname.'-'.$randHash; 65 | 66 | // Add the Task to the tasks.txt file 67 | $new_task = $db->insert( 68 | 'tasks.txt', 69 | $newtask 70 | ); 71 | 72 | // Create the Task file 73 | $task_data = $db->insert( 74 | $taskFile.'.txt', 75 | $taskdata 76 | ); 77 | 78 | // Check if the file was created 79 | $checkFile = '../data/tasks/'.$taskFile.'.txt'; 80 | 81 | if (file_exists($checkFile)) { 82 | echo '1'; // All is good! 83 | } else { 84 | echo '0'; // Nope, error... 85 | } 86 | -------------------------------------------------------------------------------- /pages/newTask.php: -------------------------------------------------------------------------------- 1 | '; 5 | $datePicker = 'true'; 6 | $jsFile = 'newTask'; 7 | include 'includes/header.php'; 8 | ?> 9 |
10 | 11 | 12 |
13 |
14 |
15 |
16 |

17 |
18 |
19 | 20 |
21 |
22 |
23 | 24 |
25 |
26 |
27 | 28 | 29 | 30 |
31 |
32 |
33 |
34 | 35 | 36 | 37 |
38 |
39 |
40 |
41 | 42 | 43 | 44 |
45 |
46 |
47 |
48 | 49 | 50 | 51 |
52 |
53 |
54 |
55 | 56 | 57 | 58 |
59 |
60 | 61 | 62 |
63 | 64 | 65 |
66 |
67 |
68 |
-------------------------------------------------------------------------------- /js/includes/newTask.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Localizations 3 | * Only translate the text between the single quotes on lines 7 through 12. 4 | * Do NOT modify any code below line 12. 5 | **/ 6 | 7 | var taskReqText = 'The New Task will need a Title.'; 8 | var taskDateAssigned = 'The Task needs the Date Assigned.'; 9 | var taskDateDue = 'The Task needs the Date Due.'; 10 | var taskDescText = 'The Task Description is required.'; 11 | var newTaskSaved = 'The New Task has been successfully created.'; 12 | var errorText = 'Looks like an unexpected error was encountered, and the New Task could not be created at this time.'; 13 | 14 | /** END Localizations **/ 15 | 16 | jQuery(document).ready(function($) { 17 | 18 | $('#dateAssigned').datetimepicker({ 19 | format: 'yyyy-mm-dd', 20 | todayBtn: 0, 21 | autoclose: 1, 22 | todayHighlight: 1, 23 | minView: 2, 24 | forceParse: 0 25 | }); 26 | 27 | $('#dateDue').datetimepicker({ 28 | format: 'yyyy-mm-dd', 29 | todayBtn: 0, 30 | autoclose: 1, 31 | todayHighlight: 1, 32 | minView: 2, 33 | forceParse: 0 34 | }); 35 | 36 | $('#newTask').click(function(e) { 37 | e.preventDefault(); 38 | 39 | var taskTitle = $("#taskTitle").val(); 40 | var dateAssigned = $("#dateAssigned").val(); 41 | var dateDue = $("#dateDue").val(); 42 | var taskType = $("#taskType").val(); 43 | var taskRef = $("#taskRef").val(); 44 | var taskDesc = $("#taskDesc").val(); 45 | 46 | if (taskTitle == '') { 47 | Meowsa.addNotification({ 48 | color: 'warning', 49 | text: taskReqText, 50 | icon: '', 51 | timeout: 10000 52 | }); 53 | $("#taskTitle").focus(); 54 | return false; 55 | } 56 | 57 | if (dateAssigned == '') { 58 | Meowsa.addNotification({ 59 | color: 'warning', 60 | text: taskDateAssigned, 61 | icon: '' 62 | }); 63 | $("#dateAssigned").focus(); 64 | return false; 65 | } 66 | 67 | if (dateDue == '') { 68 | Meowsa.addNotification({ 69 | color: 'warning', 70 | text: taskDateDue, 71 | icon: '' 72 | }); 73 | $("#dateDue").focus(); 74 | return false; 75 | } 76 | 77 | if (taskDesc == '') { 78 | Meowsa.addNotification({ 79 | color: 'warning', 80 | text: taskDescText, 81 | icon: '' 82 | }); 83 | $("#taskDesc").focus(); 84 | return false; 85 | } 86 | 87 | // Start the AJAX 88 | post_data = { 89 | 'taskTitle':taskTitle, 90 | 'dateAssigned':dateAssigned, 91 | 'dateDue':dateDue, 92 | 'taskType':taskType, 93 | 'taskRef':taskRef, 94 | 'taskDesc':taskDesc 95 | }; 96 | $.post('ajax/newtask_ajax.php', post_data, function(data) { 97 | if (data == '1') { 98 | // All is good! 99 | Meowsa.addNotification({ 100 | color: 'success', 101 | text: newTaskSaved, 102 | icon: '', 103 | timeout: 12000 104 | }); 105 | $("#taskTitle, #dateAssigned, #dateDue, #taskType, #taskRef, #taskDesc").val(''); 106 | } else { 107 | // Unknown error 108 | Meowsa.addNotification({ 109 | color: 'danger', 110 | text: errorText, 111 | icon: '', 112 | timeout: 12000 113 | }); 114 | } 115 | }); 116 | 117 | }); 118 | 119 | }); -------------------------------------------------------------------------------- /pages/notes.php: -------------------------------------------------------------------------------- 1 | datadir = 'data/notes/'; 6 | 7 | define('USER_ID', 0); 8 | define('NOTE_ID', 1); 9 | define('NOTE_TITLE', 2); 10 | define('NOTE_DATE', 3); 11 | define('NOTE_TEXT', 4); 12 | define('UPDATE_DATE', 5); 13 | 14 | // Ge the Logged In User's Note Data 15 | $res = $db->selectWhere('notes.txt', new SimpleWhereClause(USER_ID, '=', $st_userId)); 16 | 17 | $pageTitle = $notesFieldText; 18 | $notes = 'true'; 19 | $addCss = ''; 20 | $dataTables = 'true'; 21 | $jsFile = 'notes'; 22 | include 'includes/header.php'; 23 | ?> 24 |
25 | 26 | 27 |
28 |
29 |
30 |
31 |

32 |
33 |
34 | 35 |
36 |
37 |
38 | 39 |
40 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | $v) { 54 | $noteId = $v[1]; 55 | $noteTitle = $v[2]; 56 | $noteDate = shortMonthFormat($v[3]); 57 | $updDate = shortMonthTimeFormat($v[5]); ?> 58 | 59 | 64 | 65 | 66 | 72 | 73 | 75 | 76 |
60 | 61 | 62 | 63 | 67 | 68 | 69 | 70 | 71 |
77 | 80 |
81 |
82 | 83 |
84 | 85 |
86 |

87 | 89 |
90 |
91 |
-------------------------------------------------------------------------------- /css/meowsa.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Meowsa - Growl-style Web Notifications 3 | * @version v 1.0 4 | * @date January 2016 5 | * 6 | * Copyright 2015-2016 Jennifer Perrin 7 | * Released under the MIT license 8 | */ 9 | #meowsa-wrapper{position:fixed;top:20px;right:20px;bottom:auto;left:auto;width:380px;z-index:1000;pointer-events:none}.meowsa-notification{box-shadow:0 2px 2px rgba(0,0,0,.25),0 0 15px rgba(0,0,0,.05) inset;box-sizing:border-box;color:#444;cursor:pointer;display:block;left:400px;margin:0 0 10px;opacity:0;overflow:hidden;padding:15px 15px 20px 80px;pointer-events:all;position:relative;transition:opacity .21s ease 0s,left .35s ease-in 0s,margin .35s ease 0s;width:100%}.meowsa-notification.meowsa-no-text .meowsa-text,.meowsa-notification.meowsa-no-title .meowsa-title,.meowsa-notification.not-dismissable .meowsa-close{display:none}.meowsa-notification.default{background:#f2f2f2}.meowsa-notification.primary{background:#337ab7;color:#fff}.meowsa-notification.info{background:#5bc0de;color:#fff}.meowsa-notification.success{background:#5cb85c;color:#fff}.meowsa-notification.warning{background:#f0ad4e;color:#fff}.meowsa-notification.danger{background:#d9534f;color:#fff}.meowsa-notification.inverse{background:#444;color:#fff}.meowsa-notification.meowsa-in{opacity:1;left:0;transition:opacity .28s ease-out .07s,left .35s ease-out}.meowsa-notification.no-hover:not(.not-dismissable){padding-right:95px}.meowsa-notification.no-hover:not(.not-dismissable) .meowsa-close{opacity:1;box-shadow:none}.meowsa-notification.not-dismissable{padding-right:15px}.meowsa-notification .meowsa-close{position:absolute;top:0;right:0;bottom:0;width:80px;height:100%;z-index:2;vertical-align:middle;text-align:center;opacity:0;transition:opacity .25s ease-out;cursor:pointer}.meowsa-notification .meowsa-close i{color:#333;height:18px;left:20px;pointer-events:none;position:relative;top:10px;transition:color .25s ease 0s;vertical-align:top;width:18px}.meowsa-notification .meowsa-close:hover i{color:#555}.meowsa-notification.danger .meowsa-close i,.meowsa-notification.info .meowsa-close i,.meowsa-notification.inverse .meowsa-close i,.meowsa-notification.primary .meowsa-close i,.meowsa-notification.success .meowsa-close i,.meowsa-notification.warning .meowsa-close i{color:#fff}.meowsa-notification.danger .meowsa-close:hover i,.meowsa-notification.info .meowsa-close:hover i,.meowsa-notification.inverse .meowsa-close:hover i,.meowsa-notification.primary .meowsa-close:hover i,.meowsa-notification.success .meowsa-close:hover i,.meowsa-notification.warning .meowsa-close:hover i{color:#f0f0f0}.meowsa-notification:hover .meowsa-close{opacity:1}.meowsa-notification .meowsa-title{margin:0 0 6px;font-weight:400;font-size:22px}.meowsa-notification .meowsa-title:visible+.meowsa-text:visible{margin-top:.3em}.meowsa-notification p{margin:0}.meowsa-notification .meowsa-text{margin:0;font-weight:300;font-size:16px;line-height:1.25em}.meowsa-notification .meowsa-icon{position:absolute;width:80px;top:0;bottom:0;left:0;margin-right:15px}.meowsa-notification .meowsa-icon *{position:absolute;display:block;top:50%;left:50%;width:50px;height:50px;margin-top:-25px;margin-left:-25px;line-height:50px;font-size:34px;vertical-align:middle;text-align:center}.meowsa-notification.meowsa-no-icon{padding-left:15px}.meowsa-notification.meowsa-no-icon .meowsa-icon{display:none}.btn-meowsa{margin-top:10px;font-size:12px;line-height:1.5;padding:5px 10px}@media screen and (max-width:480px){#meowsa-wrapper{top:auto;bottom:0;left:0;right:0;width:auto}#meowsa-wrapper .meowsa-notification{margin:0;padding-top:10px;padding-right:90px;padding-bottom:10px 80px;border-radius:0;box-shadow:none;border-top:1px solid #ccc}#meowsa-wrapper .meowsa-notification .meowsa-close{box-shadow:none;opacity:1}#meowsa-wrapper .meowsa-notification h3{font-size:20px}} -------------------------------------------------------------------------------- /css/sign-in.css: -------------------------------------------------------------------------------- 1 | html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym, 2 | address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center, 3 | dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details, 4 | embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video { 5 | margin: 0; 6 | padding: 0; 7 | border: 0; 8 | font-size: 100%; 9 | font: inherit; 10 | vertical-align: baseline; 11 | } 12 | 13 | article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section { 14 | display: block; 15 | } 16 | 17 | body { 18 | line-height: 1; 19 | overflow-x: hidden; 20 | } 21 | 22 | ol,ul { 23 | list-style: none; 24 | } 25 | 26 | blockquote,q { 27 | quotes: none; 28 | } 29 | 30 | blockquote:before,blockquote:after,q:before,q:after { 31 | content: ""; 32 | content: none; 33 | } 34 | 35 | a { 36 | text-decoration: none; 37 | cursor: pointer; 38 | } 39 | 40 | table { 41 | border-collapse: collapse; 42 | border-spacing: 0; 43 | } 44 | 45 | body { 46 | font-family: "Muli", sans-serif; 47 | -webkit-text-size-adjust: none; 48 | } 49 | 50 | input,button { 51 | outline: 0; 52 | } 53 | 54 | .login-signup { 55 | width: 300px; 56 | background-color: #e2e2e2; 57 | position: relative; 58 | margin: 0 auto; 59 | margin-top: 145px; 60 | } 61 | 62 | .login-signup-title { 63 | padding: 15px; 64 | text-align: center; 65 | font-size: 16px; 66 | color: #ffffff; 67 | background-color: #7fc6a4; 68 | border: 1px solid #57aa82; 69 | text-transform: uppercase; 70 | } 71 | 72 | .login-signup-content { 73 | position: relative; 74 | padding: 25px; 75 | background-color: #ffffff; 76 | border: 1px solid #e2e2e2; 77 | } 78 | 79 | .input-name { font-size: 14px; } 80 | .input-margin { margin-top: 25px; } 81 | 82 | .input-name h2 { 83 | padding-bottom: 5px; 84 | float: left; 85 | } 86 | 87 | input.field-input { 88 | width: 100%; 89 | margin-top: 0; 90 | background-color: rgba(2, 2, 2, 0.07); 91 | border: 1px solid rgba(0, 0, 0, 0.02); 92 | padding: 6px 12px; 93 | } 94 | 95 | .submit-btn { 96 | width: 100%; 97 | margin-top: 28px; 98 | text-align: center; 99 | padding: 5px 0; 100 | border-radius: 20px; 101 | background-color: #7fc6a4; 102 | color: #ffffff; 103 | border: none; 104 | transition: 0.5s cubic-bezier(0.72, 0.15, 0.53, 0.84); 105 | } 106 | .submit-btn:hover { background-color: #57aa82; } 107 | 108 | .forgot-pass { 109 | width: 100%; 110 | margin-top: 50px; 111 | border-top: 1px solid #e0e0e0; 112 | padding: 5px 0; 113 | text-align: center; 114 | } 115 | .forgot-pass a { 116 | text-align: center; 117 | font-size: 10px; 118 | color: #757575; 119 | } 120 | .forgot-pass a:hover { color: #57aa82; } 121 | 122 | #signup .login-signup-content { padding-bottom: 46px; } 123 | 124 | .s-atbottom { 125 | transform: translate3d(100px, -506.5px, 0); 126 | z-index: 0; 127 | -webkit-filter: blur(1px); 128 | filter: blur(1px); 129 | opacity: 0.6; 130 | } 131 | 132 | .s-attop { 133 | transform: translate3d(100px, -506.5px, 0); 134 | aha z-index: 1; 135 | -webkit-filter: blur(0); 136 | filter: blur(0); 137 | box-shadow: -5px 6px 48px -13px rgba(0, 0, 0, 0.75); 138 | } 139 | 140 | .l-attop { 141 | z-index: 1; 142 | -webkit-filter: blur(0); 143 | filter: blur(0); 144 | box-shadow: -5px 6px 48px -13px rgba(0, 0, 0, 0.75); 145 | } 146 | 147 | .l-atbottom { 148 | z-index: 0; 149 | -webkit-filter: blur(1px); 150 | filter: blur(1px); 151 | opacity: 0.6; 152 | } 153 | 154 | #signup,#login { transition: 0.5s cubic-bezier(0.64, 0.35, 0.58, 1); } 155 | #login { transform: translateX(-160px); } -------------------------------------------------------------------------------- /ajax/viewtask_ajax.php: -------------------------------------------------------------------------------- 1 | datadir = '../data/tasks/'; 12 | 13 | $mode = $_POST['requestType']; 14 | switch ($mode) { 15 | case 'updateData': 16 | updateData($db); 17 | break; 18 | case 'checkStatus': 19 | checkStatus( 20 | $db, 21 | $_POST['taskId'] 22 | ); 23 | break; 24 | } 25 | 26 | function updateData($db) 27 | { 28 | define('USER_ID', 0); 29 | define('TASK_ID', 1); 30 | define('TASK_TITLE', 2); 31 | define('TASK_DATE', 3); 32 | define('DATE_DUE', 4); 33 | define('TASK_TYPE', 5); 34 | define('REFERENCE', 6); 35 | define('PERC_COMPLETE', 7); 36 | define('DATE_COMPLTED', 8); 37 | define('TASK_DESC', 9); 38 | define('TASK_NOTES', 10); 39 | define('TASK_STATUS', 11); 40 | define('UPDATE_DATE', 12); 41 | 42 | $taskTitle = htmlspecialchars($_POST['taskTitle']); 43 | $dateAssigned = htmlspecialchars($_POST['dateAssigned']); 44 | $dateDue = htmlspecialchars($_POST['dateDue']); 45 | $taskType = encodeIt($_POST['taskType']); 46 | $dateComp = htmlspecialchars($_POST['dateComp']); 47 | $taskStatus = encodeIt($_POST['taskStatus']); 48 | $taskRef = encodeIt($_POST['taskRef']); 49 | $taskDesc = encodeIt($_POST['taskDesc']); 50 | $taskNotes = encodeIt($_POST['taskNotes']); 51 | $taskId = htmlspecialchars($_POST['taskId']); 52 | $updatDate = htmlspecialchars($_POST['updatDate']); 53 | $now = date('Y-m-d H:i:s'); 54 | 55 | $db->updateSetWhere( 56 | 'tasks.txt', [ 57 | TASK_TITLE => $taskTitle, 58 | TASK_DATE => $dateAssigned, 59 | DATE_DUE => $dateDue, 60 | TASK_TYPE => $taskType, 61 | REFERENCE => $taskRef, 62 | DATE_COMPLTED => $dateComp, 63 | TASK_STATUS => $taskStatus, 64 | UPDATE_DATE => $now, 65 | ], 66 | new SimpleWhereClause( 67 | TASK_ID, '=', $taskId 68 | ) 69 | ); 70 | 71 | $db->updateSetWhere( 72 | $taskId.'.txt', [ 73 | TASK_DESC => $taskDesc, 74 | TASK_NOTES => $taskNotes, 75 | TASK_STATUS => $taskStatus, 76 | UPDATE_DATE => $now, 77 | ], 78 | new SimpleWhereClause( 79 | TASK_ID, '=', $taskId 80 | ) 81 | ); 82 | 83 | $checkDate = strtotime($now); 84 | 85 | if ($checkDate > $updatDate) { 86 | echo '1'; // All is good! 87 | } else { 88 | echo '0'; // Nope, error... 89 | } 90 | } 91 | 92 | function checkStatus($db, $taskId) 93 | { 94 | define('USER_ID', 0); 95 | define('TASK_ID', 1); 96 | define('TASK_TITLE', 2); 97 | define('TASK_DATE', 3); 98 | define('DATE_DUE', 4); 99 | define('TASK_TYPE', 5); 100 | define('REFERENCE', 6); 101 | define('PERC_COMPLETE', 7); 102 | define('DATE_COMPLTED', 8); 103 | define('TASK_DESC', 9); 104 | define('TASK_NOTES', 10); 105 | define('TASK_STATUS', 11); 106 | define('UPDATE_DATE', 12); 107 | 108 | $taskdata = $db->selectWhere( 109 | 'tasks.txt', 110 | new SimpleWhereClause(TASK_ID, '=', $taskId) 111 | ); 112 | 113 | echo json_encode($taskdata); 114 | } 115 | -------------------------------------------------------------------------------- /pages/profile.php: -------------------------------------------------------------------------------- 1 | datadir = 'data/'; 6 | 7 | define('USER_ID', 0); 8 | define('USERNAME', 1); 9 | define('PASSWORD', 2); 10 | define('USER_EMAIL', 3); 11 | define('DATE_CREATED', 4); 12 | 13 | // Get the User's Account Data 14 | $user = $db->selectWhere( 15 | 'users.txt', 16 | new SimpleWhereClause(USER_ID, '=', $st_userId) 17 | ); 18 | 19 | // Set some variables to empty 20 | $uname = $old = $uemail = $createdate = ''; 21 | 22 | foreach ($user as $k => $v) { 23 | $uname = $v[1]; 24 | $old = $v[2]; 25 | $uemail = $v[3]; 26 | if ($v[4] != '') { 27 | $createdate = dateFormat($v[4]); 28 | } else { 29 | $createdate = ''; 30 | } 31 | } 32 | 33 | $pageTitle = $profilePageTitle; 34 | $profile = 'true'; 35 | $jsFile = 'profile'; 36 | include 'includes/header.php'; 37 | ?> 38 |
39 | 40 | 41 |
42 |
43 |
44 |
45 |

46 |
47 |
48 | 49 |
50 |
51 |
52 | 53 |
54 |
55 |
56 |
57 |
58 | 59 | 60 | 61 |
62 |
63 |
64 |
65 | 66 | 67 | 68 |
69 |
70 |
71 | 72 |

73 |
74 | 75 | 76 | 77 |
78 | 79 |

80 |

81 |
82 |
83 |
84 | 85 | 86 |
87 |
88 |
89 |
90 | 91 | 92 |
93 |
94 |
95 | 96 | 97 | 98 | 99 |
100 |
101 |
102 |
-------------------------------------------------------------------------------- /js/respond.min.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js v1.4.2: min/max-width media query polyfill * Copyright 2013 Scott Jehl 2 | * Licensed under https://github.com/scottjehl/Respond/blob/master/LICENSE-MIT 3 | * */ 4 | 5 | !function(a){"use strict";a.matchMedia=a.matchMedia||function(a){var b,c=a.documentElement,d=c.firstElementChild||c.firstChild,e=a.createElement("body"),f=a.createElement("div");return f.id="mq-test-1",f.style.cssText="position:absolute;top:-100em",e.style.background="none",e.appendChild(f),function(a){return f.innerHTML='­',c.insertBefore(e,d),b=42===f.offsetWidth,c.removeChild(e),{matches:b,media:a}}}(a.document)}(this),function(a){"use strict";function b(){u(!0)}var c={};a.respond=c,c.update=function(){};var d=[],e=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}(),f=function(a,b){var c=e();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))};if(c.ajax=f,c.queue=d,c.regex={media:/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi,keyframes:/@(?:\-(?:o|moz|webkit)\-)?keyframes[^\{]+\{(?:[^\{\}]*\{[^\}\{]*\})+[^\}]*\}/gi,urls:/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,findStyles:/@media *([^\{]+)\{([\S\s]+?)$/,only:/(only\s+)?([a-zA-Z]+)\s?/,minw:/\([\s]*min\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/,maxw:/\([\s]*max\-width\s*:[\s]*([\s]*[0-9\.]+)(px|em)[\s]*\)/},c.mediaQueriesSupported=a.matchMedia&&null!==a.matchMedia("only all")&&a.matchMedia("only all").matches,!c.mediaQueriesSupported){var g,h,i,j=a.document,k=j.documentElement,l=[],m=[],n=[],o={},p=30,q=j.getElementsByTagName("head")[0]||k,r=j.getElementsByTagName("base")[0],s=q.getElementsByTagName("link"),t=function(){var a,b=j.createElement("div"),c=j.body,d=k.style.fontSize,e=c&&c.style.fontSize,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",c||(c=f=j.createElement("body"),c.style.background="none"),k.style.fontSize="100%",c.style.fontSize="100%",c.appendChild(b),f&&k.insertBefore(c,k.firstChild),a=b.offsetWidth,f?k.removeChild(c):c.removeChild(b),k.style.fontSize=d,e&&(c.style.fontSize=e),a=i=parseFloat(a)},u=function(b){var c="clientWidth",d=k[c],e="CSS1Compat"===j.compatMode&&d||j.body[c]||d,f={},o=s[s.length-1],r=(new Date).getTime();if(b&&g&&p>r-g)return a.clearTimeout(h),h=a.setTimeout(u,p),void 0;g=r;for(var v in l)if(l.hasOwnProperty(v)){var w=l[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?i||t():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?i||t():1)),w.hasquery&&(z&&A||!(z||e>=x)||!(A||y>=e))||(f[w.media]||(f[w.media]=[]),f[w.media].push(m[w.rules]))}for(var C in n)n.hasOwnProperty(C)&&n[C]&&n[C].parentNode===q&&q.removeChild(n[C]);n.length=0;for(var D in f)if(f.hasOwnProperty(D)){var E=j.createElement("style"),F=f[D].join("\n");E.type="text/css",E.media=D,q.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(j.createTextNode(F)),n.push(E)}},v=function(a,b,d){var e=a.replace(c.regex.keyframes,"").match(c.regex.media),f=e&&e.length||0;b=b.substring(0,b.lastIndexOf("/"));var g=function(a){return a.replace(c.regex.urls,"$1"+b+"$2$3")},h=!f&&d;b.length&&(b+="/"),h&&(f=1);for(var i=0;f>i;i++){var j,k,n,o;h?(j=d,m.push(g(a))):(j=e[i].match(c.regex.findStyles)&&RegExp.$1,m.push(RegExp.$2&&g(RegExp.$2))),n=j.split(","),o=n.length;for(var p=0;o>p;p++)k=n[p],l.push({media:k.split("(")[0].match(c.regex.only)&&RegExp.$2||"all",rules:m.length-1,hasquery:k.indexOf("(")>-1,minw:k.match(c.regex.minw)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:k.match(c.regex.maxw)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},w=function(){if(d.length){var b=d.shift();f(b.href,function(c){v(c,b.href,b.media),o[b.href]=!0,a.setTimeout(function(){w()},0)})}},x=function(){for(var b=0;b
|' => ' ', 50 | '| |' => ' ', 51 | '|’|' => '\'', 52 | '|‘|' => '\'', 53 | '|“|' => '"', 54 | '|”|' => '"', 55 | ]; 56 | 57 | $patterns = array_keys($replacements); 58 | $replacements = array_values($replacements); 59 | 60 | // Convert double newlines to spaces. 61 | $text = preg_replace($patterns, $replacements, $text); 62 | 63 | // Remove any HTML. We only want text. 64 | $text = strip_tags($text); 65 | 66 | $out = substr($text, 0, $max); 67 | if (strpos($text, ' ') === false) { 68 | return $out.$append; 69 | } 70 | 71 | return preg_replace('/(\W)&(\W)/', '$1&$2', (preg_replace('/\W+$/', ' ', preg_replace('/\w+$/', '', $out)))).$append; 72 | } 73 | 74 | /* 75 | * Functions to Encode/Decode data 76 | * 77 | * @param string $value The text to be Encoded/Decoded 78 | * @return The Encoded/Decoded text 79 | */ 80 | // Encode Function 81 | function encodeIt($value) 82 | { 83 | return trim( 84 | base64_encode( 85 | mcrypt_encrypt( 86 | MCRYPT_RIJNDAEL_256, 87 | PEPPER, 88 | $value, 89 | MCRYPT_MODE_ECB, 90 | mcrypt_create_iv( 91 | mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), 92 | MCRYPT_RAND 93 | ) 94 | ) 95 | ) 96 | ); 97 | } 98 | 99 | // Decode Function 100 | function decodeIt($value) 101 | { 102 | return trim( 103 | mcrypt_decrypt( 104 | MCRYPT_RIJNDAEL_256, 105 | PEPPER, 106 | base64_decode($value), 107 | MCRYPT_MODE_ECB, 108 | mcrypt_create_iv( 109 | mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), 110 | MCRYPT_RAND 111 | ) 112 | ) 113 | ); 114 | } 115 | 116 | /* 117 | * Function to only allow Alphanumeric characters and dash (-) 118 | * 119 | * @param string $val The string to be stripped 120 | * @return The stripped text 121 | */ 122 | function alphaNum($val) 123 | { 124 | $val = trim($val); 125 | $val = html_entity_decode($val); 126 | $val = strip_tags($val); 127 | $val = preg_replace('~[^ a-zA-Z0-9_.]~', ' ', $val); 128 | $val = preg_replace('~ ~', '-', $val); 129 | $val = preg_replace('~-+~', '-', $val); 130 | 131 | return $val; 132 | } 133 | -------------------------------------------------------------------------------- /js/meowsa.min.js: -------------------------------------------------------------------------------- 1 | /**! 2 | * Meowsa - Growl-style Web Notifications 3 | * @version v 1.0 4 | * @date January 2016 5 | * 6 | * Copyright 2015-2016 Jennifer Perrin 7 | * Released under the MIT license 8 | **/ 9 | !function(t,i){"use strict";function e(){var t="",i="abcdefghijklmnopqrstuvwxyz0123456789";do{t="";for(var e=0;5>e;e++)t+=i.charAt(Math.floor(Math.random()*i.length))}while(m.exists(t));return t}function n(i){var e=i.target?i.target:i.toElement;if("meowsa-wrapper"!==e.getAttribute("id")){for(var n=!1;!c(e,"meowsa-notification");)c(e,"meowsa-close")&&(n=!0),c(e,"btn-close-notification")&&(n=!0),e=e.parentElement;var o=e.getAttribute("id");if(o=/meowsa-notification-([a-zA-Z0-9]+)/.exec(o)[1],n&&m.notifications[o].options.dismissable)m.removeNotification(o);else{var a=m.notifications[o].action;if(void 0===a||null===a)return;"string"==typeof a?t.location=a:"function"==typeof a?a(o):(console.log("Meowsa Error: Invalid click action:"),console.log(a))}}}function o(t){if(void 0===m.notifications[t]&&(m.notifications[t]={}),null===m.notifications[t].element||void 0===m.notifications[t].element){var e=u.cloneNode(!0);s(e,"meowsa-notification"),e.setAttribute("id","meowsa-notification-"+t),m.notifications[t].element=e}if(null===m.notifications[t].element.parentElement){var n=i.getElementById("meowsa-wrapper");i.body.clientWidth>480?n.appendChild(m.notifications[t].element):n.insertBefore(m.notifications[t].element,n.firstChild)}}function a(t,i){var e,n={};for(e in t)n[e]=t[e];for(e in i)n[e]=i[e];return n}function s(t,i){c(t,i)||(t.className+=" "+i)}function c(t,i){var e=new RegExp("(?:^|\\s)"+i+"(?!\\S)","g");return null!==t.className.match(e)}function r(t,i){var e=new RegExp("(?:^|\\s)"+i+"(?!\\S)","g");t.className=t.className.replace(e,"")}function l(){return"ontouchstart"in t||"onmsgesturechange"in t}function f(){var t=i.createElement("div");t.setAttribute("id","meowsa-wrapper"),t.addEventListener("click",n),i.body.appendChild(t),m.setNotificationHTML('

')}var m=m||{};m={count:0,notifications:{},defaultOptions:{color:"default",title:"",text:"",icon:"",timeout:5e3,action:null,button:null,dismissable:!0},setDefaultOptions:function(t){m.defaultOptions=a(m.defaultOptions,t)},setNotificationHTML:function(t){var e=i.createElement("div");e.innerHTML=t;var n=e.firstChild;e.removeChild(n),u=n},addNotification:function(t){m.count+=1;var i=e();return o(i),m.editNotification(i,t),i},editNotification:function(t,i){null!==m.notifications[t].removeTimer&&(clearTimeout(m.notifications[t].removeTimer),m.notifications[t].removeTimer=null),o(t),i=i||{},void 0===m.notifications[t].options&&(m.notifications[t].options=m.defaultOptions),i=a(m.notifications[t].options,i);var e=m.notifications[t].element;s(e,i.color);var n=e.getElementsByClassName("meowsa-title")[0];i.title?(n.textContent=i.title,r(e,"meowsa-no-title")):(n.textContent="",s(e,"meowsa-no-title"));var c=e.getElementsByClassName("meowsa-text")[0];i.text?(c.textContent=i.text,r(e,"meowsa-no-text")):(c.textContent="",s(e,"meowsa-no-text"));var f=e.getElementsByClassName("meowsa-icon")[0];i.icon?(f.innerHTML=i.icon,r(e,"meowsa-no-icon")):(f.innerHTML="",s(e,"meowsa-no-icon")),null!==i.timer&&clearTimeout(m.notifications[t].timer);var u=null;null!==i.timeout&&(u=setTimeout(function(){m.removeNotification(t)},i.timeout)),m.notifications[t].timer=u,m.notifications[t].action=i.action;var d=e.getElementsByClassName("meowsa-button")[0];i.button?d.innerHTML=i.button:d.innerHTML="",i.dismissable?r(e,"not-dismissable"):s(e,"not-dismissable"),setTimeout(function(){s(e,"meowsa-in"),e.removeAttribute("style")},0),l()&&s(e,"no-hover"),m.notifications[t].options=i},reOpenNotification:function(t){m.editNotification(t)},removeNotification:function(t){if(m.isDismissed(t))return!1;var e=m.notifications[t].element;return r(e,"meowsa-in"),i.body.clientWidth>480?e.style.marginBottom=-e.offsetHeight+"px":e.style.marginTop=-e.offsetHeight+"px",m.notifications[t].removeTimer=setTimeout(function(){e.parentElement.removeChild(e)},500),clearTimeout(m.notifications[t].timer),!0},isDismissed:function(t){return m.exists(t)?null===m.notifications[t].element.parentElement:!0},exists:function(t){return void 0!==m.notifications[t]},setTitle:function(t,i){m.editNotification(t,{title:i})},setText:function(t,i){m.editNotification(t,{text:i})},setIcon:function(t,i){m.editNotification(t,{icon:i})},setTimeout:function(t,i){m.editNotification(t,{timeout:i})}};var u=null;!function(){"complete"===i.readyState||"interactive"===i.readyState&&i.body?f():i.addEventListener?i.addEventListener("DOMContentLoaded",function(){i.removeEventListener("DOMContentLoaded",null,!1),f()},!1):i.attachEvent&&i.attachEvent("onreadystatechange",function(){"complete"===i.readyState&&(i.detachEvent("onreadystatechange",null),f())})}(),t.Meowsa=m}(window,document); -------------------------------------------------------------------------------- /pages/viewNote.php: -------------------------------------------------------------------------------- 1 | datadir = 'data/notes/'; 8 | 9 | define('USER_ID', 0); 10 | define('NOTE_ID', 1); 11 | define('NOTE_TITLE', 2); 12 | define('NOTE_DATE', 3); 13 | define('NOTE_TEXT', 4); 14 | define('UPDATE_DATE', 5); 15 | 16 | // Get the Note Data 17 | $note = $db->selectWhere( 18 | 'notes.txt', 19 | new SimpleWhereClause(NOTE_ID, '=', $noteId) 20 | ); 21 | $notedata = $db->selectAll($noteId.'.txt'); 22 | 23 | // Set some variables to empty 24 | $noteTitle = $noteDate = $noteText = $lastUpd = ''; 25 | 26 | foreach ($note as $k => $v) { 27 | $noteTitle = $v[2]; 28 | if ($v[3] != '') { 29 | $noteDate = dateFormat($v[3]); 30 | } else { 31 | $dateFormat = ''; 32 | } 33 | if ($v[5] != '') { 34 | $updated = dateFormat($v[5]); 35 | } else { 36 | $updated = ''; 37 | } 38 | if ($v[5] != '') { 39 | $lastUpd = dbDateFormat($v[5]); 40 | } else { 41 | $lastUpd = ''; 42 | } 43 | } 44 | 45 | foreach ($notedata as $k => $v) { 46 | if ($v[4] != '') { 47 | $noteText = decodeIt($v[4]); 48 | } else { 49 | $noteText = ''; 50 | } 51 | } 52 | 53 | $pageTitle = $viewNotePageTitle; 54 | $jsFile = 'viewNote'; 55 | include 'includes/header.php'; 56 | ?> 57 |
58 | 59 | 60 |
61 |
62 |
63 |
64 |

65 |
66 |
67 | 68 |
69 |
70 |
71 | 72 |
73 |
74 |
75 |
76 |
77 | 78 | 79 | 80 |
81 |
82 |
83 |

84 | 85 | 86 |

87 |
88 |
89 |
90 |
91 |
92 | 93 | 94 |
95 |
96 |
97 |
98 | 99 | 100 |
101 |
102 |
103 |
104 | 105 | 106 |
107 | 108 |
109 |
110 | 111 | 112 | 113 |
114 |
115 |

116 | 117 |

118 |
119 |
120 |
121 |
122 |
123 |
-------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | ecmaFeatures: 2 | modules: true 3 | jsx: true 4 | 5 | env: 6 | amd: true 7 | browser: true 8 | es6: true 9 | jquery: true 10 | node: true 11 | 12 | # http://eslint.org/docs/rules/ 13 | rules: 14 | # Possible Errors 15 | comma-dangle: [2, never] 16 | no-cond-assign: 2 17 | no-console: 0 18 | no-constant-condition: 2 19 | no-control-regex: 2 20 | no-debugger: 2 21 | no-dupe-args: 2 22 | no-dupe-keys: 2 23 | no-duplicate-case: 2 24 | no-empty: 2 25 | no-empty-character-class: 2 26 | no-ex-assign: 2 27 | no-extra-boolean-cast: 2 28 | no-extra-parens: 0 29 | no-extra-semi: 2 30 | no-func-assign: 2 31 | no-inner-declarations: [2, functions] 32 | no-invalid-regexp: 2 33 | no-irregular-whitespace: 2 34 | no-negated-in-lhs: 2 35 | no-obj-calls: 2 36 | no-regex-spaces: 2 37 | no-sparse-arrays: 2 38 | no-unexpected-multiline: 2 39 | no-unreachable: 2 40 | use-isnan: 2 41 | valid-jsdoc: 0 42 | valid-typeof: 2 43 | 44 | # Best Practices 45 | accessor-pairs: 2 46 | block-scoped-var: 0 47 | complexity: [2, 6] 48 | consistent-return: 0 49 | curly: 0 50 | default-case: 0 51 | dot-location: 0 52 | dot-notation: 0 53 | eqeqeq: 2 54 | guard-for-in: 2 55 | no-alert: 2 56 | no-caller: 2 57 | no-case-declarations: 2 58 | no-div-regex: 2 59 | no-else-return: 0 60 | no-empty-label: 2 61 | no-empty-pattern: 2 62 | no-eq-null: 2 63 | no-eval: 2 64 | no-extend-native: 2 65 | no-extra-bind: 2 66 | no-fallthrough: 2 67 | no-floating-decimal: 0 68 | no-implicit-coercion: 0 69 | no-implied-eval: 2 70 | no-invalid-this: 0 71 | no-iterator: 2 72 | no-labels: 0 73 | no-lone-blocks: 2 74 | no-loop-func: 2 75 | no-magic-number: 0 76 | no-multi-spaces: 0 77 | no-multi-str: 0 78 | no-native-reassign: 2 79 | no-new-func: 2 80 | no-new-wrappers: 2 81 | no-new: 2 82 | no-octal-escape: 2 83 | no-octal: 2 84 | no-proto: 2 85 | no-redeclare: 2 86 | no-return-assign: 2 87 | no-script-url: 2 88 | no-self-compare: 2 89 | no-sequences: 0 90 | no-throw-literal: 0 91 | no-unused-expressions: 2 92 | no-useless-call: 2 93 | no-useless-concat: 2 94 | no-void: 2 95 | no-warning-comments: 0 96 | no-with: 2 97 | radix: 2 98 | vars-on-top: 0 99 | wrap-iife: 2 100 | yoda: 0 101 | 102 | # Strict 103 | strict: 0 104 | 105 | # Variables 106 | init-declarations: 0 107 | no-catch-shadow: 2 108 | no-delete-var: 2 109 | no-label-var: 2 110 | no-shadow-restricted-names: 2 111 | no-shadow: 0 112 | no-undef-init: 2 113 | no-undef: 0 114 | no-undefined: 0 115 | no-unused-vars: 0 116 | no-use-before-define: 0 117 | 118 | # Node.js and CommonJS 119 | callback-return: 2 120 | global-require: 2 121 | handle-callback-err: 2 122 | no-mixed-requires: 0 123 | no-new-require: 0 124 | no-path-concat: 2 125 | no-process-exit: 2 126 | no-restricted-modules: 0 127 | no-sync: 0 128 | 129 | # Stylistic Issues 130 | array-bracket-spacing: 0 131 | block-spacing: 0 132 | brace-style: 0 133 | camelcase: 0 134 | comma-spacing: 0 135 | comma-style: 0 136 | computed-property-spacing: 0 137 | consistent-this: 0 138 | eol-last: 0 139 | func-names: 0 140 | func-style: 0 141 | id-length: 0 142 | id-match: 0 143 | indent: 0 144 | jsx-quotes: 0 145 | key-spacing: 0 146 | linebreak-style: 0 147 | lines-around-comment: 0 148 | max-depth: 0 149 | max-len: 0 150 | max-nested-callbacks: 0 151 | max-params: 0 152 | max-statements: [2, 30] 153 | new-cap: 0 154 | new-parens: 0 155 | newline-after-var: 0 156 | no-array-constructor: 0 157 | no-bitwise: 0 158 | no-continue: 0 159 | no-inline-comments: 0 160 | no-lonely-if: 0 161 | no-mixed-spaces-and-tabs: 0 162 | no-multiple-empty-lines: 0 163 | no-negated-condition: 0 164 | no-nested-ternary: 0 165 | no-new-object: 0 166 | no-plusplus: 0 167 | no-restricted-syntax: 0 168 | no-spaced-func: 0 169 | no-ternary: 0 170 | no-trailing-spaces: 0 171 | no-underscore-dangle: 0 172 | no-unneeded-ternary: 0 173 | object-curly-spacing: 0 174 | one-var: 0 175 | operator-assignment: 0 176 | operator-linebreak: 0 177 | padded-blocks: 0 178 | quote-props: 0 179 | quotes: 0 180 | require-jsdoc: 0 181 | semi-spacing: 0 182 | semi: 0 183 | sort-vars: 0 184 | space-after-keywords: 0 185 | space-before-blocks: 0 186 | space-before-function-paren: 0 187 | space-before-keywords: 0 188 | space-in-parens: 0 189 | space-infix-ops: 0 190 | space-return-throw-case: 0 191 | space-unary-ops: 0 192 | spaced-comment: 0 193 | wrap-regex: 0 194 | 195 | # ECMAScript 6 196 | arrow-body-style: 0 197 | arrow-parens: 0 198 | arrow-spacing: 0 199 | constructor-super: 0 200 | generator-star-spacing: 0 201 | no-arrow-condition: 0 202 | no-class-assign: 0 203 | no-const-assign: 0 204 | no-dupe-class-members: 0 205 | no-this-before-super: 0 206 | no-var: 0 207 | object-shorthand: 0 208 | prefer-arrow-callback: 0 209 | prefer-const: 0 210 | prefer-reflect: 0 211 | prefer-spread: 0 212 | prefer-template: 0 213 | require-yield: 0 214 | -------------------------------------------------------------------------------- /sign-in.php: -------------------------------------------------------------------------------- 1 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | <?php echo $siteName; ?> · <?php echo $signInPageTitle; ?> 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 42 | 43 | 44 | 45 | 46 |
47 | 48 | '; 50 | } ?> 51 |
52 |

53 | 54 | 55 |

56 | 57 | 58 | 59 | 60 |
61 | 62 |
63 |
64 |
65 | '; 67 | } ?> 68 | 69 | 71 |
72 |
73 |
74 |

75 | 76 | 77 |

78 | 79 | 80 |

81 | 82 | 83 | 84 | 85 |
86 |
87 | '; 88 | } ?> 89 | 90 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /pages/dashboard.php: -------------------------------------------------------------------------------- 1 | datadir = 'data/tasks/'; 6 | 7 | define('USER_ID', 0); 8 | define('TASK_ID', 1); 9 | define('TASK_TITLE', 2); 10 | define('TASK_DATE', 3); 11 | define('DATE_DUE', 4); 12 | define('TASK_TYPE', 5); 13 | define('REFERENCE', 6); 14 | define('PERC_COMPLETE', 7); 15 | define('DATE_COMPLTED', 8); 16 | define('TASK_DESC', 9); 17 | define('TASK_NOTES', 10); 18 | define('TASK_STATUS', 11); 19 | define('UPDATE_DATE', 12); 20 | 21 | // Ge the Logged In User's Task Data 22 | $res = $db->selectWhere('tasks.txt', new SimpleWhereClause(USER_ID, '=', $st_userId)); 23 | 24 | $pageTitle = $myTasksText; 25 | $home = 'true'; 26 | $addCss = ''; 27 | $dataTables = 'true'; 28 | $jsFile = 'dashboard'; 29 | include 'includes/header.php'; 30 | ?> 31 |
32 | 33 | 34 |
35 |
36 |
37 |
38 |

39 |
40 |
41 | 42 |
43 |
44 |
45 | 46 |
47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | $v) { 65 | $taskBg = ''; 66 | 67 | $taskId = $v[1]; 68 | $taskTitle = $v[2]; 69 | $taskDate = shortMonthFormat($v[3]); 70 | $dateDue = shortMonthFormat($v[4]); 71 | if ($v[5] != '') { 72 | $taskType = decodeIt($v[5]); 73 | } else { 74 | $taskType = ''; 75 | } 76 | $taskRef = $v[6]; 77 | $percComp = $v[7]; 78 | if ($v[8] != '') { 79 | $dateComp = shortMonthFormat($v[8]); 80 | } else { 81 | $dateComp = ''; 82 | $taskBg = 'indev'; 83 | } 84 | if ($v[11] != '') { 85 | $taskStatus = decodeIt($v[11]); 86 | } else { 87 | $taskStatus = ''; 88 | } 89 | $updDate = shortMonthTimeFormat($v[12]); ?> 90 | 91 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 108 | 109 | 111 | 112 |
92 | 93 | 94 | 95 | 103 | 104 | 105 | 106 | 107 |
113 | 116 |
117 |
118 | 119 |
120 | 121 |
122 |

123 | 125 |
126 |
127 |
-------------------------------------------------------------------------------- /js/includes/viewTask.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Localizations 3 | * Only translate the text between the single quotes on lines 7 through 10. 4 | * Do NOT modify any code below line 10. 5 | **/ 6 | 7 | var taskTitleReq = "The Task's Title can not be empty."; 8 | var assignedDateReq = "The Task's Assigned Date can not be empty."; 9 | var dueDateReq = "The Task's Due Date can not be empty."; 10 | var taskDescReq = "The Task's Description can not be empty."; 11 | var taskUpdatedMsg = 'Cheer! The Task has been updated.'; 12 | var updateError = 'Whoops, looks like an unexpected error was encountered, and the Task could not be updated at this time.'; 13 | var taskCompOnText = 'Completed Task on'; 14 | 15 | /** END Localizations **/ 16 | 17 | jQuery(document).ready(function($) { 18 | 19 | loadStatus(); 20 | 21 | $('#dateAssigned').datetimepicker({ 22 | format: 'yyyy-mm-dd', 23 | todayBtn: 0, 24 | autoclose: 1, 25 | todayHighlight: 1, 26 | minView: 2, 27 | forceParse: 0 28 | }); 29 | 30 | $('#dateDue').datetimepicker({ 31 | format: 'yyyy-mm-dd', 32 | todayBtn: 0, 33 | autoclose: 1, 34 | todayHighlight: 1, 35 | minView: 2, 36 | forceParse: 0 37 | }); 38 | 39 | $('#dateComp').datetimepicker({ 40 | format: 'yyyy-mm-dd', 41 | todayBtn: 0, 42 | autoclose: 1, 43 | todayHighlight: 1, 44 | minView: 2, 45 | forceParse: 0 46 | }); 47 | 48 | $('#pageBottom').on('click', function(e) { 49 | e.preventDefault(); 50 | $('html,body').animate({ 51 | scrollTop: $(document).height()-$(window).height() 52 | }, 500); 53 | }); 54 | 55 | $('#pageTop').on('click', function(e) { 56 | e.preventDefault(); 57 | $('html,body').animate({ 58 | scrollTop: 0 59 | }, 500); 60 | }); 61 | 62 | $('#taskSave, #saveTask').click(function(e) { 63 | e.preventDefault(); 64 | 65 | var taskTitle = $("#taskTitle").val(); 66 | var dateAssigned = $("#dateAssigned").val(); 67 | var dateDue = $("#dateDue").val(); 68 | var taskType = $("#taskType").val(); 69 | var dateComp = $("#dateComp").val(); 70 | var taskStatus = $("#taskStatus").val(); 71 | var taskRef = $("#taskRef").val(); 72 | var taskDesc = $("#taskDesc").val(); 73 | var taskNotes = $("#taskNotes").val(); 74 | var tid = $("#tid").val(); 75 | var updatDate = $("#updatDate").val(); 76 | 77 | if (taskTitle == '') { 78 | Meowsa.addNotification({ 79 | color: 'warning', 80 | text: taskTitleReq, 81 | icon: '', 82 | timeout: 10000 83 | }); 84 | $("#taskTitle").focus(); 85 | return false; 86 | } 87 | 88 | if (dateAssigned == '') { 89 | Meowsa.addNotification({ 90 | color: 'warning', 91 | text: assignedDateReq, 92 | icon: '' 93 | }); 94 | $("#dateAssigned").focus(); 95 | return false; 96 | } 97 | 98 | if (dateDue == '') { 99 | Meowsa.addNotification({ 100 | color: 'warning', 101 | text: dueDateReq, 102 | icon: '' 103 | }); 104 | $("#dateDue").focus(); 105 | return false; 106 | } 107 | 108 | if (taskDesc == '') { 109 | Meowsa.addNotification({ 110 | color: 'warning', 111 | text: taskDescReq, 112 | icon: '' 113 | }); 114 | $("#taskDesc").focus(); 115 | return false; 116 | } 117 | 118 | // Start the AJAX 119 | post_data = { 120 | 'requestType':'updateData', 121 | 'taskTitle':taskTitle, 122 | 'dateAssigned':dateAssigned, 123 | 'dateDue':dateDue, 124 | 'taskType':taskType, 125 | 'dateComp':dateComp, 126 | 'taskStatus':taskStatus, 127 | 'taskRef':taskRef, 128 | 'taskDesc':taskDesc, 129 | 'taskNotes':taskNotes, 130 | 'taskId':tid, 131 | 'updatDate':updatDate 132 | }; 133 | $.post('ajax/viewtask_ajax.php', post_data, function(data) { 134 | if (data == '1') { 135 | // All is good! 136 | Meowsa.addNotification({ 137 | color: 'success', 138 | text: taskUpdatedMsg, 139 | icon: '', 140 | timeout: 12000 141 | }); 142 | 143 | loadStatus(); 144 | 145 | // Resize the Text Boxes to fit the updated content 146 | $(".autosize").each(function () { 147 | resizeTextArea($(this)); 148 | }); 149 | 150 | $('html,body').animate({ 151 | scrollTop: 0 152 | }, 100); 153 | } else { 154 | // Unknown error 155 | Meowsa.addNotification({ 156 | color: 'danger', 157 | text: updateError, 158 | icon: '', 159 | timeout: 12000 160 | }); 161 | 162 | loadStatus(); 163 | 164 | $('html,body').animate({ 165 | scrollTop: 0 166 | }, 100); 167 | } 168 | }); 169 | 170 | }); 171 | }); 172 | 173 | function loadStatus() { 174 | var tid = $("#tid").val(); 175 | var msgDiv = $("#msgDiv"); 176 | 177 | post_data = { 178 | 'requestType':'checkStatus', 179 | 'taskId':tid 180 | }; 181 | $.post('ajax/viewtask_ajax.php', post_data, function(resdata) { 182 | $.each($.parseJSON(resdata), function(idx, obj) { 183 | var compDate = formatDate(new Date(obj[8]), "M d, y"); 184 | 185 | if (obj[8] != '') { 186 | msgDiv.html('
'+taskCompOnText+' '+compDate+'
');} 187 | if (obj[8] == '') { 188 | msgDiv.html(''); 189 | } 190 | }); 191 | }); 192 | } 193 | 194 | var formatDate = function(date, format) { 195 | date = convertDate(date); 196 | 197 | var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], 198 | getPaddedComp = function(comp) { 199 | return ((parseInt(comp) < 10) ? ('0' + comp) : comp) 200 | }, 201 | formattedDate = format, 202 | o = { 203 | "y+": date.getFullYear(), 204 | "M+": months[date.getMonth()], 205 | "d+": getPaddedComp(date.getDate()) 206 | }; 207 | 208 | for (var k in o) { 209 | if (new RegExp("("+k+")").test(format)) { 210 | formattedDate = formattedDate.replace(RegExp.$1, o[k]); 211 | } 212 | } 213 | return formattedDate; 214 | }; 215 | 216 | var convertDate = function(date) { 217 | var theDate = new Date(date); 218 | var addDate = 1; 219 | theDate.setDate(theDate.getDate() + addDate); 220 | return theDate; 221 | }; -------------------------------------------------------------------------------- /pages/viewTask.php: -------------------------------------------------------------------------------- 1 | datadir = 'data/tasks/'; 8 | 9 | define('USER_ID', 0); 10 | define('TASK_ID', 1); 11 | define('TASK_TITLE', 2); 12 | define('TASK_DATE', 3); 13 | define('DATE_DUE', 4); 14 | define('TASK_TYPE', 5); 15 | define('REFERENCE', 6); 16 | define('PERC_COMPLETE', 7); 17 | define('DATE_COMPLTED', 8); 18 | define('TASK_DESC', 9); 19 | define('TASK_NOTES', 10); 20 | define('TASK_STATUS', 11); 21 | define('UPDATE_DATE', 12); 22 | 23 | // Get the Task Data 24 | $task = $db->selectWhere( 25 | 'tasks.txt', 26 | new SimpleWhereClause(TASK_ID, '=', $taskId) 27 | ); 28 | $taskdata = $db->selectAll($taskId.'.txt'); 29 | 30 | // Set some variables to empty 31 | $taskTitle = $dateAssigned = $dateDue = $taskType = $taskRef = $percComp = $dateComp = $taskDesc = $taskNotes = $taskStatus = $updatDate = ''; 32 | 33 | foreach ($task as $k => $v) { 34 | $taskTitle = $v[2]; 35 | if ($v[3] != '') { 36 | $dateAssigned = dbDateFormat($v[3]); 37 | } else { 38 | $dateAssigned = ''; 39 | } 40 | if ($v[4] != '') { 41 | $dateDue = dbDateFormat($v[4]); 42 | } else { 43 | $dateDue = ''; 44 | } 45 | if ($v[5] != '') { 46 | $taskType = decodeIt($v[5]); 47 | } else { 48 | $taskType = ''; 49 | } 50 | if ($v[6] != '') { 51 | $taskRef = decodeIt($v[6]); 52 | } else { 53 | $taskRef = ''; 54 | } 55 | if ($v[8] != '') { 56 | $dateComp = dbDateFormat($v[8]); 57 | } else { 58 | $dateComp = ''; 59 | } 60 | } 61 | 62 | foreach ($taskdata as $k => $v) { 63 | if ($v[9] != '') { 64 | $taskDesc = decodeIt($v[9]); 65 | } else { 66 | $taskDesc = ''; 67 | } 68 | if ($v[10] != '') { 69 | $taskNotes = decodeIt($v[10]); 70 | } else { 71 | $taskNotes = ''; 72 | } 73 | if ($v[11] != '') { 74 | $taskStatus = decodeIt($v[11]); 75 | } else { 76 | $taskStatus = ''; 77 | } 78 | if ($v[12] != '') { 79 | $updatDate = strtotime($v[12]); 80 | } else { 81 | $updatDate = ''; 82 | } 83 | } 84 | 85 | $pageTitle = $viewTaskPageTitle; 86 | $addCss = ''; 87 | $datePicker = 'true'; 88 | $jsFile = 'viewTask'; 89 | include 'includes/header.php'; 90 | ?> 91 |
92 | 93 | 94 |
95 |
96 |
97 |
98 |

99 |
100 |
101 | 102 |
103 |
104 |
105 | 106 |
107 |
108 | 109 |
110 |
111 |
112 |
113 | 114 | 115 | 116 |
117 |
118 |
119 |

120 | 121 | 122 |

123 |
124 |
125 | 126 |
127 |
128 |
129 | 130 | 131 | 132 |
133 |
134 |
135 |
136 | 137 | 138 | 139 |
140 |
141 |
142 |
143 | 144 | 145 | 146 |
147 |
148 |
149 |
150 |
151 |
152 | 153 | 154 | 155 |
156 |
157 |
158 |
159 | 160 | 161 | 162 |
163 |
164 |
165 |
166 | 167 | 168 | 169 |
170 |
171 | 172 | 173 |
174 |
175 | 176 | 177 |
178 | 179 |
180 |
181 | 182 | 183 | 184 |
185 |
186 |

187 | 188 |

189 |
190 |
191 |
192 |
193 |
194 |
-------------------------------------------------------------------------------- /js/includes/sign-in.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Localizations 3 | * Only translate the text between the single quotes on lines 7 through 24. 4 | * Do NOT modify any code below line 24. 5 | **/ 6 | 7 | var dupUsername1 = 'Whoops, Looks like there is all ready an account registered with the Username'; 8 | var dupUsername2 = 'Please select something different.'; 9 | var usernameQuip = 'Usernames can contain upper and lower case letters, numbers and dashes only. Duplicate usernames are not allowed.'; 10 | var dupEmail = 'Whoops, Looks like there is all ready an account registered with that Email Address.'; 11 | var usernameReq = 'Your Account Username is required.'; 12 | var passReq = 'Your Account Password is required.'; 13 | var invalidSignin = 'Whoops, Invalid Sign In. Please check your Username and/or Password and try again.'; 14 | var signinSuccess = 'Cheer! Sign In Successfull'; 15 | var signinError = 'Uh oh, Looks like an unexpected error was encountered, and you were not Signed In.'; 16 | var newusernameReq = 'Your New Account will need a Username.'; 17 | var validEmailReq = 'Your New Account will need a valid Email Address.'; 18 | var newpassReq = 'Your New Account will need a Password.'; 19 | var newAccCreated = 'Your New Account has been successfully created.'; 20 | var newAccError = 'Looks like an unexpected error was encountered, and your New Account was unable to be created.'; 21 | var accountEmailReq = 'Your Account Email Address is required.'; 22 | var passResetSuccess = 'Your Account Password has been reset, and an email has been sent with the new password.'; 23 | var noAccError = 'Hmmm, An Account with that Email Address could not be found.'; 24 | var resetPassError = 'Looks like an unexpected error was encountered, and your Account Password could not be reset.'; 25 | 26 | /** END Localizations **/ 27 | 28 | $(document).ready( 29 | // Toggle Sign In and Sign Up forms 30 | function() { 31 | $('#signup').on("click", function() { 32 | var x = this.id; 33 | $("#" + x).removeClass("s-atbottom"); 34 | $("#" + x).addClass("s-attop"); 35 | $("#login").removeClass("l-attop"); 36 | $("#login").addClass("l-atbottom"); 37 | }); 38 | 39 | $('#login').on("click", function() { 40 | var x = this.id; 41 | $("#" + x).removeClass("l-atbottom"); 42 | $("#" + x).addClass("l-attop"); 43 | $("#signup").removeClass("s-attop"); 44 | $("#signup").addClass("s-atbottom"); 45 | }); 46 | } 47 | ); 48 | 49 | jQuery(document).ready(function($) { 50 | var focused = 0; 51 | 52 | // Check for Duplicate username 53 | $('#newusername').blur(function() { 54 | var username = $("#newusername").val(); 55 | 56 | if (username != '' ) { 57 | // Start the AJAX 58 | post_data = { 59 | 'username':username, 60 | 'requestType':'usercheck' 61 | }; 62 | $.post('ajax/signin_ajax.php', post_data, function(data) { 63 | if (data == '1') { 64 | // Duplicate Username found 65 | Meowsa.addNotification({ 66 | color: 'warning', 67 | text: dupUsername1+' "'+username+'". '+dupUsername2, 68 | icon: '', 69 | timeout: 12000 70 | }); 71 | 72 | // Reset the form fields 73 | $("#newusername").val(''); 74 | } 75 | }); 76 | } 77 | }); 78 | 79 | // Show Username restictions on first focus 80 | $('#newusername').focus(function() { 81 | if (focused === 0) { 82 | Meowsa.addNotification({ 83 | color: 'info', 84 | text: usernameQuip, 85 | icon: '', 86 | timeout: 8000 87 | }); 88 | focused++; 89 | } 90 | }); 91 | 92 | // Check for Duplicate Email 93 | $('#newemail').blur(function() { 94 | var useremail = $("#newemail").val(); 95 | 96 | if (useremail != '') { 97 | // Start the AJAX 98 | post_data = { 99 | 'useremail':useremail, 100 | 'requestType':'emailcheck' 101 | }; 102 | $.post('ajax/signin_ajax.php', post_data, function(data) { 103 | if (data == '1') { 104 | // Duplicate Email found 105 | Meowsa.addNotification({ 106 | color: 'warning', 107 | text: dupEmail, 108 | icon: '', 109 | timeout: 12000 110 | }); 111 | 112 | // Reset the form fields 113 | $("#newemail").val(''); 114 | } 115 | }); 116 | } 117 | }); 118 | 119 | // Sign the user in 120 | $('#signin-btn').click(function(e) { 121 | e.preventDefault(); 122 | 123 | var username = $("#username").val(); 124 | var password = $("#password").val(); 125 | 126 | if (username == '') { 127 | Meowsa.addNotification({ 128 | color: 'danger', 129 | text: usernameReq, 130 | icon: '', 131 | timeout: 10000 132 | }); 133 | $("#username").focus(); 134 | return false; 135 | } 136 | 137 | if (password == '') { 138 | Meowsa.addNotification({ 139 | color: 'danger', 140 | text: passReq, 141 | icon: '', 142 | timeout: 10000 143 | }); 144 | $("#password").focus(); 145 | return false; 146 | } 147 | 148 | // Start the AJAX 149 | post_data = { 150 | 'username':username, 151 | 'password':password, 152 | 'requestType':'signin' 153 | }; 154 | $.post('ajax/signin_ajax.php', post_data, function(resdata) { 155 | var datacheck = $.parseJSON(resdata).length; 156 | if (datacheck === 0) { 157 | // Unknown error 158 | Meowsa.addNotification({ 159 | color: 'warning', 160 | text: invalidSignin, 161 | icon: '', 162 | timeout: 12000 163 | }); 164 | 165 | // Reset the form fields 166 | $("#username, #password").val(''); 167 | } else { 168 | $.each($.parseJSON(resdata), function(idx, obj) { 169 | if (obj[0] != '') { 170 | // All is good! 171 | Meowsa.addNotification({ 172 | color: 'success', 173 | text: signinSuccess, 174 | icon: '', 175 | timeout: 10000 176 | }); 177 | 178 | // Reset the form fields 179 | $("#username, #password").val(''); 180 | 181 | // Redirect after 1.5 Seconds 182 | window.setTimeout(function () { location.href = "index.php"; }, 1500); 183 | } else { 184 | // Unknown error 185 | Meowsa.addNotification({ 186 | color: 'danger', 187 | text: signinError, 188 | icon: '', 189 | timeout: 12000 190 | }); 191 | } 192 | }); 193 | } 194 | }); 195 | }); 196 | 197 | // Create a new account 198 | $('#signup-btn').click(function(e) { 199 | e.preventDefault(); 200 | 201 | var username = $("#newusername").val(); 202 | var useremail = $("#newemail").val(); 203 | var password = $("#newpass").val(); 204 | var newacc = $("#newacc").val(); 205 | 206 | if (newacc == '') { 207 | if (username == '') { 208 | Meowsa.addNotification({ 209 | color: 'danger', 210 | text: newusernameReq, 211 | icon: '', 212 | timeout: 10000 213 | }); 214 | $("#newusername").focus(); 215 | return false; 216 | } 217 | 218 | if (useremail == '') { 219 | Meowsa.addNotification({ 220 | color: 'danger', 221 | text: validEmailReq, 222 | icon: '', 223 | timeout: 10000 224 | }); 225 | $("#newemail").focus(); 226 | return false; 227 | } 228 | 229 | if (password == '') { 230 | Meowsa.addNotification({ 231 | color: 'danger', 232 | text: newpassReq, 233 | icon: '', 234 | timeout: 10000 235 | }); 236 | $("#newpass").focus(); 237 | return false; 238 | } 239 | 240 | // Start the AJAX 241 | post_data = { 242 | 'username':username, 243 | 'useremail':useremail, 244 | 'password':password, 245 | 'requestType':'signup' 246 | }; 247 | $.post('ajax/signin_ajax.php', post_data, function(data) { 248 | if (data == '1') { 249 | // All is good! 250 | Meowsa.addNotification({ 251 | color: 'success', 252 | text: newAccCreated, 253 | icon: '', 254 | timeout: 10000 255 | }); 256 | 257 | // Reset the form fields 258 | $("#newusername, #newemail, #newpass").val(''); 259 | } else { 260 | // Unknown error 261 | Meowsa.addNotification({ 262 | color: 'danger', 263 | text: newAccError, 264 | icon: '', 265 | timeout: 12000 266 | }); 267 | } 268 | }); 269 | } else { 270 | $("#newusername, #newemail, #newpass").val(''); 271 | return false; 272 | } 273 | }); 274 | 275 | // Reset account password 276 | $('#resetPass').click(function(e) { 277 | e.preventDefault(); 278 | 279 | var useremail = $("#accountEmail").val(); 280 | 281 | if (useremail == '') { 282 | Meowsa.addNotification({ 283 | color: 'danger', 284 | text: accountEmailReq, 285 | icon: '', 286 | timeout: 10000 287 | }); 288 | $("#newemail").focus(); 289 | return false; 290 | } 291 | 292 | // Start the AJAX 293 | post_data = { 294 | 'useremail':useremail, 295 | 'requestType':'resetpass' 296 | }; 297 | $.post('ajax/signin_ajax.php', post_data, function(data) { 298 | if (data == '1') { 299 | // All is good! 300 | Meowsa.addNotification({ 301 | color: 'success', 302 | text: passResetSuccess, 303 | icon: '', 304 | timeout: 10000 305 | }); 306 | 307 | // Reset the form fields 308 | $("#accountEmail").val(''); 309 | } else if (data == '0') { 310 | // Unknown error 311 | Meowsa.addNotification({ 312 | color: 'danger', 313 | text: noAccError, 314 | icon: '', 315 | timeout: 12000 316 | }); 317 | 318 | // Reset the form fields 319 | $("#accountEmail").val(''); 320 | } else { 321 | // Unknown error 322 | Meowsa.addNotification({ 323 | color: 'danger', 324 | text: resetPassError, 325 | icon: '', 326 | timeout: 12000 327 | }); 328 | 329 | // Reset the form fields 330 | $("#accountEmail").val(''); 331 | } 332 | }); 333 | }); 334 | 335 | }); -------------------------------------------------------------------------------- /ajax/signin_ajax.php: -------------------------------------------------------------------------------- 1 | datadir = '../data/'; 12 | 13 | /* 14 | * Initialize the process 15 | * This switch decides what task is to be performed based on the requestType 16 | */ 17 | $mode = $_POST['requestType']; 18 | switch ($mode) { 19 | case 'signin':signin($db, $_POST['username'], $_POST['password']); break; 20 | case 'signup':signup($db, $siteName, $siteUrl, $siteEmail, $_POST['username'], $_POST['useremail'], $_POST['password']); break; 21 | case 'resetpass':resetpass($db, $siteName, $siteUrl, $siteEmail, $_POST['useremail']); break; 22 | case 'usercheck':usercheck($db, $_POST['username']); break; 23 | case 'emailcheck':emailcheck($db, $_POST['useremail']); break; 24 | } 25 | 26 | /* 27 | * Function to Sign a user in 28 | * 29 | * @param string $db The Flat File Connction 30 | * @param string $username The User's Account username passed via the sign in form 31 | * @param string $password The User's Account password passed via the sign in form 32 | * 33 | * @return array The User's basic Account info 34 | */ 35 | function signin($db, $username, $password) 36 | { 37 | define('USER_ID', 0); 38 | define('USERNAME', 1); 39 | define('PASSWORD', 2); 40 | define('USER_EMAIL', 3); 41 | define('DATE_CREATED', 4); 42 | 43 | $usrname = htmlspecialchars($username); 44 | $pass = encodeIt($password); 45 | 46 | $compClause = new AndWhereClause(); 47 | $compClause->add(new SimpleWhereClause(USERNAME, '=', $usrname, STRING_COMPARISON)); 48 | $compClause->add(new SimpleWhereClause(PASSWORD, '=', $pass, STRING_COMPARISON)); 49 | $userdata = $db->selectWhere('users.txt', $compClause, 1); 50 | 51 | foreach ($userdata as $item => $row) { 52 | $_SESSION['st']['userId'] = $row[0]; 53 | $_SESSION['st']['userName'] = $row[1]; 54 | $_SESSION['st']['userEmail'] = $row[3]; 55 | } 56 | 57 | echo json_encode($userdata); 58 | } 59 | 60 | /* 61 | * Function to create a New User Account 62 | * 63 | * @param string $db The Flat File Connction 64 | * @param string $username The desired Account username passed via the sign in form 65 | * @param string $useremail The User's Email Address passed via the sign in form 66 | * @param string $password The desired Account password passed via the sign in form 67 | * 68 | * @return boolean 69 | */ 70 | function signup($db, $siteName, $siteUrl, $siteEmail, $username, $useremail, $password) 71 | { 72 | define('USER_ID', 0); 73 | define('USERNAME', 1); 74 | define('PASSWORD', 2); 75 | define('USER_EMAIL', 3); 76 | define('DATE_CREATED', 4); 77 | 78 | // Get $_POST data 79 | $usrname = htmlspecialchars(alphaNum($username)); 80 | $usremail = htmlspecialchars($useremail); 81 | $pass = encodeIt($password); 82 | $dateCreated = date('Y-m-d H:i:s'); 83 | 84 | // Generate a RANDOM Hash 85 | $randomHash = uniqid(rand()); 86 | // Take the first 8 hash digits and use it as the User's ID 87 | $randHash = substr($randomHash, 0, 8); 88 | 89 | // Set the values to insert 90 | $newuser[USER_ID] = $randHash; 91 | $newuser[USERNAME] = $usrname; 92 | $newuser[PASSWORD] = $pass; 93 | $newuser[USER_EMAIL] = $usremail; 94 | $newuser[DATE_CREATED] = $dateCreated; 95 | 96 | $new_user = $db->insert( 97 | 'users.txt', 98 | $newuser 99 | ); 100 | 101 | $userinfo[USER_ID] = $randHash; 102 | $userinfo[USERNAME] = $usrname; 103 | $userinfo[DATE_CREATED] = $dateCreated; 104 | 105 | // Define the File to be created 106 | $userFile = $usrname.'-'.$randHash; 107 | 108 | // Insert the data 109 | $new_user = $db->insert( 110 | $userFile.'.txt', 111 | $userinfo 112 | ); 113 | 114 | // Send out Notification Email to the New User 115 | $subject = $siteName.' New Account Created'; 116 | 117 | $message = ''; 118 | $message .= '

'.$subject.'

'; 119 | $message .= '

'; 120 | $message .= 'Your new account has been successfully created, and you can now sign in.
'; 121 | $message .= 'Sign In'; 122 | $message .= '

'; 123 | $message .= '

Username: '.$usrname.'
Password: The password you signed up with.

'; 124 | $message .= '
'; 125 | $message .= '

Thank You,
'.$siteName.'

'; 126 | $message .= ''; 127 | 128 | $headers = 'From: '.$siteName.' <'.$siteEmail.">\r\n"; 129 | $headers .= 'Reply-To: '.$siteEmail."\r\n"; 130 | $headers .= "MIME-Version: 1.0\r\n"; 131 | $headers .= "Content-Type: text/html; charset=UTF-8\r\n"; 132 | 133 | mail($usremail, $subject, $message, $headers); 134 | 135 | // Check if the file was created 136 | $checkFile = '../data/'.$userFile.'.txt'; 137 | 138 | if (file_exists($checkFile)) { 139 | echo '1'; // All is good! 140 | } else { 141 | echo '0'; // Nope, error... 142 | } 143 | } 144 | 145 | /* 146 | * Function to reset a User's Account Password 147 | * 148 | * @param string $db The Flat File Connction 149 | * @param string $useremail The User's Email Address passed via the sign in form 150 | * 151 | * @return boolean 1 = Account password for the specific account has been reset 152 | */ 153 | function resetpass($db, $siteName, $siteUrl, $siteEmail, $useremail) 154 | { 155 | $userdata = ''; 156 | 157 | define('USER_ID', 0); 158 | define('USERNAME', 1); 159 | define('PASSWORD', 2); 160 | define('USER_EMAIL', 3); 161 | define('DATE_CREATED', 4); 162 | 163 | $useremail = htmlspecialchars($useremail); 164 | 165 | $userdata = $db->selectWhere( 166 | 'users.txt', 167 | new SimpleWhereClause(USER_EMAIL, '=', $useremail) 168 | ); 169 | 170 | if (empty($userdata)) { 171 | echo '0'; // No User found 172 | } else { 173 | global $uid; 174 | global $uname; 175 | foreach ($userdata as $item => $row) { 176 | $uid = $row[0]; 177 | $uname = $row[1]; 178 | } 179 | 180 | $userfile = $db->selectAll($uname.'-'.$uid.'.txt'); 181 | 182 | // Generate a RANDOM Hash 183 | $randomHash = uniqid(rand()); 184 | // Take the first 8 hash digits and use it as the User's new password 185 | $randHash = substr($randomHash, 0, 8); 186 | 187 | $newpass = encodeIt($randHash); 188 | 189 | $db->updateSetWhere( 190 | 'users.txt', [ 191 | PASSWORD => $newpass, 192 | ], 193 | new SimpleWhereClause( 194 | USER_ID, '=', $uid 195 | ) 196 | ); 197 | 198 | // Send out Notification Email with the new Password 199 | $subject = $siteName.' Account Password Reset'; 200 | 201 | $message = ''; 202 | $message .= '

'.$subject.'

'; 203 | $message .= '

'; 204 | $message .= 'Your Account Password has been Reset
'; 205 | $message .= 'Temporary Password: '.$randHash; 206 | $message .= '

'; 207 | $message .= '

Once you have signed in, please take the time to update your account password to something you can easily remember.

'; 208 | $message .= '
'; 209 | $message .= '

Thank You,
'.$siteName.'

'; 210 | $message .= ''; 211 | 212 | $headers = 'From: '.$siteName.' <'.$siteEmail.">\r\n"; 213 | $headers .= 'Reply-To: '.$siteEmail."\r\n"; 214 | $headers .= "MIME-Version: 1.0\r\n"; 215 | $headers .= "Content-Type: text/html; charset=UTF-8\r\n"; 216 | 217 | mail($useremail, $subject, $message, $headers); 218 | 219 | echo '1'; // Update completed 220 | } 221 | } 222 | 223 | /* 224 | * Function to check for a duplicate username 225 | * 226 | * @param string $db The Flat File Connction 227 | * @param string $username The desired Account username passed via the sign in form 228 | * 229 | * @return boolean 230 | */ 231 | function usercheck($db, $username) 232 | { 233 | $userdata = ''; 234 | 235 | define('USER_ID', 0); 236 | define('USERNAME', 1); 237 | define('PASSWORD', 2); 238 | define('USER_EMAIL', 3); 239 | define('DATE_CREATED', 4); 240 | 241 | $usrname = htmlspecialchars(alphaNum($username)); 242 | 243 | $userdata = $db->selectWhere( 244 | 'users.txt', 245 | new SimpleWhereClause(USERNAME, '=', $usrname) 246 | ); 247 | 248 | if (empty($userdata)) { 249 | echo '0'; // No Duplicate found 250 | } else { 251 | echo '1'; // Duplicate found 252 | } 253 | } 254 | 255 | /* 256 | * Function to check for a duplicate email 257 | * 258 | * @param string $db The Flat File Connction 259 | * @param string $useremail The User's Email Address passed via the sign in form 260 | * 261 | * @return boolean 262 | */ 263 | function emailcheck($db, $useremail) 264 | { 265 | $userdata = ''; 266 | 267 | define('USER_ID', 0); 268 | define('USERNAME', 1); 269 | define('PASSWORD', 2); 270 | define('USER_EMAIL', 3); 271 | define('DATE_CREATED', 4); 272 | 273 | $useremail = htmlspecialchars($useremail); 274 | 275 | $userdata = $db->selectWhere( 276 | 'users.txt', 277 | new SimpleWhereClause(USER_EMAIL, '=', $useremail) 278 | ); 279 | 280 | if (empty($userdata)) { 281 | echo '0'; // No Duplicate found 282 | } else { 283 | echo '1'; // Duplicate found 284 | } 285 | } 286 | 287 | // That's it! Done! 288 | -------------------------------------------------------------------------------- /css/datetimepicker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Datetimepicker for Bootstrap 3 | * 4 | * Copyright 2012 Stefan Petre 5 | * Improvements by Andrew Rowls 6 | * Licensed under the Apache License v2.0 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * Documentation: http://www.malot.fr/bootstrap-datetimepicker 9 | * 10 | */ 11 | .datetimepicker { 12 | padding: 4px; 13 | margin-top: 1px; 14 | -webkit-border-radius: 0; 15 | -moz-border-radius: 0; 16 | border-radius: 0; 17 | direction: ltr; 18 | } 19 | 20 | .datetimepicker-inline { 21 | width: 220px; 22 | } 23 | 24 | .datetimepicker.datetimepicker-rtl { 25 | direction: rtl; 26 | } 27 | 28 | .datetimepicker.datetimepicker-rtl table tr td span { 29 | float: right; 30 | } 31 | 32 | .datetimepicker-dropdown, .datetimepicker-dropdown-left { 33 | top: 0; 34 | left: 0; 35 | } 36 | 37 | [class*=" datetimepicker-dropdown"]:before { 38 | content: ''; 39 | display: inline-block; 40 | border-left: 7px solid transparent; 41 | border-right: 7px solid transparent; 42 | border-bottom: 7px solid #cccccc; 43 | border-bottom-color: rgba(0, 0, 0, 0.2); 44 | position: absolute; 45 | } 46 | 47 | [class*=" datetimepicker-dropdown"]:after { 48 | content: ''; 49 | display: inline-block; 50 | border-left: 6px solid transparent; 51 | border-right: 6px solid transparent; 52 | border-bottom: 6px solid #ffffff; 53 | position: absolute; 54 | } 55 | 56 | [class*=" datetimepicker-dropdown-top"]:before { 57 | content: ''; 58 | display: inline-block; 59 | border-left: 7px solid transparent; 60 | border-right: 7px solid transparent; 61 | border-top: 7px solid #cccccc; 62 | border-top-color: rgba(0, 0, 0, 0.2); 63 | border-bottom: 0; 64 | } 65 | 66 | [class*=" datetimepicker-dropdown-top"]:after { 67 | content: ''; 68 | display: inline-block; 69 | border-left: 6px solid transparent; 70 | border-right: 6px solid transparent; 71 | border-top: 6px solid #ffffff; 72 | border-bottom: 0; 73 | } 74 | 75 | .datetimepicker-dropdown-bottom-left:before { 76 | top: -7px; 77 | right: 6px; 78 | } 79 | 80 | .datetimepicker-dropdown-bottom-left:after { 81 | top: -6px; 82 | right: 7px; 83 | } 84 | 85 | .datetimepicker-dropdown-bottom-right:before { 86 | top: -7px; 87 | left: 6px; 88 | } 89 | 90 | .datetimepicker-dropdown-bottom-right:after { 91 | top: -6px; 92 | left: 7px; 93 | } 94 | 95 | .datetimepicker-dropdown-top-left:before { 96 | bottom: -7px; 97 | right: 6px; 98 | } 99 | 100 | .datetimepicker-dropdown-top-left:after { 101 | bottom: -6px; 102 | right: 7px; 103 | } 104 | 105 | .datetimepicker-dropdown-top-right:before { 106 | bottom: -7px; 107 | left: 6px; 108 | } 109 | 110 | .datetimepicker-dropdown-top-right:after { 111 | bottom: -6px; 112 | left: 7px; 113 | } 114 | 115 | .datetimepicker > div { 116 | display: none; 117 | } 118 | 119 | .datetimepicker.minutes div.datetimepicker-minutes { 120 | display: block; 121 | } 122 | 123 | .datetimepicker.hours div.datetimepicker-hours { 124 | display: block; 125 | } 126 | 127 | .datetimepicker.days div.datetimepicker-days { 128 | display: block; 129 | } 130 | 131 | .datetimepicker.months div.datetimepicker-months { 132 | display: block; 133 | } 134 | 135 | .datetimepicker.years div.datetimepicker-years { 136 | display: block; 137 | } 138 | 139 | .datetimepicker table { 140 | margin: 0; 141 | } 142 | 143 | .datetimepicker tr { border: none; } 144 | 145 | .datetimepicker td, 146 | .datetimepicker th { 147 | text-align: center; 148 | width: 20px; 149 | height: 20px; 150 | -webkit-border-radius: 0; 151 | -moz-border-radius: 0; 152 | border: 1px solid #f0f0f0; 153 | background: #ffffff none repeat scroll 0 0; 154 | } 155 | .datetimepicker th { 156 | background: #ffffff none repeat scroll 0 0; 157 | font-weight: 400; 158 | } 159 | 160 | .table-striped .datetimepicker table tr td, 161 | .table-striped .datetimepicker table tr th { 162 | background-color: transparent; 163 | } 164 | 165 | .datetimepicker table tr td.minute:hover { 166 | background: #eeeeee; 167 | cursor: pointer; 168 | } 169 | 170 | .datetimepicker table tr td.hour:hover { 171 | background: #eeeeee; 172 | cursor: pointer; 173 | } 174 | 175 | .datetimepicker table tr td.day:hover { 176 | background: #f0f0f0; 177 | cursor: pointer; 178 | } 179 | 180 | .datetimepicker table tr td.old, 181 | .datetimepicker table tr td.new { 182 | color: #999999; 183 | } 184 | 185 | .datetimepicker table tr td.disabled, 186 | .datetimepicker table tr td.disabled:hover { 187 | background: none; 188 | color: #999999; 189 | cursor: default; 190 | } 191 | 192 | .datetimepicker table tr td.today, 193 | .datetimepicker table tr td.today:hover, 194 | .datetimepicker table tr td.today.disabled, 195 | .datetimepicker table tr td.today.disabled:hover { 196 | background-color: #d1d1d1; 197 | } 198 | 199 | .datetimepicker table tr td.today:hover, 200 | .datetimepicker table tr td.today:hover:hover, 201 | .datetimepicker table tr td.today.disabled:hover, 202 | .datetimepicker table tr td.today.disabled:hover:hover, 203 | .datetimepicker table tr td.today:active, 204 | .datetimepicker table tr td.today:hover:active, 205 | .datetimepicker table tr td.today.disabled:active, 206 | .datetimepicker table tr td.today.disabled:hover:active, 207 | .datetimepicker table tr td.today.active, 208 | .datetimepicker table tr td.today:hover.active, 209 | .datetimepicker table tr td.today.disabled.active, 210 | .datetimepicker table tr td.today.disabled:hover.active, 211 | .datetimepicker table tr td.today.disabled, 212 | .datetimepicker table tr td.today:hover.disabled, 213 | .datetimepicker table tr td.today.disabled.disabled, 214 | .datetimepicker table tr td.today.disabled:hover.disabled, 215 | .datetimepicker table tr td.today[disabled], 216 | .datetimepicker table tr td.today:hover[disabled], 217 | .datetimepicker table tr td.today.disabled[disabled], 218 | .datetimepicker table tr td.today.disabled:hover[disabled] { 219 | background-color: #e5e5e5; 220 | } 221 | 222 | .datetimepicker table tr td.today:active, 223 | .datetimepicker table tr td.today:hover:active, 224 | .datetimepicker table tr td.today.disabled:active, 225 | .datetimepicker table tr td.today.disabled:hover:active, 226 | .datetimepicker table tr td.today.active, 227 | .datetimepicker table tr td.today:hover.active, 228 | .datetimepicker table tr td.today.disabled.active, 229 | .datetimepicker table tr td.today.disabled:hover.active { 230 | background-color: #fbf069; 231 | } 232 | 233 | .datetimepicker table tr td.active, 234 | .datetimepicker table tr td.active:hover, 235 | .datetimepicker table tr td.active.disabled, 236 | .datetimepicker table tr td.active.disabled:hover { 237 | background-color: #f0f0f0; 238 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 239 | } 240 | 241 | .datetimepicker table tr td.active:hover, 242 | .datetimepicker table tr td.active:hover:hover, 243 | .datetimepicker table tr td.active.disabled:hover, 244 | .datetimepicker table tr td.active.disabled:hover:hover, 245 | .datetimepicker table tr td.active:active, 246 | .datetimepicker table tr td.active:hover:active, 247 | .datetimepicker table tr td.active.disabled:active, 248 | .datetimepicker table tr td.active.disabled:hover:active, 249 | .datetimepicker table tr td.active.active, 250 | .datetimepicker table tr td.active:hover.active, 251 | .datetimepicker table tr td.active.disabled.active, 252 | .datetimepicker table tr td.active.disabled:hover.active, 253 | .datetimepicker table tr td.active.disabled, 254 | .datetimepicker table tr td.active:hover.disabled, 255 | .datetimepicker table tr td.active.disabled.disabled, 256 | .datetimepicker table tr td.active.disabled:hover.disabled, 257 | .datetimepicker table tr td.active[disabled], 258 | .datetimepicker table tr td.active:hover[disabled], 259 | .datetimepicker table tr td.active.disabled[disabled], 260 | .datetimepicker table tr td.active.disabled:hover[disabled] { 261 | background-color: #0044cc; 262 | color: #ffffff; 263 | } 264 | 265 | .datetimepicker table tr td.active:active, 266 | .datetimepicker table tr td.active:hover:active, 267 | .datetimepicker table tr td.active.disabled:active, 268 | .datetimepicker table tr td.active.disabled:hover:active, 269 | .datetimepicker table tr td.active.active, 270 | .datetimepicker table tr td.active:hover.active, 271 | .datetimepicker table tr td.active.disabled.active, 272 | .datetimepicker table tr td.active.disabled:hover.active { 273 | background-color: #2481be; 274 | } 275 | 276 | .datetimepicker table tr td span { 277 | display: block; 278 | width: 23%; 279 | height: 54px; 280 | line-height: 54px; 281 | float: left; 282 | margin: 1%; 283 | cursor: pointer; 284 | -webkit-border-radius: 0; 285 | -moz-border-radius: 0; 286 | border-radius: 0; 287 | } 288 | 289 | .datetimepicker .datetimepicker-hours span { 290 | height: 26px; 291 | line-height: 26px; 292 | } 293 | 294 | .datetimepicker .datetimepicker-hours table tr td span.hour_am, 295 | .datetimepicker .datetimepicker-hours table tr td span.hour_pm { 296 | width: 14.6%; 297 | } 298 | 299 | .datetimepicker .datetimepicker-hours fieldset legend, 300 | .datetimepicker .datetimepicker-minutes fieldset legend { 301 | margin-bottom: inherit; 302 | line-height: 30px; 303 | } 304 | 305 | .datetimepicker .datetimepicker-minutes span { 306 | height: 26px; 307 | line-height: 26px; 308 | } 309 | 310 | .datetimepicker table tr td span:hover { 311 | background: #eeeeee; 312 | } 313 | 314 | .datetimepicker table tr td span.disabled, 315 | .datetimepicker table tr td span.disabled:hover { 316 | background: none; 317 | color: #999999; 318 | cursor: default; 319 | } 320 | 321 | .datetimepicker table tr td span.active, 322 | .datetimepicker table tr td span.active:hover, 323 | .datetimepicker table tr td span.active.disabled, 324 | .datetimepicker table tr td span.active.disabled:hover { 325 | background-color: #f0f0f0; 326 | border-color: #0044cc #0044cc #002a80; 327 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 328 | color: #ffffff; 329 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 330 | } 331 | 332 | .datetimepicker table tr td span.active:hover, 333 | .datetimepicker table tr td span.active:hover:hover, 334 | .datetimepicker table tr td span.active.disabled:hover, 335 | .datetimepicker table tr td span.active.disabled:hover:hover, 336 | .datetimepicker table tr td span.active:active, 337 | .datetimepicker table tr td span.active:hover:active, 338 | .datetimepicker table tr td span.active.disabled:active, 339 | .datetimepicker table tr td span.active.disabled:hover:active, 340 | .datetimepicker table tr td span.active.active, 341 | .datetimepicker table tr td span.active:hover.active, 342 | .datetimepicker table tr td span.active.disabled.active, 343 | .datetimepicker table tr td span.active.disabled:hover.active, 344 | .datetimepicker table tr td span.active.disabled, 345 | .datetimepicker table tr td span.active:hover.disabled, 346 | .datetimepicker table tr td span.active.disabled.disabled, 347 | .datetimepicker table tr td span.active.disabled:hover.disabled, 348 | .datetimepicker table tr td span.active[disabled], 349 | .datetimepicker table tr td span.active:hover[disabled], 350 | .datetimepicker table tr td span.active.disabled[disabled], 351 | .datetimepicker table tr td span.active.disabled:hover[disabled] { 352 | background-color: #0044cc; 353 | } 354 | 355 | .datetimepicker table tr td span.active:active, 356 | .datetimepicker table tr td span.active:hover:active, 357 | .datetimepicker table tr td span.active.disabled:active, 358 | .datetimepicker table tr td span.active.disabled:hover:active, 359 | .datetimepicker table tr td span.active.active, 360 | .datetimepicker table tr td span.active:hover.active, 361 | .datetimepicker table tr td span.active.disabled.active, 362 | .datetimepicker table tr td span.active.disabled:hover.active { 363 | background-color: #6699cc; 364 | } 365 | 366 | .datetimepicker table tr td span.old { 367 | color: #999999; 368 | } 369 | 370 | .datetimepicker th.switch { 371 | width: 145px; 372 | } 373 | 374 | .datetimepicker thead tr:first-child th, 375 | .datetimepicker tfoot tr:first-child th { 376 | cursor: pointer; 377 | } 378 | 379 | .datetimepicker thead tr:first-child th:hover, 380 | .datetimepicker tfoot tr:first-child th:hover { 381 | background: #f0f0f0; 382 | color: #444444; 383 | } 384 | 385 | .input-append.date .add-on i, 386 | .input-prepend.date .add-on i, 387 | .input-group.date .input-group-addon span { 388 | cursor: pointer; 389 | width: 14px; 390 | height: 14px; 391 | } 392 | -------------------------------------------------------------------------------- /css/custom.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Muli:300,400,700); 2 | * { border-radius:0 !important; } 3 | 4 | /* ========================================================================== 5 | The Basics 6 | ========================================================================== */ 7 | html, body { height: 100%; } 8 | body { 9 | font-family: "Muli", sans-serif; 10 | font-size: 16px; 11 | font-weight: 300; 12 | line-height: 1.6; 13 | color: #555555; 14 | background: none repeat scroll 0 0 #333; 15 | overflow-x: hidden; 16 | } 17 | 18 | a { 19 | color: #444444; 20 | text-decoration: none; 21 | outline: 0; 22 | font-weight: 400; 23 | } 24 | a:hover { 25 | color: #57aa82; 26 | text-decoration: none; 27 | } 28 | 29 | a, a:before, a:after { 30 | -webkit-transition-property: background color; 31 | -webkit-transition-duration: 0.2s; 32 | -webkit-transition-timing-function: ease; 33 | -moz-transition-property: background color; 34 | -moz-transition-duration: 0.2s; 35 | -moz-transition-timing-function: ease; 36 | -o-transition-property: background color; 37 | -o-transition-duration: 0.2s; 38 | -o-transition-timing-function: ease; 39 | transition-property: background color; 40 | transition-duration: 0.2s; 41 | transition-timing-function: ease; 42 | } 43 | 44 | h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { 45 | color: inherit; 46 | font-weight: 300; 47 | line-height: 1.1; 48 | } 49 | 50 | h1 .small, h1 small, h2 .small, h2 small, h3 .small, h3 small { 51 | font-size: 12px; 52 | font-weight: 300; 53 | padding-left: 15px; 54 | color: #999999; 55 | } 56 | 57 | img { 58 | max-width: 100%; 59 | height: auto; 60 | } 61 | 62 | pre { max-width: 1200px; } 63 | 64 | /* ========================================================================== 65 | Layout 66 | ========================================================================== */ 67 | .wrapper { 68 | display: -webkit-flex; 69 | display: flex; 70 | min-height: 100%; 71 | } 72 | 73 | .sidebar { 74 | position: fixed; 75 | width: 220px; 76 | } 77 | 78 | .content { 79 | -webkit-flex: 1; 80 | flex: 1; 81 | background: #ffffff; 82 | box-shadow: 0 0 5px rgba(0,0,0,1); 83 | transform: translate3d(0,0,0); 84 | transition: all .3s; 85 | } 86 | .content.content-is-open { margin-left: 220px; } 87 | 88 | .page-content { padding: 0 30px 20px; } 89 | 90 | .side-panel-toggle { 91 | cursor: pointer; 92 | font-size: 24px; 93 | color: #40966d; 94 | } 95 | 96 | .top-block { 97 | line-height: 47px; 98 | border-bottom: 1px solid #57aa82; 99 | background: #7fc6a4; 100 | padding: 0 20px; 101 | } 102 | .top-block p { 103 | font-size: 20px; 104 | color: #f0f0f0; 105 | letter-spacing: 0.02em; 106 | margin: 0; 107 | } 108 | 109 | /* ========================================================================== 110 | Navigation 111 | ========================================================================== */ 112 | .title { 113 | font-size: 16px; 114 | line-height: 50px; 115 | text-align: center; 116 | text-transform: uppercase; 117 | letter-spacing: 1px; 118 | color: #eeeeee; 119 | border-bottom: 1px solid #222222; 120 | background: #2a2a2a; 121 | } 122 | 123 | .nav li a { 124 | position: relative; 125 | display: block; 126 | padding: 15px 15px 15px 50px; 127 | font-size: 14px; 128 | color: #eeeeee; 129 | border-bottom: 1px solid #222222; 130 | } 131 | .nav li a:before { 132 | font: 14px fontawesome; 133 | position: absolute; 134 | top: 18px; 135 | left: 20px; 136 | } 137 | .nav li:nth-child(1) a:before { content: '\f0ae'; } 138 | .nav li:nth-child(2) a:before { content: '\f196'; } 139 | .nav li:nth-child(3) a:before { content: '\f044'; } 140 | .nav li:nth-child(4) a:before { content: '\f196'; } 141 | .nav li:nth-child(5) a:before { content: '\f007'; } 142 | .nav li:nth-child(6) a:before { content: '\f08b'; } 143 | .nav li a:hover { background: #444444; } 144 | 145 | .nav li a.active, .nav > li > a:focus, .nav > li > a:hover { 146 | box-shadow: inset 5px 0 0 #7fc6a4, inset 6px 0 0 #222222; 147 | background: #444444; 148 | } 149 | 150 | /* ========================================================================== 151 | Margins 152 | ========================================================================== */ 153 | .no-margin { margin: 0 !important; } 154 | 155 | .mt-0 { margin-top: 0 !important; } 156 | .mt-5 { margin-top: 5px !important; } 157 | .mt-10 { margin-top: 10px !important; } 158 | .mt-15 { margin-top: 15px !important; } 159 | .mt-20 { margin-top: 20px !important; } 160 | .mt-30 { margin-top: 30px !important; } 161 | 162 | .mb-0 { margin-bottom: 0 !important; } 163 | .mb-5 { margin-bottom: 5px !important; } 164 | .mb-10 { margin-bottom: 10px !important; } 165 | .mb-15 { margin-bottom: 15px !important; } 166 | .mb-20 { margin-bottom: 20px !important; } 167 | .mb-30 { margin-bottom: 30px !important; } 168 | 169 | .mr-0 { margin-right: 0 !important; } 170 | .ml-0 { margin-left: 0 !important; } 171 | 172 | /* ========================================================================== 173 | Paddings 174 | ========================================================================== */ 175 | .no-padding { padding: 0 !important; } 176 | 177 | .pt-0 { padding-top: 0 !important; } 178 | .pt-5 { padding-top: 5px !important; } 179 | .pt-10 { padding-top: 10px !important; } 180 | .pt-15 { padding-top: 15px !important; } 181 | .pt-20 { padding-top: 20px !important; } 182 | 183 | .pb-0 { padding-bottom: 0 !important; } 184 | .pb-5 { padding-bottom: 5px !important; } 185 | .pb-10 { padding-bottom: 10px !important; } 186 | .pb-15 { padding-bottom: 15px !important; } 187 | .pb-20 { padding-bottom: 20px !important; } 188 | 189 | .pr-0 { padding-right: 0 !important; } 190 | .pl-0 { padding-left: 0 !important; } 191 | 192 | .check-muted { color: #aeaeae !important; } 193 | 194 | 195 | /* ========================================================================== 196 | dataTables Overwrites 197 | ========================================================================== */ 198 | table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, 199 | table.dataTable.display tbody th, table.dataTable.display tbody td, 200 | table.dataTable.display tbody tr.odd > .sorting_1, 201 | table.dataTable.display tbody tr.even > .sorting_1, 202 | table.dataTable.order-column.stripe tbody tr.odd > .sorting_1, 203 | table.dataTable.order-column.stripe tbody tr.even > .sorting_1, 204 | table.dataTable.display tbody tr:hover > .sorting_1, 205 | table.dataTable.display tbody tr.odd:hover > .sorting_1, 206 | table.dataTable.display tbody tr.even:hover > .sorting_1, 207 | table.dataTable.order-column.hover tbody tr:hover > .sorting_1, 208 | table.dataTable.order-column.hover tbody tr.odd:hover > .sorting_1, 209 | table.dataTable.order-column.hover tbody tr.even:hover > .sorting_1 { 210 | background-color: #ffffff; 211 | } 212 | 213 | table.dataTable.display tbody tr.odd > .sorting_1.indev, 214 | table.dataTable.display tbody tr.even > .sorting_1.indev, 215 | table.dataTable.order-column.stripe tbody tr.odd > .sorting_1.indev, 216 | table.dataTable.order-column.stripe tbody tr.even > .sorting_1.indev, .indev { 217 | background-color: #e8f3fa !important; 218 | } 219 | 220 | table.dataTable.display tbody tr.odd > .sorting_1.inqa, 221 | table.dataTable.display tbody tr.even > .sorting_1.inqa, 222 | table.dataTable.order-column.stripe tbody tr.odd > .sorting_1.inqa, 223 | table.dataTable.order-column.stripe tbody tr.even > .sorting_1.inqa, .inqa { 224 | background-color: #fbf5de !important; 225 | } 226 | 227 | /* ========================================================================== 228 | Dashboard Boxes 229 | ========================================================================== */ 230 | .dashblocks { color: #57aa82; } 231 | .dashblocks.default { 232 | border: 1px solid #7fc6a4; 233 | background: #ffffff; 234 | } 235 | 236 | .dashblocks .dashblocksBody { 237 | font: 300 32px/38px "Muli", sans-serif; 238 | height: 60px; 239 | padding: 10px 20px; 240 | position: relative; 241 | } 242 | .dashblocks .dashblocksBody .boxIcon { 243 | color: rgba(102, 153, 204, 0.3); 244 | font-size: 32px; 245 | position: absolute; 246 | right: 20px; 247 | top: 14px; 248 | } 249 | 250 | .dashblocks .dashblocksFooter { 251 | background: #7fc6a4; 252 | border-top: 1px solid #7fc6a4; 253 | padding: 4px 20px; 254 | color: #f0f0f0; 255 | } 256 | 257 | @media (max-width:767px) { 258 | .dashblocks { margin-bottom: 10px; } 259 | } 260 | 261 | /* ========================================================================== 262 | Buttons 263 | ========================================================================== */ 264 | .btn { 265 | font-family: "Muli", sans-serif; 266 | font-weight: 400; 267 | font-size: 1em; 268 | -webkit-font-smoothing: antialiased; 269 | -moz-osx-font-smoothing: grayscale; 270 | border: none; 271 | padding: 0.65em 1.3em; 272 | } 273 | 274 | .btn-lg { padding: 1em 2em; } 275 | 276 | .btn-sm, .btn-xs { 277 | font-size: .85em; 278 | padding: .5em 1em; 279 | } 280 | .btn-xs { 281 | font-size: 10px; 282 | padding: 2px 6px; 283 | } 284 | 285 | .btn-default { background: #f2f2f2; } 286 | .btn-default:hover { background: #e6e6e6; } 287 | 288 | .btn-primary { background: #357faa; } 289 | .btn-primary:hover { background: #2f7096; } 290 | 291 | .btn-success { background: #329c51; } 292 | .btn-success:hover { background: #2c8847; } 293 | 294 | .btn-info { background: #46b8da; } 295 | .btn-info:hover { background: #31b0d5; } 296 | 297 | .btn-warning { background: #eea236; } 298 | .btn-warning:hover { background: #ec971f; } 299 | 300 | .btn-danger { background: #d43f3a; } 301 | .btn-danger:hover { background: #c9302c; } 302 | 303 | .btn-inverse { 304 | background: #3c3c3c; 305 | color: #dcdcdc; 306 | } 307 | .btn-inverse:active, .btn-inverse:focus, .btn-inverse:hover { color: #f0f0f0; } 308 | .btn-inverse:hover { background: #2f2f2f; } 309 | 310 | .btn-link { 311 | border: 0; 312 | cursor: pointer; 313 | display: inline-block; 314 | font-family: "Muli", sans-serif; 315 | font-weight: 400; 316 | font-size: 1em; 317 | line-height: 1.42857; 318 | margin-bottom: 0; 319 | padding: 0; 320 | text-align: center; 321 | vertical-align: middle; 322 | white-space: nowrap; 323 | } 324 | 325 | .btn-icon i { margin-right: 4px; } 326 | .btn-icon-alt i { margin-left: 4px; } 327 | 328 | /* ========================================================================== 329 | Forms 330 | ========================================================================== */ 331 | .form-control { 332 | background-color: #fafafa; 333 | background-image: none; 334 | border: 2px solid #eeeeee; 335 | box-shadow: none; 336 | color: #202b33; 337 | display: block; 338 | font-size: 14px; 339 | height: 35px; 340 | line-height: 1.4; 341 | padding: 5px 10px; 342 | transition: border-color 0.15s ease-in-out 0s, box-shadow 0.15s ease-in-out 0s; 343 | width: 100%; 344 | resize: vertical; 345 | } 346 | .form-control:focus { 347 | background-color: #ffffff; 348 | border-color: #d3e6f3; 349 | box-shadow: none; 350 | outline: 0 none; 351 | } 352 | .form-control.smallText { font-size: 14px; } 353 | 354 | .form-control.hasError { border: 1px solid #e9573f !important; } 355 | 356 | .inline-form { display: inline-block; } 357 | 358 | label { 359 | display: inline-block; 360 | font-weight: 300; 361 | color: #202b33; 362 | } 363 | 364 | .help-block { 365 | color: #999999; 366 | font-size: 12px; 367 | display: block; 368 | margin: 5px 0 10px 2px; 369 | } 370 | 371 | .radio-group { 372 | height: 35px; 373 | padding-top: 5px; 374 | } 375 | 376 | /* ========================================================================== 377 | Tooltips 378 | ========================================================================== */ 379 | .tooltip-inner { 380 | max-width: 600px; 381 | padding: 4px 8px; 382 | color: #ffffff; 383 | text-align: center; 384 | text-decoration: none; 385 | background-color: #40966d; 386 | font-size: 14px; 387 | font-weight: 300; 388 | font-family: "Muli", sans-serif; 389 | } 390 | .tooltip-arrow { 391 | position: absolute; 392 | width: 0; 393 | height: 0; 394 | border-color: transparent; 395 | border-style: solid; 396 | } 397 | .tooltip.top .tooltip-arrow { border-top-color: #40966d; } 398 | .tooltip.top-left .tooltip-arrow { border-top-color: #40966d; } 399 | .tooltip.top-right .tooltip-arrow { border-top-color: #40966d; } 400 | .tooltip.right .tooltip-arrow { border-right-color: #40966d; } 401 | .tooltip.left .tooltip-arrow { border-left-color: #40966d; } 402 | .tooltip.bottom .tooltip-arrow { border-bottom-color: #40966d; } 403 | .tooltip.bottom-left .tooltip-arrow { border-bottom-color: #40966d; } 404 | .tooltip.bottom-right .tooltip-arrow { border-bottom-color: #40966d; } 405 | 406 | /* ========================================================================== 407 | Alert Message Boxes 408 | ========================================================================== */ 409 | .alertMsg { 410 | padding: 10px 20px 10px 50px; 411 | position: relative; 412 | font-size: 14px; 413 | line-height: 20px; 414 | margin: 10px 0; 415 | } 416 | .alertMsg a { 417 | display: block; 418 | position: absolute; 419 | top: 6px; 420 | right: 10px; 421 | color: #fff; 422 | text-decoration: none; 423 | font-size: 14pt; 424 | opacity: 0.4; 425 | } 426 | .alertMsg a:hover { opacity: 0.8; } 427 | 428 | .alertMsg a.textlink { 429 | color: #ffffff; 430 | display: inline; 431 | font-size: 15px; 432 | left: 10px; 433 | opacity: 0.6; 434 | position: inherit; 435 | top: 0; 436 | } 437 | 438 | .alertMsg .msgIcon { 439 | font-size: 20px !important; 440 | color: #ffffff; 441 | position: absolute; 442 | top: 9px; 443 | left: 15px; 444 | } 445 | 446 | .alertMsg.default { 447 | border-left: 5px solid #dcdcdc; 448 | border-color: #cacaca; 449 | background-image: linear-gradient(#fafafa, #f0f0f0); 450 | color: #202b33; 451 | } 452 | .alertMsg.default .msgIcon { color: #999999; } 453 | .alertMsg.default a { color: #202b33; } 454 | 455 | .alertMsg.primary { 456 | border-left: 4px solid #2e79b9; 457 | border-color: #2a6496; 458 | background-image: linear-gradient(#3b8dbd, #357faa); 459 | color: #ffffff; 460 | } 461 | .alertMsg.info { 462 | border-left: 4px solid #31a3c4; 463 | border-color: #28a1c5; 464 | background-image: linear-gradient(#5bc0de, #46b8da); 465 | color: #ffffff; 466 | } 467 | .alertMsg.success { 468 | border-left: 4px solid #43a543; 469 | border-color: #3d8b3d; 470 | background-image: linear-gradient(#38af5b, #329c51); 471 | color: #ffffff; 472 | } 473 | .alertMsg.warning { 474 | border-left: 4px solid #cd851e; 475 | border-color: #df8a13; 476 | background-image: linear-gradient(#f0ad4e, #eea236); 477 | color: #ffffff; 478 | } 479 | .alertMsg.danger { 480 | border-left: 4px solid #c53734; 481 | border-color: #b52b27; 482 | background-image: linear-gradient(#d9534f, #d43f3a); 483 | color: #ffffff; 484 | } 485 | .alertMsg.inverse { 486 | border-left: 4px solid #232323; 487 | border-color: #232323; 488 | background-image: linear-gradient(#494949, #3c3c3c); 489 | color: #ffffff; 490 | } 491 | 492 | /* ========================================================================== 493 | Meowsa Overwrites 494 | ========================================================================== */ 495 | .meowsa-notification .meowsa-icon * { font-size: 28px !important; } 496 | 497 | .btn-meowsa { 498 | margin-top: 10px; 499 | font-size: 12px; 500 | line-height: 1.5; 501 | padding: 5px 10px; 502 | } 503 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /includes/flatfile.php: -------------------------------------------------------------------------------- 1 | INTEGER_COMPARISON, 22 | DATE_COL => INTEGER_COMPARISON, 23 | STRING_COL => STRING_COMPARISON, 24 | FLOAT_COL => NUMERIC_COMPARISON, 25 | ]; 26 | 27 | function get_comparison_type_for_col_type($coltype) 28 | { 29 | global $comparison_type_for_col_type; 30 | 31 | return $comparison_type_for_col_type[$coltype]; 32 | } 33 | 34 | /* 35 | * Provides simple but powerful flatfile database storage and retrieval 36 | * 37 | * @package flatfile 38 | */ 39 | class Flatfile 40 | { 41 | public $tables; 42 | public $schemata; 43 | public $datadir; 44 | 45 | public function __construct() 46 | { 47 | $this->schemata = []; 48 | } 49 | 50 | /* 51 | * Get all rows from a table 52 | * 53 | * @param string $tablename The table to get rows from 54 | * @return array The table as an array of rows, where each row is an array of columns 55 | */ 56 | public function selectAll($tablename) 57 | { 58 | if (!isset($this->tables[$tablename])) { 59 | $this->loadTable($tablename); 60 | } 61 | 62 | return $this->tables[$tablename]; 63 | } 64 | 65 | /* 66 | * Selects rows from a table that match the specified criteria 67 | * 68 | * This simulates the following SQL query: 69 | *
 70 |          *   SELECT LIMIT $limit * FROM  $tablename
 71 |          *   WHERE $whereclause
 72 |          *   ORDER BY $orderBy [ASC | DESC] [, $orderBy2 ...]
 73 |          * 
74 | * 75 | * @param string $tablename The table (file) to get the data from 76 | * @param object $whereClause Either a {@link WhereClause WhereClause} object to do selection of rows, or NULL to select all 77 | * @param mixed $limit Specifies limits for the rows returned: 78 | * @param mixed $orderBy Either an {@link OrderBy} object or an array of them, defining the sorting that should be applied (if an array, then the first object in the array is the first key to sort on etc). Use NULL for no sorting. 79 | * @return array The matching data, as an array of rows, where each row is an array of columns 80 | */ 81 | public function selectWhere($tablename, $whereClause, $limit = -1, $orderBy = null) 82 | { 83 | if (!isset($this->tables[$tablename])) { 84 | $this->loadTable($tablename); 85 | } 86 | 87 | $table = $this->selectAll($tablename); 88 | 89 | $schema = $this->getSchema($tablename); 90 | if ($orderBy !== null) { 91 | usort($table, $this->getOrderByFunction($orderBy, $schema)); 92 | } 93 | 94 | $results = []; 95 | $count = 0; 96 | 97 | if ($limit == -1) { 98 | $limit = [0, -1]; 99 | } elseif (!is_array($limit)) { 100 | $limit = [0, $limit]; 101 | } 102 | 103 | foreach ($table as $row) { 104 | if ($whereClause === null || $whereClause->testRow($row, $schema)) { 105 | if ($count >= $limit[0]) { 106 | $results[] = $row; 107 | } 108 | $count++; 109 | if (($count >= $limit[1]) && ($limit[1] != -1)) { 110 | break; 111 | } 112 | } 113 | } 114 | 115 | return $results; 116 | } 117 | 118 | /* 119 | * Select a row using a unique ID 120 | * 121 | * @param string $tablename The table to get data from 122 | * @param string $idField The index of the field containing the ID 123 | * @param string $id The ID to search for 124 | * @return array The row of the table as an array 125 | */ 126 | public function selectUnique($tablename, $idField, $id) 127 | { 128 | $result = $this->selectWhere($tablename, new SimpleWhereClause($idField, '=', $id)); 129 | if (count($result) > 0) { 130 | return $result[0]; 131 | } else { 132 | return []; 133 | } 134 | } 135 | 136 | /* 137 | * Get a lock for writing a file 138 | * 139 | * @access private 140 | */ 141 | public function getLock($tablename) 142 | { 143 | ignore_user_abort(true); 144 | $fp = fopen($this->datadir.$tablename.'.lock', 'w'); 145 | if (!flock($fp, LOCK_EX)) { 146 | // log error? 147 | } 148 | $this->loadTable($tablename); 149 | 150 | return $fp; 151 | } 152 | 153 | /* 154 | * Release a lock 155 | * 156 | * @access private 157 | */ 158 | public function releaseLock($lockfp) 159 | { 160 | flock($lockfp, LOCK_UN); 161 | ignore_user_abort(false); 162 | } 163 | 164 | /* 165 | * Inserts a row with an automatically generated ID 166 | * 167 | * @param string $tablename The table to insert data into 168 | * @param int $idField The index of the field which is the ID field 169 | * @param array $newRow The new row to add to the table 170 | * @return int The newly assigned ID 171 | */ 172 | public function insertWithAutoId($tablename, $idField, $newRow) 173 | { 174 | $lockfp = $this->getLock($tablename); 175 | $rows = $this->selectWhere( 176 | $tablename, 177 | null, 178 | 1, 179 | new OrderBy($idField, DESCENDING, INTEGER_COMPARISON) 180 | ); 181 | if ($rows) { 182 | $newId = $rows[0][$idField] + 1; 183 | } else { 184 | $newId = 1; 185 | } 186 | $newRow[$idField] = $newId; 187 | $this->tables[$tablename][] = $newRow; 188 | $this->writeTable($tablename); 189 | $this->releaseLock($lockfp); 190 | 191 | return $newId; 192 | } 193 | 194 | /* 195 | * Inserts a row in a table 196 | * 197 | * @param string $tablename The table to insert data into 198 | * @param array $newRow The new row to add to the table 199 | */ 200 | public function insert($tablename, $newRow) 201 | { 202 | $lockfp = $this->getLock($tablename); 203 | $this->tables[$tablename][] = $newRow; 204 | $this->writeTable($tablename); 205 | $this->releaseLock($lockfp); 206 | } 207 | 208 | /* 209 | * Updates an existing row using a unique ID 210 | * 211 | * @param string $tablename The table to update 212 | * @param int $idField The index of the field which is the ID field 213 | * @param array $updatedRow The updated row to add to the table 214 | */ 215 | public function updateRowById($tablename, $idField, $updatedRow) 216 | { 217 | $this->updateSetWhere( 218 | $tablename, 219 | $updatedRow, 220 | new SimpleWhereClause($idField, '=', $updatedRow[$idField]) 221 | ); 222 | } 223 | 224 | /* 225 | * Updates fields in a table for rows that match the provided criteria 226 | * 227 | * @param string $tablename The table to update 228 | * @param array $newFields A hashtable (with integer keys) of fields to update 229 | * @param WhereClause $whereClause The criteria or NULL to update all rows 230 | */ 231 | public function updateSetWhere($tablename, $newFields, $whereClause) 232 | { 233 | $schema = $this->getSchema($tablename); 234 | $lockfp = $this->getLock($tablename); 235 | for ($i = 0; $i < count($this->tables[$tablename]); $i++) { 236 | if ($whereClause === null || $whereClause->testRow($this->tables[$tablename][$i], $schema)) { 237 | foreach ($newFields as $k => $v) { 238 | $this->tables[$tablename][$i][$k] = $v; 239 | } 240 | } 241 | } 242 | $this->writeTable($tablename); 243 | $this->releaseLock($lockfp); 244 | $this->loadTable($tablename); 245 | } 246 | 247 | /* 248 | * Deletes all rows in a table that match specified criteria 249 | * 250 | * @param string $tablename The table to alter 251 | * @param object $whereClause. {@link WhereClause WhereClause} object that will select 252 | * rows to be deleted. All rows are deleted if $whereClause === NULL 253 | */ 254 | public function deleteWhere($tablename, $whereClause) 255 | { 256 | $schema = $this->getSchema($tablename); 257 | $lockfp = $this->getLock($tablename); 258 | for ($i = count($this->tables[$tablename]) - 1; $i >= 0; $i--) { 259 | if ($whereClause === null || $whereClause->testRow($this->tables[$tablename][$i], $schema)) { 260 | unset($this->tables[$tablename][$i]); 261 | } 262 | } 263 | $this->writeTable($tablename); 264 | $this->releaseLock($lockfp); 265 | $this->loadTable($tablename); // reset array indexes 266 | } 267 | 268 | /* 269 | * Delete all rows in a table 270 | * 271 | * @param string $tablename The table to alter 272 | */ 273 | public function deleteAll($tablename) 274 | { 275 | $this->deleteWhere($tablename, null); 276 | } 277 | 278 | /* 279 | * Gets a function that can be passed to usort to do the ORDER BY clause 280 | * 281 | * @param mixed $orderBy Either an OrderBy object or an array of them 282 | * @return string function name 283 | */ 284 | public function getOrderByFunction($orderBy, $rowSchema = null) 285 | { 286 | $orderer = new Orderer($orderBy, $rowSchema); 287 | 288 | return [&$orderer, 'compare']; 289 | } 290 | 291 | public function loadTable($tablename) 292 | { 293 | $filedata = @file($this->datadir.$tablename); 294 | $table = []; 295 | if (is_array($filedata)) { 296 | foreach ($filedata as $line) { 297 | $line = rtrim($line, "\n"); 298 | $table[] = explode("\t", $line); 299 | } 300 | } 301 | $this->tables[$tablename] = $table; 302 | } 303 | 304 | public function writeTable($tablename) 305 | { 306 | $output = ''; 307 | 308 | foreach ($this->tables[$tablename] as $row) { 309 | $keys = array_keys($row); 310 | rsort($keys, SORT_NUMERIC); 311 | $max = $keys[0]; 312 | for ($i = 0; $i <= $max; $i++) { 313 | if ($i > 0) { 314 | $output .= "\t"; 315 | } 316 | $data = (!isset($row[$i]) ? '' : $row[$i]); 317 | $output .= str_replace(["\t", "\r", "\n"], [''], $data); 318 | } 319 | $output .= "\n"; 320 | } 321 | $fp = @fopen($this->datadir.$tablename, 'w'); 322 | fwrite($fp, $output, strlen($output)); 323 | fclose($fp); 324 | } 325 | 326 | /* 327 | * Adds a schema definition to the DB for a specified regular expression 328 | * 329 | * @param string $fileregex A regular expression used to match filenames 330 | * @param string $rowSchema An array specifying the column types for data 331 | * files that match the regex, using constants defined in flatfile_utils.php 332 | */ 333 | public function addSchema($fileregex, $rowSchema) 334 | { 335 | array_push($this->schemata, [$fileregex, $rowSchema]); 336 | } 337 | 338 | // Retrieves the schema for a given filename 339 | public function getSchema($filename) 340 | { 341 | foreach ($this->schemata as $rowSchemaPair) { 342 | $fileregex = $rowSchemaPair[0]; 343 | if (preg_match($fileregex, $filename)) { 344 | return $rowSchemaPair[1]; 345 | } 346 | } 347 | } 348 | } 349 | 350 | /* 351 | * equivalent of strcmp for comparing integers, used internally for sorting and comparing 352 | */ 353 | function intcmp($a, $b) 354 | { 355 | return (int) $a - (int) $b; 356 | } 357 | 358 | /* 359 | * equivalent of strcmp for comparing floats, used internally for sorting and comparing 360 | */ 361 | function numcmp($a, $b) 362 | { 363 | return (float) $a - (float) $b; 364 | } 365 | 366 | /* 367 | * Used to test rows in a database table, like the WHERE clause in an SQL statement. 368 | * 369 | * @abstract 370 | * @package flatfile 371 | */ 372 | class WhereClause 373 | { 374 | /* 375 | * Tests a table row object 376 | * @abstract 377 | * @param array $row The row to test 378 | * @param array $rowSchema An optional array specifying the schema of the table, using the INT_COL, STRING_COL etc constants 379 | * @return bool True if the $row passes the WhereClause 380 | * selection criteria, false otherwise 381 | */ 382 | public function testRow($row, $rowSchema = null) 383 | { 384 | } 385 | } 386 | 387 | /* 388 | * Negates a where clause 389 | * @package flatfile 390 | */ 391 | class NotWhere extends WhereClause 392 | { 393 | public $clause; 394 | 395 | /* 396 | * Contructs a new NotWhere object 397 | */ 398 | public function __construct($whereclause) 399 | { 400 | $this->clause = $whereclause; 401 | } 402 | 403 | public function testRow($row, $rowSchema = null) 404 | { 405 | return !$this->clause->testRow($row, $rowSchema); 406 | } 407 | } 408 | 409 | /* 410 | * Implements a single WHERE clause that does simple comparisons of a field with a value. 411 | * 412 | * @package flatfile 413 | */ 414 | class SimpleWhereClause extends WhereClause 415 | { 416 | public $field; 417 | public $operator; 418 | public $value; 419 | public $compare_type; 420 | 421 | /* 422 | * Creates a new {@link WhereClause WhereClause} object that does a comparison 423 | * of a field and a value. 424 | * 425 | * @param int $field The index (in the table row) of the field to test 426 | * @param string $operator The comparison operator, one of "=", "!=", "<", ">", "<=", ">=" 427 | * @param mixed $value The value to compare to. 428 | * @param string $compare_type The comparison method to use - either 429 | * STRING_COMPARISON (default), NUMERIC COMPARISON or INTEGER_COMPARISON 430 | * 431 | */ 432 | public function __construct($field, $operator, $value, $compare_type = DEFAULT_COMPARISON) 433 | { 434 | $this->field = $field; 435 | $this->operator = $operator; 436 | $this->value = $value; 437 | $this->compare_type = $compare_type; 438 | } 439 | 440 | public function testRow($tablerow, $rowSchema = null) 441 | { 442 | if ($this->field < 0) { 443 | return true; 444 | } 445 | 446 | $cmpfunc = $this->compare_type; 447 | if ($cmpfunc == DEFAULT_COMPARISON) { 448 | if ($rowSchema !== null) { 449 | $cmpfunc = get_comparison_type_for_col_type($rowSchema[$this->field]); 450 | } else { 451 | $cmpfunc = STRING_COMPARISON; 452 | } 453 | } 454 | 455 | if ($this->field >= count($tablerow)) { 456 | $dbval = ''; 457 | } else { 458 | $dbval = $tablerow[$this->field]; 459 | } 460 | $cmp = $cmpfunc($dbval, $this->value); 461 | if ($this->operator == '=') { 462 | return $cmp == 0; 463 | } elseif ($this->operator == '!=') { 464 | return $cmp != 0; 465 | } elseif ($this->operator == '>') { 466 | return $cmp > 0; 467 | } elseif ($this->operator == '<') { 468 | return $cmp < 0; 469 | } elseif ($this->operator == '<=') { 470 | return $cmp <= 0; 471 | } elseif ($this->operator == '>=') { 472 | return $cmp >= 0; 473 | } 474 | 475 | return false; 476 | } 477 | } 478 | 479 | /* 480 | * {@link WhereClause WhereClause} class to match a value from a list of items 481 | * 482 | * @package flatfile 483 | */ 484 | class ListWhereClause extends WhereClause 485 | { 486 | public $field; 487 | public $list; 488 | public $compareAs; 489 | 490 | /* 491 | * Creates a new ListWhereClause object 492 | * 493 | * @param int $field Field to match 494 | * @param array $list List of items 495 | * @param string $compare_type Comparison type, string by default. 496 | */ 497 | public function __construct($field, $list, $compare_type = DEFAULT_COMPARISON) 498 | { 499 | $this->list = $list; 500 | $this->field = (int) $field; 501 | $this->compareAs = $compare_type; 502 | } 503 | 504 | public function testRow($tablerow, $rowSchema = null) 505 | { 506 | $func = $this->compareAs; 507 | if ($func == DEFAULT_COMPARISON) { 508 | if ($rowSchema) { 509 | $func = get_comparison_type_for_col_type($rowSchema[$this->field]); 510 | } else { 511 | $func = STRING_COMPARISON; 512 | } 513 | } 514 | 515 | foreach ($this->list as $item) { 516 | if ($func($tablerow[$this->field], $item) == 0) { 517 | return true; 518 | } 519 | } 520 | 521 | return false; 522 | } 523 | } 524 | 525 | /* 526 | * Abstract class that combines zero or more {@link WhereClause WhereClause} objects together. 527 | * 528 | * @package flatfile 529 | */ 530 | class CompositeWhereClause extends WhereClause 531 | { 532 | public $clauses = []; 533 | 534 | /* 535 | * Add a {@link WhereClause WhereClause} to the list of clauses to be used for testing 536 | * 537 | * @param WhereClause $whereClause The WhereClause object to add 538 | */ 539 | public function add($whereClause) 540 | { 541 | $this->clauses[] = $whereClause; 542 | } 543 | } 544 | 545 | /* 546 | * {@link CompositeWhereClause CompositeWhereClause} that does an OR on all its child WhereClauses. 547 | * 548 | * @package flatfile 549 | */ 550 | class OrWhereClause extends CompositeWhereClause 551 | { 552 | public function testRow($tablerow, $rowSchema = null) 553 | { 554 | foreach ($this->clauses as $clause) { 555 | if ($clause->testRow($tablerow, $rowSchema)) { 556 | return true; 557 | } 558 | } 559 | 560 | return false; 561 | } 562 | 563 | /* 564 | * Creates a new OrWhereClause 565 | * 566 | * @param WhereClause $whereClause,... optional unlimited list of WhereClause objects to be added 567 | */ 568 | public function __construct() 569 | { 570 | $this->clauses = func_get_args(); 571 | } 572 | } 573 | 574 | /* 575 | * {@link CompositeWhereClause CompositeWhereClause} that does an AND on all its child WhereClauses. 576 | * 577 | * @package flatfile 578 | */ 579 | class AndWhereClause extends CompositeWhereClause 580 | { 581 | public function testRow($tablerow, $rowSchema = null) 582 | { 583 | foreach ($this->clauses as $clause) { 584 | if (!$clause->testRow($tablerow, $rowSchema)) { 585 | return false; 586 | } 587 | } 588 | 589 | return true; 590 | } 591 | 592 | /* 593 | * Creates a new AndWhereClause 594 | * 595 | * @param WhereClause $whereClause,... optional unlimited list of WhereClause objects to be added 596 | */ 597 | public function __construct() 598 | { 599 | $this->clauses = func_get_args(); 600 | } 601 | } 602 | 603 | /* 604 | * Stores information about an ORDER BY clause 605 | * 606 | * @package flatfile 607 | */ 608 | class OrderBy 609 | { 610 | public $field; 611 | public $orderType; 612 | public $compareAs; 613 | 614 | /* 615 | * Creates a new OrderBy structure 616 | * 617 | * @param int $field The index of the field to order by 618 | * @param int $orderType ASCENDING or DESCENDING 619 | * @param int $compareAs Comparison type: DEFAULT_COMPARISON, STRING_COMPARISON, INTEGER_COMPARISION, 620 | * or NUMERIC_COMPARISON, or the name of a user defined function that you want to use for doing the comparison. 621 | */ 622 | public function __construct($field, $orderType, $compareAs = DEFAULT_COMPARISON) 623 | { 624 | $this->field = $field; 625 | $this->orderType = $orderType; 626 | $this->compareAs = $compareAs; 627 | } 628 | } 629 | 630 | /* 631 | * Implements the sorting defined by an array of OrderBy objects. This class is used by {@link Flatfile::selectWhere()} 632 | * 633 | * @access private 634 | * @package flatfile 635 | */ 636 | class Orderer 637 | { 638 | public $orderByList; 639 | 640 | /* 641 | * Creates new Orderer that will provide a sort function 642 | * 643 | * @param mixed $orderBy An OrderBy object or an array of them 644 | * @param array $rowSchema Option row schema 645 | */ 646 | public function __construct($orderBy, $rowSchema = null) 647 | { 648 | if (!is_array($orderBy)) { 649 | $orderBy = [$orderBy]; 650 | } 651 | if ($rowSchema) { 652 | foreach ($orderBy as $index => $discard) { 653 | $item = &$orderBy[$index]; 654 | if ($item->compareAs == DEFAULT_COMPARISON) { 655 | $item->compareAs = get_comparison_type_for_col_type($rowSchema[$item->field]); 656 | } 657 | } 658 | } 659 | $this->orderByList = $orderBy; 660 | } 661 | 662 | /* 663 | * Compares two table rows using the comparisons defined by the OrderBy 664 | * objects. This function is of the type that can be used passed to usort(). 665 | */ 666 | public function compare($row1, $row2) 667 | { 668 | return $this->compare_priv($row1, $row2, 0); 669 | } 670 | 671 | /* 672 | * @access private 673 | */ 674 | public function compare_priv($row1, $row2, $index) 675 | { 676 | $orderBy = $this->orderByList[$index]; 677 | $cmpfunc = $orderBy->compareAs; 678 | if ($cmpfunc == DEFAULT_COMPARISON) { 679 | $cmpfunc = STRING_COMPARISON; 680 | } 681 | $cmp = $orderBy->orderType * $cmpfunc($row1[$orderBy->field], $row2[$orderBy->field]); 682 | if ($cmp == 0) { 683 | if ($index == (count($this->orderByList) - 1)) { 684 | return 0; 685 | } else { 686 | return $this->compare_priv($row1, $row2, $index + 1); 687 | } 688 | } else { 689 | return $cmp; 690 | } 691 | } 692 | } 693 | --------------------------------------------------------------------------------