├── LICENSE ├── README.md ├── ajax-npage.php ├── css ├── admin.css └── front.css ├── fonts ├── icomoon.eot ├── icomoon.svg ├── icomoon.ttf └── icomoon.woff ├── img ├── attachment.png ├── attachment.svg ├── bubble.png ├── bubble.svg ├── calendar.png ├── logo.16.png ├── logo.32.png └── logo.32c.png ├── inc ├── classes │ ├── controller.php │ ├── models.php │ └── tools.php ├── controllers │ ├── admin.php │ ├── admin │ │ ├── dashboard.php │ │ ├── project_edit.php │ │ ├── project_list.php │ │ ├── project_view.php │ │ └── settings.php │ ├── front.php │ └── front │ │ ├── project_list.php │ │ ├── project_users.php │ │ ├── project_view.php │ │ ├── task_edit.php │ │ ├── task_history.php │ │ ├── task_list.php │ │ ├── task_recent.php │ │ ├── task_status.php │ │ └── task_view.php ├── install.php ├── models │ ├── item.php │ ├── log.php │ ├── project.php │ └── user.php └── views │ ├── admin │ ├── about.php │ ├── dashboard.php │ ├── project_edit.php │ ├── project_list.php │ └── settings.php │ └── front │ ├── edit.php │ ├── list.php │ ├── nav.php │ ├── project_list.php │ ├── project_view.php │ ├── recent.php │ ├── status.php │ ├── task.php │ ├── task_history.php │ └── task_list.php ├── index.php ├── js ├── admin.js └── front.js ├── languages ├── taskfreak-ar.mo ├── taskfreak-ar.po ├── taskfreak-de_DE.mo ├── taskfreak-de_DE.po ├── taskfreak-es_ES.mo ├── taskfreak-es_ES.po ├── taskfreak-fr_FR.mo ├── taskfreak-fr_FR.po ├── taskfreak-it_IT.mo ├── taskfreak-it_IT.po ├── taskfreak-pl_PL.mo ├── taskfreak-pl_PL.po ├── taskfreak-pt_BR.mo ├── taskfreak-pt_BR.po ├── taskfreak-ru_RU.mo └── taskfreak-ru_RU.po ├── readme.txt ├── tests ├── index.php ├── run-tests.sh ├── tfwp-test.mysql └── tfwp-test.php └── uninstall.php /README.md: -------------------------------------------------------------------------------- 1 | tfwp 2 | ==== 3 | 4 | TaskFreak! plugin for WordPress 5 | 6 | This is the free version of this plugin for the popular WordPress web software and blog system. 7 | 8 | Create projects, give rights to your WP users and any visitors. 9 | Create and assign tasks, discuss them and follow their progress by status. 10 | 11 | Advantages : 12 | 13 | - Easy to install 14 | - Full integration with Wordpress users and roles 15 | - Create public and private projects 16 | - Add attachments and comment your tasks 17 | - Mobile devices friendly (smartphones, tablets) 18 | - Users are associated to projects by WP roles 19 | 20 | -------------------------------------------------------------------------------- /ajax-npage.php: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /css/admin.css: -------------------------------------------------------------------------------- 1 | .welcome-panel-column-container { 2 | margin: 30px 0; 3 | } 4 | .tfk-dash-col1 { 5 | width: 30%; 6 | margin-right: 8%; 7 | } 8 | .tfk-dash-col2 { 9 | width:60%; 10 | } 11 | table.tfk_stats { 12 | border-collapse: collapse; 13 | } 14 | table.tfk_stats th, table.tfk_stats td { 15 | padding: 2px 10px; 16 | } 17 | table.tfk_stats thead td { 18 | border-bottom: 1px solid #ddd; 19 | } 20 | table.tfk_stats thead th { 21 | font-weight: normal; 22 | border-left: 1px solid #e9e9e9; 23 | border-bottom: 1px solid #ddd; 24 | } 25 | table.tfk_stats tbody th { 26 | text-align: left; 27 | font-weight: normal; 28 | } 29 | table.tfk_stats tbody td { 30 | font-weight: bold; 31 | text-align: center; 32 | border-left: 1px solid #e9e9e9; 33 | } 34 | table.tfk_stats tbody tr.sep th, table.tfk_stats tbody tr.sep td { 35 | border-top: 1px solid #e9e9e9; 36 | } 37 | table.tfk_stats tbody tr:hover th, table.tfk_stats tbody tr:hover td { 38 | background-color: #fff; 39 | } 40 | .tzn_option_section { 41 | padding: 8px 10px 8px; 42 | border-width: 1px 0; 43 | border-color: #dfdfdf; 44 | border-style: solid; 45 | } 46 | .tzn_option_section:first-child { 47 | border-top-width: 0; 48 | } 49 | 50 | .tzn_option_section-last { 51 | border-bottom-width: 0; 52 | } 53 | .tzn_option_section label { 54 | vertical-align: baseline; 55 | } 56 | .tzn_option_section .tzn_option_display { 57 | font-weight: bold; 58 | line-height: 1.4em; 59 | } 60 | .tzn_option_panel { 61 | padding: .5em 0; 62 | } 63 | .tfk_subsubsub li { 64 | display: inline; 65 | } 66 | .tfk_subsubsub li + li:before { 67 | content: " | "; 68 | font-weight: normal; 69 | } 70 | .tfk_subsubsub a { 71 | text-decoration: none; 72 | } 73 | .tfk_subsubsub a.current { 74 | font-weight: bold; 75 | color: #555; 76 | } -------------------------------------------------------------------------------- /css/front.css: -------------------------------------------------------------------------------- 1 | .tfk_projects th { 2 | white-space: nowrap; 3 | } 4 | #tfk_tasksheet * { 5 | padding: 0; margin: 0; 6 | margin-top: 0; margin-bottom: 0; margin-left: 0; margin-right: 0; 7 | text-indent: 0; background: none; 8 | /* impulse theme */ box-sizing: content-box; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; 9 | } 10 | #tfk_tasksheet li:before, #tfk_tasksheet li:after { display: none; } /* buttercream and spun themes */ 11 | #tfk_tasksheet li i { display: none; } /* montezuma theme adds an in JS */ 12 | 13 | .tfk_hid { display: none !important; } 14 | 15 | #tfk_tasksheet { display: table; width: 100%; border-collapse: collapse; margin: 0 !important; } 16 | #tfk_tasksheet ul { margin: 0 0 0.1em 0; padding: 0; } 17 | #tfk_tasksheet li { list-style: none !important; margin-left: 0 !important; } 18 | body.rtl #tfk_tasksheet li { margin-left: initial; margin-right: 0 !important; } 19 | #tfk_tasksheet > li { display: table-row; margin: 0; border-bottom: 1px solid #ccc; } 20 | #tfk_tasksheet > li > ul > li { display: table-cell; padding: 0.7em 0.4em 1em 0.4em; } 21 | #tfk_tasksheet sup { font-size: 0.5em; } 22 | 23 | /* 1st column width depends on avatar size */ 24 | #tfk_tasksheet .tfk_col1 { vertical-align: top; } 25 | .tfk_avatar_32 { min-width: 38px; } 26 | .tfk_avatar_48 { min-width: 54px; } 27 | .tfk_avatar_64 { min-width: 70px; } 28 | 29 | /* 2nd column needs as much space as possible */ 30 | #tfk_tasksheet .tfk_col2 { width: 100%; } 31 | #tfk_tasksheet .tfk_pr1 .tfk_pri, 32 | #tfk_tasksheet .tfk_pr2 .tfk_pri, 33 | #tfk_tasksheet .tfk_pr3 .tfk_pri, 34 | #tfk_task_details .tfk_pri { display: inline; font-size: 0.9em; padding: 0.15em 0.5em; color: white; margin-right: 0.2em; } 35 | #tfk_tasksheet .tfk_pr1 .tfk_pri, #tfk_task_details .tfk_pr1, #tfk_select_prio_color.tfk_pr1 { background-color: red; } 36 | #tfk_tasksheet .tfk_pr2 .tfk_pri, #tfk_task_details .tfk_pr2, #tfk_select_prio_color.tfk_pr2 { background-color: orange; } 37 | #tfk_tasksheet .tfk_pr3 .tfk_pri, #tfk_task_details .tfk_pr3, #tfk_select_prio_color.tfk_pr3 { background-color: green; } 38 | #tfk_tasksheet .tfk_prox { display: block; max-width: 100%; text-indent: 110%; overflow: hidden; height: 0.4em; padding: 0; margin-bottom: 0.5em; } 39 | 40 | #tfk_tasksheet .tfk_pr1 .tfk_prox { background-color: red; background-image: linear-gradient(to right, red, white) } 41 | #tfk_tasksheet .tfk_pr2 .tfk_prox { background-color: orange; background-image: linear-gradient(to right, orange, white) } 42 | #tfk_tasksheet .tfk_pr3 .tfk_prox { background-color: green; background-image: linear-gradient(to right, green, white) } 43 | 44 | #tfk_tasksheet .tfk_pr1.tfk_late .tfk_prox { background-image: linear-gradient(to right, red, red) } 45 | #tfk_tasksheet .tfk_pr2.tfk_late .tfk_prox { background-image: linear-gradient(to right, orange, orange) } 46 | #tfk_tasksheet .tfk_pr3.tfk_late .tfk_prox { background-image: linear-gradient(to right, green, green) } 47 | #tfk_tasksheet .tfk_none .tfk_prox { background: #eee; } 48 | 49 | #tfk_tasksheet .tfk_pr1 .tfk_desc.tfk_size { font-size: 150%; } 50 | #tfk_tasksheet .tfk_pr2 .tfk_desc.tfk_size { font-size: 120%; } 51 | 52 | #tfk_tasksheet .tfk_desc, #tfk_tasksheet .tfk_prj { display: block; } 53 | #tfk_tasksheet .tfk_prj { margin: 0.3em 0; font-size: 0.9em; } 54 | #tfk_tasksheet .tfk_usr, #tfk_tasksheet .tfk_upd { display: inline; } 55 | #tfk_tasksheet .tfk_upd { font-size: 0.8em; color: #777; white-space: nowrap; } 56 | #tfk_tasksheet .tfk_late .tfk_desc a, #tfk_tasksheet .tfk_late .tfk_due { color: red; font-weight: bold; } 57 | #tfk_tasksheet .tfk_urg .tfk_desc a, #tfk_tasksheet .tfk_urg .tfk_due { font-weight: bold; } 58 | #tfk_tasksheet .tfk_usr { font-size: 0.9em; margin-right: 0.2em; } 59 | #tfk_tasksheet .tfk_usr.unassigned { font-style: italic; } 60 | 61 | /* 3rd column */ 62 | #tfk_tasksheet .tfk_col3 { width: 60px; text-align: right; white-space: nowrap; } 63 | #tfk_tasksheet .tfk_com { display: block; margin-bottom: 1.3em; } 64 | #tfk_tasksheet .tfk_com a { 65 | padding: 0.4em 0.7em 1em 0.7em; 66 | font-size: 0.8em; color: white; font-weight: bold; text-decoration: none; 67 | background: url(../img/bubble.png) 50% no-repeat; 68 | background: linear-gradient(transparent, transparent), url(../img/bubble.svg) 50% no-repeat; 69 | } 70 | #tfk_tasksheet .tfk_priv0, #tfk_tasksheet .tfk_priv1, #tfk_tasksheet .tfk_priv2 { 71 | display: inline-block; width: 14px; height: 20px; 72 | overflow: hidden; text-indent: 110%; 73 | } 74 | #tfk_tasksheet .tfk_priv0 { 75 | background: url(images/priv0.png) 50% no-repeat; 76 | background: linear-gradient(transparent, transparent), url(images/priv0.svg) no-repeat; 77 | } 78 | #tfk_tasksheet .tfk_priv1 { 79 | background: url(images/priv1.png) 50% no-repeat; 80 | background: linear-gradient(transparent, transparent), url(images/priv1.svg) no-repeat; 81 | } 82 | #tfk_tasksheet .tfk_priv2 { 83 | background: url(images/priv2.png) 50% no-repeat; 84 | background: linear-gradient(transparent, transparent), url(images/priv2.svg) no-repeat; 85 | } 86 | #tfk_tasksheet .tfk_atch { 87 | display: inline-block; 88 | font-size: 0.8em; 89 | } 90 | #tfk_tasksheet .tfk_atch a { 91 | font-size: 0.8em; padding: 5px 0 5px 14px; 92 | background: url(../img/attachment.png) 0 50% no-repeat; 93 | background: linear-gradient(transparent, transparent), url(../img/attachment.svg) 0 50% no-repeat; 94 | } 95 | 96 | 97 | /* 4th column */ 98 | #tfk_tasksheet .tfk_col4 { min-width: 110px; margin-right: 60px; text-align: center; } 99 | #tfk_tasksheet .tfk_due { margin-bottom: 0.7em; white-space: nowrap; } 100 | #tfk_tasksheet .tfk_sts { display: inline-block; width: 80px; height: 26px; white-space: nowrap; } 101 | #tfk_tasksheet .tfk_sts a { 102 | display: inline-block; 103 | margin: 0; 104 | width: 24px; height: 24px; text-indent: 110%; white-space: nowrap; overflow: hidden; 105 | border: 1px solid white; outline: 1px solid #ddd; margin-right: 1px; 106 | } 107 | #tfk_tasksheet .tfk_sts_lbl { display: block; font-size: 0.9em; white-space: nowrap; } 108 | #tfk_tasksheet .tfk_sts a + a + a { margin-right: 0; } 109 | #tfk_tasksheet .tfk_sts0 { background-color: white; } 110 | #tfk_tasksheet .tfk_sts1, #tfk_tasksheet .tfk_sts2, #tfk_tasksheet .tfk_sts3 { background-color: #bbb; } 111 | 112 | #tfk_filters { font-size: 1.2em; margin-left: 0; padding: 0; } 113 | #tfk_subfilters { margin-left: 0; padding: 0; } 114 | #tfk_filters li, #tfk_subfilters li, #tfk_breadcrumb li { list-style-type: none; display: inline; margin: 0; line-height: normal; } 115 | #tfk_subfilters { line-height: normal; } 116 | #tfk_filters li + li:before, #tfk_subfilters li + li:before { content: " | "; font-weight: normal; } 117 | #tfk_breadcrumb li + li:before { content: " > "; font-weight: bold; } 118 | #tfk_breadcrumb { padding-left: 0; } 119 | #tfk_filters li.tfk_selected_filter, #tfk_subfilters li.tfk_selected_filter { font-weight: bold; } 120 | #tfk_new_task { float: right; margin-left: 2em; line-height: 1.5em; font-weight: bold; } 121 | 122 | #tfk_task_count { float: right; font-size: 0.8em; } 123 | body.rtl #tfk_task_count { float: left; } 124 | #tfk_sort_criteria { padding-bottom: 1.5em; } 125 | #tfk_sort_criteria ul, #tfk_sort_criteria_js { display: inline; font-size: 0.9em; } 126 | #tfk_sort_criteria li { list-style-type: none; display: inline; margin: 0 0 0 0.5em; } 127 | .tfk_selected_order { font-weight: bold; } 128 | 129 | #tfk_pager { margin: 2em 0; } 130 | #tfk_pager ul, #tfk_pager li { display: inline; } 131 | #tfk_pager li { margin: 0 0 0 0.5em; } 132 | .tfk_current_page a { font-weight: bold; background-color: #eee; color: #555; border: 1px solid #ccc; padding: 0.2em; } 133 | 134 | #tfk_page_size { margin: 2em 0; } 135 | #tfk_page_size ul, #tfk_page_size li { display: inline; } 136 | #tfk_page_size li { margin: 0 0 0 0.5em; } 137 | #tfk_page_size_js { font-size: 0.9em; } 138 | .tfk_selected_page_size { font-weight: bold; } 139 | 140 | .tfk_warn, .tfk_err, .tfk_ok { padding: 0.5em; box-shadow: 3px 3px 6px #ccc; } 141 | .tfk_warn { background-color: #fdf6ad; border: 1px solid orange; } 142 | .tfk_err { background-color: #f8a2a2; border: 1px solid red; } 143 | .tfk_ok { background-color: #bceaa6; border: 1px solid green; } 144 | 145 | /* task view and edit */ 146 | #tfk_task_edit_comment { float: right; text-align: right; } 147 | body.rtl #tfk_task_edit_comment { float: left; } 148 | #tfk_task_edit { display: block; } 149 | #tfk_task_author_pic { float: left; padding: 0 1em 1em 0; } 150 | body.rtl #tfk_task_author_pic { float: right; padding: 0 0 1em 1em; } 151 | #tfk_task_project_name { font-weight: bold; } 152 | .tfk_task_head { margin: 0 !important; } 153 | #tfk_task_details { clear: both; width: calc(100% - 2em); display: table; padding: 1em 0 1em 2em; background-color: #eee; } 154 | #tfk_task_details > p { display: table-cell; padding-right: 2em; } 155 | #tfk_task_details > p > span { display: block; } 156 | #tfk_task_details > p > span:first-child { display: block; color: #888; white-space: nowrap; } 157 | #tfk_task_history_toggle { 158 | text-decoration: none; 159 | display: block; 160 | opacity: 0.5; 161 | width: 20px; 162 | height: 30px; 163 | overflow: hidden; 164 | white-space: nowrap; 165 | } 166 | #tfk_task_history_toggle:before { 167 | font-family: 'icomoon'; 168 | content: "\e004"; 169 | font-size: 20px; 170 | } 171 | #tfk_task_history_toggle.tfk_task_history_hidden { 172 | opacity: 1; 173 | } 174 | #tfk_task_history { max-width: 90%; padding: 0; margin: 2em; } 175 | #tfk_task_history td { padding: 0 0.2em 0 0; } 176 | #tfk_task_description { margin-top: 2em; } 177 | #tfk_add_comment { margin: 0; } 178 | #tfk_add_file { margin: 2em 0 1em 0; } 179 | #tfk_file_1, #tfk_file_2, #tfk_file_3, #tfk_file_4, #tfk_file_5 { list-style-type: none; } 180 | #tfk_file_2, #tfk_file_3, #tfk_file_4, #tfk_file_5, 181 | #tfk_edit_task_form #tfk_file_2, 182 | #tfk_edit_task_form #tfk_file_3, 183 | #tfk_edit_task_form #tfk_file_4, 184 | #tfk_edit_task_form #tfk_file_5 { display: none; } 185 | .tfk_file_more { margin-left: 2em; } 186 | .tfk_file_reset { margin-left: 2em; font-weight: bold; } 187 | #tfk_task_details .tfk_sts { white-space: nowrap; } 188 | #tfk_task_details .tfk_comm_user { float: left; width: 60px; } 189 | #tfk_comments + ul { padding: 0; display: table; border-bottom: 1px solid #ccc; } 190 | .tfk_comment { display: table-row; } 191 | .tfk_comm_user, .tfk_comm_user + div { display: table-cell; vertical-align: top; border-top: 1px solid #ccc; padding: 0.7em 0.4em 1em 0.4em; } 192 | .tfk_comm_date { color: #777; display: block; } 193 | #tfk_comment_form, #tfk_edit_task_form { margin-bottom: 2em; } 194 | #tfk_edit_task_form > fieldset > ol { margin: 2em 0 0 0; display: table; width: 100%; padding: 0; } 195 | #tfk_edit_task_form > fieldset > ol > li { margin: 1em 0; display: table-row; } 196 | #tfk_edit_task_form > fieldset > ol > li > label { padding: 1em 1em 0 0; margin-right: 1em; display: table-cell; } 197 | #tfk_edit_task_form > fieldset > #tfk_uploaded_files, #tfk_uploaded_files { display: block; margin: 1em 0; padding: 0; } 198 | #tfk_edit_task_form > fieldset > #tfk_uploaded_files > li, #tfk_uploaded_files > li { display: list-item; margin: 0 0 0 2em; padding: 0; } 199 | #tfk_edit_task_form #tfk_title { width: 100%; } 200 | .tfk_comm_lnk { float: right; font-size: 0.9em; } 201 | #tfk_select_prio_color { float: left; width: 1.7em; margin-right: 0.2em; } 202 | body.rtl #tfk_select_prio_color { float: right; margin-left: 0.2em; margin-right: initial; } 203 | #tfk_cal_btn { vertical-align: middle; cursor: pointer; } 204 | #tfk_back_list { float: right; margin-left: 1em; } 205 | body.rtl #tfk_back_list { float: left; margin-left: 0; margin-right: 1em; } 206 | #tfk_back_list + * { display: inline; } 207 | #tfk_task_details .tfk_sts { text-align: center; vertical-align: middle; width: 80px; height: 26px; white-space: nowrap; } 208 | #tfk_task_details .tfk_sts a { 209 | display: inline-block; 210 | margin: 0; 211 | width: 24px; height: 24px; text-indent: 110%; white-space: nowrap; overflow: hidden; 212 | border: 1px solid white; outline: 1px solid #ddd; margin-right: 1px; 213 | } 214 | #tfk_task_details .tfk_sts_lbl { display: block; font-size: 0.8em; white-space: nowrap; } 215 | #tfk_task_details .tfk_sts a + a + a { margin-right: 0; } 216 | #tfk_task_details .tfk_sts0 { background-color: white; } 217 | #tfk_task_details .tfk_sts1, #tfk_task_details .tfk_sts2, #tfk_task_details .tfk_sts3 { background-color: #bbb; } 218 | #tfk_deadline_date { width: 10em; } 219 | 220 | /* task updates */ 221 | @font-face { 222 | font-family: 'icomoon'; 223 | src:url('../fonts/icomoon.eot'); 224 | src:url('../fonts/icomoon.eot?#iefix') format('embedded-opentype'), 225 | url('../fonts/icomoon.woff') format('woff'), 226 | url('../fonts/icomoon.ttf') format('truetype'), 227 | url('../fonts/icomoon.svg#icomoon') format('svg'); 228 | font-weight: normal; 229 | font-style: normal; 230 | } 231 | #tfk_updates h4 { background-color: #eee; padding: 0.2em 0.7em; font-weight: bold; } 232 | #tfk_updates ul { margin-left: 0; padding: 0; } 233 | #tfk_updates li { list-style-type: none; list-style-position: inside; margin-left: 3em; } 234 | #tfk_updates li:before { margin-right: 1em; font-family: 'icomoon'; margin-left: -2.3em; } 235 | body.rtl #tfk_updates li:before { float: right; margin-right: 0; margin-left: .5em; } 236 | #tfk_updates li.tfk_mod:before { content: "\e000"; } 237 | #tfk_updates li.tfk_com:before { content: "\e001"; } 238 | #tfk_updates li.tfk_set:before { content: "\e002"; } 239 | body.rtl #tfk_updates li.tfk_set:before { 240 | -moz-transform: scaleX(-1); 241 | -o-transform: scaleX(-1); 242 | -webkit-transform: scaleX(-1); 243 | transform: scaleX(-1); 244 | filter: FlipH; 245 | } 246 | #tfk_updates li.tfk_cre:before { content: "\e003"; } 247 | #tfk_updates small { opacity: 0.5; } 248 | .tfk_user { font-weight: bold; } 249 | 250 | #tfk_proj_desc { background-color: #eee; color: #555; border: 1px solid #ccc; padding: 1em; margin: 1em 0; border-radius: 0.5em; } 251 | #tfk_proj_desc p { margin: 0; } 252 | 253 | @media screen and (max-width: 640px) { 254 | #tfk_subfilters { margin-left: 0; } 255 | #tfk_tasksheet > li > ul { margin-bottom: 1.5em; } 256 | #tfk_tasksheet > li > ul > li { display: block; position: relative; padding-bottom: 0; } 257 | #tfk_tasksheet .tfk_col1, #tfk_tasksheet .tfk_col2 { padding-top: 1.5em; } 258 | #tfk_tasksheet .tfk_col2, #tfk_tasksheet .tfk_col3, #tfk_tasksheet .tfk_col4 { padding-left: 0; } 259 | #tfk_tasksheet .tfk_col1, #tfk_tasksheet .tfk_col3 { float: right; padding-right: 0; } 260 | #tfk_tasksheet .tfk_prox { position: absolute; left: 0; top: 0.5em; } 261 | #tfk_tasksheet .tfk_prj { padding-top: 0.5em; padding-bottom: 0.7em; } 262 | #tfk_tasksheet .tfk_col4 { text-align: left; padding-bottom: 1em; } 263 | #tfk_new_task { float: none; display: block; margin-bottom: 1em; } 264 | .tfk_comm_user, .tfk_comm_user + div { padding: 1em 0.5em 0.5em 0; } 265 | #tfk_task_details { display: block; padding: 0.5em; } 266 | #tfk_task_details > p { display: block; margin: 0.5em; } 267 | #tfk_task_details > p > span, #tfk_task_details > p > span:first-child { display: inline; } 268 | #tfk_file_1, #tfk_file_2, #tfk_file_3, #tfk_file_4, #tfk_file_5 { margin: 0; } 269 | #tfk_task_history { margin: 0; max-width: 100%; } 270 | #tfk_edit_task_form > fieldset > ol > li > label { display: block; } 271 | #tfk_edit_task_form #tfk_project { width: 100%; } 272 | #tfk_task_count { float: none; margin-bottom: 1em; font-size: inherit; } 273 | #tfk_new_task { margin-left: 0; } 274 | #tfk_tasksheet .tfk_sts a { border: 2px solid white; outline: 2px solid #ddd; margin-right: 2px; } 275 | #tfk_task_details .tfk_sts a { border: 2px solid white; outline: 2px solid #ddd; margin-right: 2px; } 276 | #tfk_task_details .tfk_sts_lbl { display: inline-block; margin-left: 1em; font-size: 0.8em; white-space: nowrap; line-height: 28px; vertical-align: top; } 277 | #tfk_task_history_toggle { 278 | width: auto; 279 | } 280 | } 281 | 282 | @media screen and (max-width: 320px) { 283 | #tfk_breadcrumb { padding-left: 0; } 284 | #tfk_subfilters li, #tfk_breadcrumb li { display: block; } 285 | #tfk_subfilters li + li:before, #tfk_breadcrumb li + li:before { content: "↳ "; } 286 | } 287 | -------------------------------------------------------------------------------- /fonts/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/fonts/icomoon.eot -------------------------------------------------------------------------------- /fonts/icomoon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /fonts/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/fonts/icomoon.ttf -------------------------------------------------------------------------------- /fonts/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/fonts/icomoon.woff -------------------------------------------------------------------------------- /img/attachment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/img/attachment.png -------------------------------------------------------------------------------- /img/attachment.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 50 | 62 | 63 | 65 | 66 | 68 | image/svg+xml 69 | 71 | 72 | 73 | 74 | 75 | 83 | 89 | 90 | -------------------------------------------------------------------------------- /img/bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/img/bubble.png -------------------------------------------------------------------------------- /img/bubble.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 48 | 60 | 61 | 63 | 64 | 66 | image/svg+xml 67 | 69 | 70 | 71 | 72 | 73 | 79 | 80 | -------------------------------------------------------------------------------- /img/calendar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/img/calendar.png -------------------------------------------------------------------------------- /img/logo.16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/img/logo.16.png -------------------------------------------------------------------------------- /img/logo.32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/img/logo.32.png -------------------------------------------------------------------------------- /img/logo.32c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/img/logo.32c.png -------------------------------------------------------------------------------- /inc/classes/controller.php: -------------------------------------------------------------------------------- 1 | _mode = 'echo'; 16 | } 17 | 18 | protected function call($file) { 19 | include TFK_ROOT_PATH.'inc/controllers/'.$file; 20 | } 21 | 22 | protected function view_inc($file) { 23 | include TFK_ROOT_PATH.'inc/views/'.$file; 24 | } 25 | 26 | protected function view($file) { 27 | if ($this->_mode == 'return') { 28 | $this->_view = $file; 29 | } else { 30 | include TFK_ROOT_PATH.'inc/views/'.$file; 31 | } 32 | } 33 | 34 | protected function view_front() { 35 | if (isset($this->_view)) { 36 | ob_start(); 37 | include TFK_ROOT_PATH.'inc/views/'.$this->_view; 38 | return ob_get_clean(); 39 | } 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /inc/classes/tools.php: -------------------------------------------------------------------------------- 1 | sani($value, $type); 18 | } 19 | 20 | /** 21 | * get clean baselink 22 | */ 23 | public static function baselink() { 24 | return remove_query_arg(array( 25 | 'mode', 26 | 'proj', 27 | 'filter_project', 28 | 'filter_task', 29 | 'filter_recent', 30 | 'view', 31 | 'edit', 32 | 'js', 33 | 'tfknonce', 34 | 'status', 35 | 't', 36 | 'pg', 37 | 'npg', 38 | 'sort', 39 | 'ord', 40 | 'noheader' 41 | )); 42 | } 43 | 44 | 45 | /** 46 | * add / replace parameter in URL 47 | */ 48 | public static function concat_url($url, $param) { 49 | // hash 50 | $hash = ''; 51 | if ($pos = strpos($url,'#')) { 52 | $hash = substr($url,$pos); 53 | $url = substr($url,0,$pos); 54 | } 55 | if ($pos = strpos($param,'#')) { 56 | $hash = substr($param,$pos); 57 | } 58 | // params 59 | $url = str_replace('&','&',$url); 60 | if ($pos = strpos($url,'?')) { 61 | $arrParam = explode('=',$param); 62 | if (strpos($url,$arrParam[0].'=')) { 63 | // parameter already in url 64 | $strQuery = substr($url,$pos+1); 65 | $arrQuery = explode('&',$strQuery); 66 | $arrResult = array(); 67 | $found = false; 68 | foreach ($arrQuery as $value) { 69 | if (preg_match('/^'.$arrParam[0].'=/', $value)) { 70 | if ($arrParam[1]) { 71 | // add only if has a value 72 | $arrResult[] = $param; 73 | } 74 | $found = true; 75 | } else { 76 | $arrResult[] = $value; 77 | } 78 | } 79 | if ($found) { 80 | $url = substr($url,0,$pos).'?'.implode('&',$arrResult); 81 | } else { 82 | $url .= '&'.$param; 83 | } 84 | } else { 85 | $url .= '&'.$param; 86 | } 87 | } else { 88 | $url .= '?'.$param; 89 | } 90 | return str_replace('&','&',$url).$hash; 91 | } 92 | 93 | /* ==== HTML FORM HELPERS ======================================== */ 94 | 95 | /** 96 | * generates a $label) { 105 | $str .= ''.$label.''; 108 | } 109 | $str .= ''; 110 | return $str; 111 | } 112 | 113 | /** 114 | * generates links from a list 115 | */ 116 | public static function form_links($url, $name, $list, $selected) { 117 | $str = ''; 118 | foreach ($list as $p => $label) { 119 | if ("$selected" == "$p") { 120 | $str .= '
  • '.$label.'
  • '; 121 | } else { 122 | $str .= '
  • '.$label.'
  • '; 123 | } 124 | } 125 | return $str; 126 | } 127 | 128 | /* ==== STRING HELPERS =========================================== */ 129 | 130 | /** 131 | * transforms a string from CamelCasing to flat_with_underscores 132 | */ 133 | public static function camel_to_flat($str, $sep='_') { 134 | $str = preg_replace('/(?<=\\w)(?=[A-Z])/',"$sep$1", trim($str)); 135 | return strtolower($str); 136 | } 137 | 138 | /** 139 | * transforms a string from flat_with_underscores to CamelCasing 140 | */ 141 | public static function flat_to_camel($str, $firstCap=false, $sep='_') { 142 | $arr = explode($sep,trim($str)); 143 | $str = ''; 144 | foreach($arr as $sep) { 145 | if ((!$str && $firstCap) || $str) { 146 | $str .= ucfirst($sep); 147 | } else { 148 | $str .= $sep; 149 | } 150 | } 151 | return $str; 152 | } 153 | 154 | /** 155 | * gets any kind of arguments and returns an array 156 | * (an array, a string containing values separated by commas, or many arguments) 157 | */ 158 | public static function mixed_to_array() { 159 | $arg = func_get_arg(0); 160 | if (empty($arg)) { 161 | return array(); 162 | } else if (is_array($arg)) { 163 | return $arg; 164 | } else if (func_num_args() == 1) { 165 | $arr = explode(',',$arg); 166 | array_walk($arr, 'trim'); 167 | return $arr; 168 | } else { 169 | return func_get_args(); 170 | } 171 | } 172 | 173 | /** 174 | * generate random string 175 | */ 176 | public static function get_random($len = 32, $strChars = 'abcdefghijklmnopqrstuvwxyz0123456789') 177 | { 178 | $strCode = ""; 179 | $intLenChars = strlen($strChars); 180 | for ( $i = 0; $i < $len; $i++ ) { 181 | $n = mt_rand(1, $intLenChars); 182 | $strCode .= substr($strChars, ($n-1), 1); 183 | } 184 | return $strCode; 185 | } 186 | 187 | /** 188 | * convert string to XML friendly string (no space, no special caracters) 189 | */ 190 | public static function clean_strip($str) { 191 | $str = trim($str); 192 | if ($str) { 193 | /* 194 | if (constant('APP_CHARSET') == 'UTF-8') { 195 | $str = utf8_decode($str); 196 | } 197 | */ 198 | $str = utf8_decode($str); 199 | $str = preg_replace( 200 | array('/[àáäâ]/','/[èéëê]/','/[ìíïî]/','/[òóöôø]/','/[ùúüû]/','/[çć]/','/[ \'\?\/\\&"]/'), 201 | array('a','e','i','o','u','c','-'), 202 | strtolower($str)); 203 | $str = preg_replace('/[^a-z0-9\-]/','_',$str); 204 | $str = str_replace('---','-',$str); 205 | $str = trim($str,'_'); 206 | $str = trim($str,'-'); 207 | } 208 | return $str; 209 | } 210 | 211 | /* ==== DATE HELPERS ============================================ */ 212 | 213 | /** 214 | * get current user's timezone offset in seconds 215 | * note: currently uses WP timezone's setting 216 | * @todo support user's timezone 217 | */ 218 | public static function get_user_timezone_offset() { 219 | return get_option('gmt_offset')*3600; 220 | } 221 | 222 | /** 223 | * tries to convert string to unix time using strtotime 224 | */ 225 | public static function str_to_unix($val) { 226 | if (!$val || preg_match('/^(0000|9999)/',$val)) { 227 | return 0; 228 | } 229 | $t = strtotime($val); 230 | if ($t === false) { 231 | return false; 232 | } 233 | return $t; 234 | } 235 | 236 | /** 237 | * parse date from human readable to SQL format 238 | */ 239 | public static function parse_date($val) { 240 | if (empty($val)) { 241 | return '0000-00-00'; 242 | } 243 | if (preg_match('/^[0|2|9][0|1|9][0-9]{2}\-[0-1][0-9]\-[0-3][0-9]$/', $val)) { 244 | // SQL format, return as it is (no timezone evaluation) 245 | return $val; 246 | } 247 | if (!get_option('datetime_us_format')) { 248 | // try to parse non US format (dd/mm/yy) 249 | if (preg_match('/^([0-3]?[0-9])\/([0-1][0-9])(\/(20)?[0-9]{2})?$/',$val, $arr)) { 250 | $year = date('Y'); 251 | if (empty($arr[3])) { 252 | $arr[3] = $year; 253 | } else if (strlen($arr[3]) == 2) { 254 | $arr[3] = substr($year,0,2).substr($arr[3],1); 255 | } else { 256 | $arr[3] = substr($arr[3],1); 257 | } 258 | return $arr[3].'-'.$arr[2].'-'.$arr[1]; 259 | } 260 | } 261 | // try human readable formats (english only) 262 | if ($t = self::str_to_unix($val)) { 263 | return strftime('%Y-%m-%d', $t); 264 | } 265 | $info['error'] = 'invalid_date'; 266 | return false; 267 | } 268 | 269 | /** 270 | * format date from SQL format to human readable 271 | */ 272 | public static function format_date($val, $format='') { 273 | if (empty($format)) { 274 | $options = get_option('tfk_options'); 275 | $format = $options['format_date']; 276 | } 277 | $t = self::str_to_unix($val); 278 | if ($t === false) { 279 | return '/!\\'; 280 | } 281 | if ($t) { 282 | return date($format, $t); 283 | } else { 284 | return ''; 285 | } 286 | } 287 | 288 | /** 289 | * parse duration from human readable to SQL format (seconds) 290 | */ 291 | public static function parse_duration($val) { 292 | if (empty($val)) { 293 | return 0; 294 | } 295 | if (preg_match('/^[0-9]+$/', $val)) { 296 | // just a number of seconds 297 | return intval($val); 298 | } else if (preg_match('/^([0-2]?[0-9])\:([0-5][0-9])(\:([0-5][0-9]))?$/', $val, $arr)) { 299 | $t = 0; 300 | switch(count($arr)) { 301 | case 5: 302 | // seconds 303 | $t += intval($arr[4]); 304 | case 3: 305 | // minutes 306 | $t += intval($arr[2])*60; 307 | $t += intval($arr[1])*3600; 308 | } 309 | return $t; 310 | } else { 311 | return false; 312 | } 313 | } 314 | 315 | /** 316 | * format duration from SQL format to human readable 317 | * eg. 12:00 (12 hours) 318 | */ 319 | public static function format_duration($val, $seconds=false) { 320 | $h = floor($val / 3600); 321 | $m = floor($val / 60) - ($h*60); 322 | $s = $val - ($h*3600 + $m*60); 323 | 324 | $str = str_pad($h, 2, '0',STR_PAD_LEFT) 325 | .':'.str_pad($m, 2, '0',STR_PAD_LEFT); 326 | if ($seconds) { 327 | $str .= ':'.str_pad($s, 2, '0',STR_PAD_LEFT); 328 | } 329 | return $str; 330 | } 331 | 332 | /** 333 | * parse time from human readable to SQL format (seconds) 334 | * also converts to GMT date to store in database 335 | */ 336 | public static function parse_time($val) { 337 | if ($val = self::parse_duration($val)) { 338 | $val -= self::get_user_timezone_offset(); 339 | } 340 | return $val; 341 | } 342 | 343 | /** 344 | * format duration from SQL format to human readable 345 | * also converts to user's timezone 346 | * eg. 12:00 (12 hours) 347 | */ 348 | public static function format_time($val) { 349 | if ($val = self::format_duration($val)) { 350 | $val += self::get_user_timezone_offset(); 351 | } 352 | return $val; 353 | } 354 | 355 | /** 356 | * format date from SQL format to human readable 357 | * also converts to user's timezone 358 | */ 359 | public static function parse_datetime($val) { 360 | if (empty($val)) { 361 | return '0000-00-00 00:00:00'; 362 | } 363 | if ($val == 'NOW') { 364 | return current_time('mysql', 1); 365 | } 366 | if (preg_match('/^[0|2|9][0|1|9][0-9]{2}\-[0-1][0-9]\-[0-3][0-9]( ([0-2][0-9]\:[0-5][0-9])(\:[0-5][0-9])?)?$/', $val)) { 367 | // SQL format, return as it is (no timezone evaluation) 368 | return $val; 369 | } 370 | $d = ''; 371 | $t = 0; 372 | $arr = explode(' ',$val); 373 | foreach($arr as $v) { 374 | if ($tmp = self::parse_time($v)) { 375 | $t = $tmp; 376 | } else if ($tmp = self::parse_date($v)) { 377 | $d = $tmp; 378 | } 379 | } 380 | if ($d && is_integer($t)) { 381 | $dh = 3600*24; 382 | if ($t < 0) { 383 | // got to go one day back 384 | $d = strftime('%Y-%m-%d',strtotime($d)-$dh); 385 | $t = $dh+$t; // + means - as $t is < 0 386 | } else if ($t > $dh) { 387 | // got to move on one day 388 | $d = strftime('%Y-%m-%d',strtotime($d)+$dh); 389 | $t = $t-$dh; 390 | } 391 | // return datetime in SQL format 392 | return $d.' '.self::format_duration($t, false); // timezone already processed 393 | } 394 | return false; 395 | } 396 | 397 | /** 398 | * format date from SQL format to human readable 399 | * also converts to user's timezone 400 | */ 401 | public static function format_datetime($val, $format='', $default=false) { 402 | if (empty($format)) { 403 | $options = get_option('tfk_options'); 404 | $format = $options['format_date'].' '.$options['format_time']; 405 | } 406 | $t = self::str_to_unix($val); 407 | if ($t === false) { 408 | return '/!\\'; 409 | } 410 | if ($t) { 411 | $t += self::get_user_timezone_offset(); 412 | return date($format, $t); 413 | } else { 414 | return $default; 415 | } 416 | } 417 | 418 | /** 419 | * convert PHP date formats to jQuery datepicker formats 420 | */ 421 | public static function date_format_convert_php_jquery($val) { 422 | return preg_replace(array( 423 | '/j/', 424 | '/d/', 425 | '/z/', 426 | '/D/', 427 | '/l/', 428 | '/n/', 429 | '/m/', 430 | '/M/', 431 | '/F/', 432 | '/y/', 433 | '/Y/', 434 | 435 | ), array( 436 | 'd', 437 | 'dd', 438 | 'o', 439 | 'D', 440 | 'DD', 441 | 'm', 442 | 'mm', 443 | 'M', 444 | 'MM', 445 | 'y', 446 | 'yy', 447 | ), $val); 448 | } 449 | } -------------------------------------------------------------------------------- /inc/controllers/admin.php: -------------------------------------------------------------------------------- 1 | call('admin/dashboard.php'); 40 | } 41 | 42 | /** 43 | * Projects page 44 | * list, create, edit or delete project 45 | */ 46 | public function page_projects() { 47 | $this->options = get_option('tfk_options'); 48 | $this->linkadmin = '?page=taskfreak_projects'; 49 | $this->linkfront = add_query_arg('mode', 'projects', $this->options['page_url']); 50 | $this->is_manager = tfk_user::check_role('editor'); 51 | if (empty($_REQUEST['id'])) { 52 | // list projects by default 53 | // author, editor and admin 54 | $this->call('admin/project_list.php'); 55 | } else if ($this->is_manager) { 56 | // only editor and admin can add/edit projects (any projects) 57 | $this->call('admin/project_edit.php'); 58 | } 59 | } 60 | 61 | /** 62 | * Global settings page 63 | */ 64 | public function page_settings() { 65 | $this->baselink = '?page=taskfreak_settings'; 66 | $this->call('admin/settings.php'); 67 | } 68 | 69 | /** 70 | * Add "settings" link to plugin 71 | */ 72 | public function init_plugin_links($links, $file) { 73 | static $this_plugin; 74 | if (!$this_plugin) { 75 | $this_plugin = plugin_basename(TFK_ROOT_PATH); 76 | } 77 | if (preg_match('/^'.$this_plugin.'/', $file)) { 78 | $settings_link = 'Settings'; 79 | array_unshift($links, $settings_link); 80 | } 81 | return $links; 82 | } 83 | 84 | } 85 | 86 | tfk_admin::init(); -------------------------------------------------------------------------------- /inc/controllers/admin/dashboard.php: -------------------------------------------------------------------------------- 1 | options = get_option('tfk_options'); 13 | $this->linktsk = esc_url(add_query_arg('mode', 'tasks', $this->options['page_url'])); 14 | $this->linkprj = '?page=taskfreak_projects'; 15 | $this->linkupd = esc_url(add_query_arg('mode', 'recent', $this->options['page_url'])); 16 | 17 | // User tasks 18 | 19 | $this->tskusr = array( 20 | 20 => 0, 21 | 30 => 0, 22 | 60 => 0 23 | ); 24 | 25 | foreach ($this->tskusr as $k => $v) { 26 | $lst = new tfk_item_info(); 27 | if ($c = $lst->load_list( 28 | array( 29 | 'where' => 'item.user_id = '.get_current_user_id().' AND trashed=0', 30 | 'having' => 'item_status_action_code = '.$k 31 | ) 32 | )) { 33 | $this->tskusr[$k] = $c; 34 | } 35 | } 36 | 37 | // All tasks 38 | 39 | $this->tskall = array( 40 | 20 => 0, 41 | 30 => 0, 42 | 60 => 0 43 | ); 44 | 45 | foreach ($this->tskall as $k => $v) { 46 | $lst = new tfk_item_info(); 47 | if ($c = $lst->load_list( 48 | array( 49 | 'where' => 'trashed=0', 50 | 'having' => 'item_status_action_code = '.$k 51 | ) 52 | )) { 53 | $this->tskall[$k] = $c; 54 | } 55 | } 56 | 57 | // User projects 58 | 59 | $this->prjusr = array( 60 | 20 => 0, 61 | 30 => 0, 62 | 60 => 0 63 | ); 64 | 65 | foreach ($this->prjusr as $k => $v) { 66 | $lst = new tfk_project_info(); 67 | if ($c = $lst->load_list( 68 | array( 69 | 'where' => tfk_user::get_roles_sql('who_read').' AND trashed=0', 70 | 'having' => 'project_status_action_code='.$k 71 | ) 72 | )) { 73 | $this->prjusr[$k] = $c; 74 | } 75 | } 76 | 77 | // All projects 78 | 79 | $this->prjall = array( 80 | 20 => 0, 81 | 30 => 0, 82 | 60 => 0 83 | ); 84 | 85 | foreach ($this->prjall as $k => $v) { 86 | $lst = new tfk_project_info(); 87 | if ($c = $lst->load_list( 88 | array( 89 | 'where' => 'trashed=0', 90 | 'having' => 'project_status_action_code='.$k 91 | ) 92 | )) { 93 | $this->prjall[$k] = $c; 94 | } 95 | } 96 | 97 | // View 98 | 99 | wp_register_style( 'tznadmincss', plugins_url('css/admin.css', TFK_ROOT_FILE)); 100 | wp_enqueue_style( 'tznadmincss' ); 101 | $this->view('admin/dashboard.php'); -------------------------------------------------------------------------------- /inc/controllers/admin/project_edit.php: -------------------------------------------------------------------------------- 1 | pid = intval($_REQUEST['id']); 13 | 14 | $this->saveok = false; 15 | $this->saverror = false; 16 | 17 | // load project 18 | $this->data = new tfk_project(); 19 | if ($this->pid) { 20 | $this->data->set_uid($this->pid); 21 | if (!$this->data->load()) { 22 | $this->pid = 0; 23 | } 24 | } 25 | 26 | if (!$this->pid) { 27 | // create new project, set defaults 28 | $this->data->init_rights($this->options); 29 | 30 | } 31 | 32 | // modify project (on submit) 33 | if (!empty($_REQUEST['action'])) { 34 | 35 | // check user right (admin and editors only) 36 | if (!$this->is_manager) { 37 | die('Security: only Administrators and Editors can do that'); 38 | } 39 | 40 | // check nonce 41 | check_admin_referer('tfk_project_'.$_REQUEST['action']); 42 | 43 | if ($_REQUEST['action'] == 'trash') { 44 | // move to trash 45 | $this->data->set('trashed',1); 46 | $this->data->update(); 47 | wp_redirect(admin_url().'admin.php?page=taskfreak_projects'); 48 | exit; 49 | } 50 | if ($_REQUEST['action'] == 'restore') { 51 | // move to trash 52 | $this->data->set('trashed',0); 53 | $this->data->update(); 54 | wp_redirect(admin_url().'admin.php?page=taskfreak_projects&filter=trash'); 55 | exit; 56 | } 57 | if ($_REQUEST['action'] == 'delete') { 58 | // delete permanently 59 | $this->data->delete(); 60 | wp_redirect(admin_url().'admin.php?page=taskfreak_projects&filter=trash'); 61 | exit; 62 | } 63 | 64 | if ($_POST['action'] == 'save') { 65 | // clean POST values 66 | $_POST = stripslashes_deep($_POST); 67 | // set object properties 68 | $this->data->set_auto($_POST); 69 | // check 'n save 70 | if ($this->data->check()) { 71 | if ($this->pid) { 72 | $this->data->update(); 73 | if ($_POST['project_status_new'] != $_POST['project_status_old']) { 74 | $this->data->set_status($_POST['project_status_new']); 75 | } 76 | } else { 77 | $this->data->set('creation_date', 'NOW'); 78 | $this->pid = $this->data->insert(); 79 | $this->data->set_status($_POST['project_status_new'], null, 'creation'); 80 | } 81 | 82 | // notify user : project saved 83 | $this->saveok = true; 84 | } else { 85 | // notify user : error on form 86 | $this->saverror = true; 87 | } 88 | } 89 | } 90 | 91 | // load status history 92 | $this->status = new tfk_project_status(); 93 | if ($this->pid) { 94 | $this->status->load_list(array( 95 | 'where' => 'project_id='.$this->pid, 96 | 'order' => 'log_date ASC' 97 | )); 98 | } 99 | 100 | // No need to redirect, show headers now 101 | if (isset($_REQUEST['noheader'])) { 102 | require_once(ABSPATH . 'wp-admin/admin-header.php'); 103 | } 104 | 105 | $this->baselink = add_query_arg('id',($this->pid?$this->pid:'new'), $this->linkadmin); 106 | 107 | // prepare view 108 | add_thickbox(); 109 | 110 | wp_enqueue_script('post'); 111 | 112 | wp_register_script('tznadminjs', plugins_url( '/js/admin.js' , TFK_ROOT_FILE), array('jquery') ); 113 | wp_enqueue_script('tznadminjs'); 114 | 115 | wp_register_style( 'tznadmincss', plugins_url('css/admin.css', TFK_ROOT_FILE)); 116 | wp_enqueue_style( 'tznadmincss' ); 117 | 118 | if ( function_exists('wp_is_mobile') && wp_is_mobile() ) { 119 | wp_enqueue_script( 'jquery-touch-punch' ); 120 | } 121 | $this->view('admin/project_edit.php'); -------------------------------------------------------------------------------- /inc/controllers/admin/project_list.php: -------------------------------------------------------------------------------- 1 | filter = 'all'; 14 | $where = 'trashed=0'; 15 | 16 | if (!$this->is_manager) { 17 | // non admin can not list drafts 18 | $where .= " AND project_status.action_code<>'0'"; 19 | } 20 | 21 | if (isset($_REQUEST['filter'])) { 22 | 23 | $this->filter = $_REQUEST['filter']; 24 | 25 | // check filter 26 | switch ($_REQUEST['filter']) { 27 | case 'trash': 28 | if ($this->is_manager) { 29 | $where = 'trashed=1'; 30 | break; 31 | } 32 | case 'draft': 33 | if ($this->is_manager) { 34 | $where .= " AND project_status.action_code='0'"; 35 | break; 36 | } 37 | default: 38 | if ($f = intval($_REQUEST['filter'])) { 39 | $where .= " AND project_status.action_code='$f'"; 40 | } else { 41 | $this->filter = 'all'; // all or invalid 42 | } 43 | break; 44 | } 45 | } 46 | 47 | $where .= ' AND '.tfk_user::get_roles_sql('who_read'); 48 | 49 | $this->data = new tfk_project_info(); 50 | $this->data->load_list( 51 | array( 52 | 'where' => $where, 53 | 'order' => 'project_status_action_code ASC, project_id DESC', 54 | 'page_size' => 10, 55 | 'page' => empty($_REQUEST['pg'])?1:$_REQUEST['pg'] 56 | ) 57 | ); 58 | 59 | wp_register_style( 'tznadmincss', plugins_url('css/admin.css', TFK_ROOT_FILE)); 60 | wp_enqueue_style( 'tznadmincss' ); 61 | 62 | $this->view('admin/project_list.php'); -------------------------------------------------------------------------------- /inc/controllers/admin/project_view.php: -------------------------------------------------------------------------------- 1 | pid = intval($_REQUEST['id']); 13 | 14 | // load project 15 | $this->data = new tfk_project(); 16 | if ($this->pid) { 17 | $this->data->set_uid($this->pid); 18 | if (!$this->data->load()) { 19 | $this->pid = 0; 20 | } 21 | } 22 | 23 | // TODO, view project details and list users 24 | 25 | // TEMPORARY : redirect to page in front office, or list if not possible 26 | 27 | if (headers_sent()) { 28 | $this->call('admin/project_list.php'); 29 | } else { 30 | wp_redirect(add_query_arg(array( 31 | 'mode' => 'projects', 32 | 'proj' => $this->pid 33 | ), $this->linkfront)); 34 | exit(); 35 | } -------------------------------------------------------------------------------- /inc/controllers/admin/settings.php: -------------------------------------------------------------------------------- 1 | options = get_option('tfk_options'); 13 | 14 | // for compat with versions prior to 1.1.0 15 | if (!isset($this->options['prio_size'])) 16 | $this->options['prio_size'] = 0; 17 | 18 | $this->message = ''; 19 | 20 | if (isset($_POST['opt_save']) && check_admin_referer('tfk_settings')) { 21 | // save options 22 | $this->options['tfk_all'] = $_POST['tfk_all']; 23 | $this->options['tasks_per_page'] = $_POST['tasks_per_page']; 24 | $this->options['number_updates'] = $_POST['number_updates']; 25 | $this->options['avatar_size'] = $_POST['avatar_size']; 26 | $this->options['format_date'] = $_POST['format_date']; 27 | $this->options['format_time'] = $_POST['format_time']; 28 | $this->options['proximity'] = intval($_POST['proximity']); 29 | $this->options['prio_size'] = intval($_POST['prio_size']); 30 | $this->options['access_read'] = $_POST['access_read']; 31 | $this->options['access_comment'] = $_POST['access_comment']; 32 | $this->options['access_post'] = $_POST['access_post']; 33 | $this->options['access_manage'] = $_POST['access_manage']; 34 | $this->options['comment_upload'] = $_POST['comment_upload']; 35 | 36 | if (update_option('tfk_options', $this->options)) { 37 | $this->message = __('Changes saved', 'taskfreak'); 38 | } else { 39 | $this->message = __('No changes saved', 'taskfreak'); 40 | } 41 | 42 | } 43 | 44 | $this->view('admin/settings.php'); 45 | -------------------------------------------------------------------------------- /inc/controllers/front.php: -------------------------------------------------------------------------------- 1 | plugins_url(), 31 | 'error_message' => __('An error has occured.', 'taskfreak'), 32 | 'datepicker_format' => tzn_tools::date_format_convert_php_jquery($options['format_date']), 33 | 'task_hist_show' => __('Show History', 'taskfreak'), 34 | 'task_hist_hide' => __('Hide History', 'taskfreak'), 35 | ) 36 | ); 37 | wp_enqueue_script('jquery-ui-datepicker'); 38 | 39 | $wp_lang = get_locale(); 40 | $wp_lang = preg_replace('/_..$/', '', $wp_lang); 41 | if ($wp_lang != 'en') { 42 | wp_register_script('jquery-ui-datepicker-l10n', 43 | '//raw.githubusercontent.com/jquery/jquery-ui/master/ui/i18n/datepicker-'.$wp_lang.'.js', 44 | array('jquery-ui-datepicker')); 45 | wp_enqueue_script('jquery-ui-datepicker-l10n'); 46 | } 47 | wp_enqueue_style('jquery-style', '//ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/themes/smoothness/jquery-ui.css'); 48 | } 49 | 50 | public function tfk_all() { 51 | 52 | $this->_mode = 'return'; 53 | 54 | // Load options 55 | $this->options = get_option('tfk_options'); 56 | 57 | // update page url in options for widgets 58 | $baselink = tzn_tools::baselink(); 59 | if ($this->options['page_url'] != $baselink || $this->options['page_id'] != get_the_ID()) { 60 | $this->options['page_url'] = $baselink; 61 | $this->options['page_id'] = get_the_ID(); 62 | update_option('tfk_options', $this->options); 63 | } 64 | 65 | $this->mode = ''; 66 | 67 | // List filters 68 | // TODO keep in session 69 | $this->filters = array( 70 | 'filter_project' => '20', 71 | 'filter_task' => '20', 72 | 'filter_recent' => 'all' 73 | ); 74 | foreach (array('filter_project', 'filter_task') as $key) { 75 | if (isset($_GET[$key]) && ($_GET[$key] == 'all' || preg_match('/^\d+$/', $_GET[$key]))) { 76 | $this->filters[$key] = $_GET[$key]; 77 | } 78 | } 79 | if (isset($_GET['filter_recent']) && in_array($_GET['filter_recent'], array('all', 'tasks', 'projects', 'comments'))) { 80 | $this->filters['filter_recent'] = $_GET['filter_recent']; 81 | } 82 | 83 | // ACTION 84 | 85 | if (isset($_GET['hist'])) { 86 | 87 | // task history (called by Ajax if JS is enabled) 88 | $this->call('front/task_history.php'); 89 | 90 | } else if (isset($_GET['view'])) { 91 | 92 | // view task 93 | $this->call('front/task_view.php'); 94 | 95 | } else if (isset($_GET['edit']) && isset($_GET['js'])) { 96 | 97 | // update task status 98 | $this->call('front/task_status.php'); 99 | 100 | } else if (isset($_REQUEST['edit'])) { 101 | 102 | // edit or create task 103 | $this->call('front/task_edit.php'); 104 | 105 | } else if (isset($_GET['proj']) && isset($_GET['js'])) { 106 | 107 | // get users list for the project (ajax call) 108 | $this->call('front/project_users.php'); 109 | 110 | } else if (isset($_REQUEST['proj'])) { 111 | 112 | $this->mode = 'projects'; // tell nav view it's a project so we can add tasks filter 113 | 114 | // view project 115 | $this->call('front/project_view.php'); 116 | 117 | } else { 118 | 119 | $this->mode = $this->options['tfk_all']; 120 | if (isset($_REQUEST['mode'])) { 121 | $this->mode = $_REQUEST['mode']; 122 | } 123 | 124 | switch ($this->mode) { 125 | case 'recent': 126 | // list recent updates 127 | $this->call('front/task_recent.php'); 128 | break; 129 | case 'tasks': 130 | // list tasks 131 | $this->call('front/task_list.php'); 132 | break; 133 | case 'projects': 134 | default: 135 | // list projects 136 | $this->mode = 'projects'; 137 | $this->call('front/project_list.php'); 138 | break; 139 | } 140 | } 141 | 142 | // Return view 143 | return $this->view_front(); 144 | } 145 | } 146 | 147 | tfk_front::init(); 148 | -------------------------------------------------------------------------------- /inc/controllers/front/project_list.php: -------------------------------------------------------------------------------- 1 | baselink = add_query_arg('mode', 'projects', tzn_tools::baselink()); 13 | 14 | $this->data = new tfk_project_info(); 15 | 16 | if ($this->filters['filter_project'] == 'all') { 17 | $sql = array('where' => tfk_user::get_roles_sql('who_read').' AND trashed=0'); 18 | if (!current_user_can('manage_options')) { 19 | $sql['having'] = 'project_status_action_code <> 0'; 20 | } 21 | $this->data->load_list($sql); 22 | } else { 23 | $this->data->load_list(array( 24 | 'where' => tfk_user::get_roles_sql('who_read').' AND trashed=0', 25 | 'having' => 'project_status_action_code='.intval($this->filters['filter_project']) 26 | )); 27 | } 28 | 29 | $this->view('front/project_list.php'); -------------------------------------------------------------------------------- /inc/controllers/front/project_users.php: -------------------------------------------------------------------------------- 1 | set_uid($pid); 19 | if ($pid && $project->load()) { 20 | if (!$project->check_access('post')) { 21 | echo '

    '.__("Sorry, you can't post in this project. Please contact an admin.", 'taskfreak').'

    '; 22 | } else { 23 | echo '
    '; 24 | echo ''; 25 | foreach ($users as $user) { 26 | if ($project->check_access('read', $user->ID)) { 27 | echo ''; 28 | } 29 | } 30 | echo '
    '; 31 | } 32 | } -------------------------------------------------------------------------------- /inc/controllers/front/project_view.php: -------------------------------------------------------------------------------- 1 | pid = intval($_REQUEST['proj']); 13 | 14 | $this->baselink = add_query_arg(array('mode' => 'projects', 'proj' => $this->pid), tzn_tools::baselink()); 15 | 16 | $sort_params = array('priority', 'deadline_date', 'title', 'name', 'display_name', 'log_date', 'item_status_action_code', 'comment_count', 'file_count'); 17 | $sort = (isset($_REQUEST['sort']) && in_array($_REQUEST['sort'], $sort_params) ? $_REQUEST['sort'] : 'proximity'); 18 | $order = (isset($_REQUEST['ord']) && in_array($_REQUEST['ord'], array('asc', 'desc')) ? $_REQUEST['ord'] : 'ASC'); 19 | 20 | $this->page = isset($_REQUEST['pg']) && preg_match('/^\d+$/', $_REQUEST['pg']) ? $_REQUEST['pg'] : 1; 21 | 22 | if (isset($_REQUEST['npg']) && preg_match('/^\d+$/', $_REQUEST['npg'])) { 23 | if (!headers_sent()) { 24 | setcookie('tfk_page_size', $_REQUEST['npg']); 25 | } 26 | $this->page_size = $_REQUEST['npg']; 27 | } elseif (isset($_COOKIE['tfk_page_size']) && preg_match('/^\d+$/', $_COOKIE['tfk_page_size'])) { 28 | $this->page_size = $_COOKIE['tfk_page_size']; 29 | } elseif ($this->options['tasks_per_page']) { 30 | $this->page_size = $this->options['tasks_per_page']; 31 | } else { 32 | $this->page_size = 5; 33 | } 34 | 35 | // load project 36 | $this->project = new tfk_project(); 37 | $this->project->set_uid($this->pid); 38 | if (!$this->pid || !$this->project->load()) { 39 | echo '

    '.__("No such project.", 'taskfreak').'

    '; 40 | return; 41 | } else { 42 | // check if user has access 43 | if (!$this->project->check_access('read')) { 44 | echo '

    '.__("Sorry, you can't access this project. Please contact an admin.", 'taskfreak').'

    '; 45 | return; 46 | } 47 | } 48 | 49 | $this->user_can_post = $this->project->check_access('post'); 50 | 51 | // show only drafts to their author and to users who have the right to manage the project 52 | if (is_user_logged_in()) { 53 | $current_user = wp_get_current_user(); 54 | $can_see_drafts = '( item_status_action_code <> 0 OR item.author_id = '.$current_user->ID.' )'; 55 | } else { 56 | $can_see_drafts = 'item_status_action_code <> 0'; 57 | } 58 | 59 | // load tasks 60 | $this->data = new tfk_item_info(); 61 | $this->data->load_list( 62 | array( 63 | 'where' => 'item.project_id = '.$this->pid.' AND trashed = 0', 64 | 'having' => $can_see_drafts 65 | .($this->filters['filter_task'] == 'all' ? '' : ' AND item_status_action_code = '.$this->filters['filter_task']), 66 | 'order' => $sort.' '.$order, 67 | 'page_size' => $this->page_size, 68 | 'page' => $this->page 69 | ) 70 | ); 71 | 72 | $this->npages = ceil($this->data->total() / $this->page_size); 73 | 74 | $this->view('front/project_view.php'); -------------------------------------------------------------------------------- /inc/controllers/front/task_edit.php: -------------------------------------------------------------------------------- 1 | '.__("Sorry, you can't edit tasks when logged out.", 'taskfreak').'

    '; 14 | return; 15 | } 16 | 17 | $this->pid = intval($_REQUEST['edit']); 18 | 19 | $this->data = new tfk_item(); 20 | if ($this->pid) { 21 | $this->data->set_uid($this->pid); 22 | if (!$this->data->load()) { 23 | $this->pid = 0; 24 | $this->data->set_uid(0); // prevent user from setting id 25 | } 26 | } 27 | 28 | // if we hit "new task" in a project's task list, then... 29 | if (!$this->pid && isset($_REQUEST['proj']) && preg_match('/^\d+$/', $_REQUEST['proj'])) { 30 | $this->current_project = $_REQUEST['proj']; 31 | } else { 32 | $this->current_project = ''; 33 | } 34 | 35 | if ($this->pid != 0 36 | && get_current_user_id() != $this->data->get('author_id') // current user must be the author of the task 37 | && !$this->data->get('project')->check_access('manage')) { // or must be a manager of the project 38 | echo '

    '.__("Sorry, you can't edit this task. Please contact an admin.", 'taskfreak').'

    '; 39 | return; 40 | } 41 | 42 | // --- SUBMIT --------------------------- 43 | 44 | if (isset($_POST['edit'])) { 45 | 46 | // Submit data, check and save 47 | 48 | // we have to do this because of http://codex.wordpress.org/Function_Reference/stripslashes_deep#Notes 49 | $_POST = stripslashes_deep($_POST); 50 | 51 | if ($this->pid) { // prepare log info (if data check fails, it won't be saved) 52 | $update_log = new tfk_item_status(); 53 | $update_log->set('log_date', 'NOW'); 54 | $update_log->set('item_id', $this->pid); 55 | $update_log->set_object('user', get_current_user_id()); 56 | $update_log_info = array(); 57 | foreach (array('title', 'priority', 'user_id', 'description') as $prop) { 58 | if (!empty($_POST[$prop]) && $_POST[$prop] != $this->data->get($prop)) 59 | $update_log_info[] = $prop; 60 | } 61 | if (!empty($_POST['project_id']) 62 | && $_POST['project_id'] != $this->data->get('project')->get_uid()) 63 | $update_log_info[] = 'project'; 64 | if (!empty($_POST['deadline_date']) 65 | && $_POST['deadline_date'] != $this->data->html('deadline_date')) 66 | $update_log_info[] = 'deadline'; 67 | } 68 | 69 | $this->data->set_auto($_POST); 70 | 71 | if (!$this->data->get('project')->get_uid() || !$this->data->get('project')->load()) { 72 | $this->data->errors['project'] = '

    '.__("Unavailable project", 'taskfreak').'

    '; 73 | } elseif (!$this->data->get('project')->check_access('post')) { 74 | $this->data->errors['project'] = '

    '.__("Sorry, you can't post in this project. Please contact an admin.", 'taskfreak').'

    '; 75 | } elseif ($this->data->get('user_id') 76 | && ! $this->data->get('project')->check_access('read', $this->data->get('user_id'))) { 77 | $this->data->errors['user_id'] = '

    '.__("Please select a user who has access to this project", 'taskfreak').'

    '; 78 | } elseif ($this->data->check()) { 79 | if ($this->pid) { 80 | $this->data->update(); 81 | if ($_POST['status'] != $this->data->get_status()->get('action_code')) { 82 | $this->data->set_status($_POST['status']); 83 | } 84 | } else { 85 | $this->data->set('author_id', get_current_user_id()); 86 | $this->data->set('creation_date', 'NOW'); 87 | $this->pid = $this->data->insert(); 88 | $this->data->set_status($_POST['status'], null, 'creation'); 89 | } 90 | 91 | $this->data->errors['file'] = ''; 92 | $wp_upload_dir = wp_upload_dir(); 93 | $saved_files = 0; 94 | for ($i = 1; $i <= 5 ; $i++) { // up to 5 uploads per edit 95 | if (!empty($_FILES["tfk_file_$i"]['name'])) { 96 | $file = new tfk_item_file(); 97 | if ($file->upload("tfk_file_$i")) { 98 | $file->set('item_id', $this->pid); 99 | $file->set('user_id', get_current_user_id()); 100 | $file->set('file_tags', 'task'); 101 | $file->save(); 102 | $saved_files++; 103 | } else { 104 | $this->data->errors['file'] .= '

    '.$file->error.'

    '; 105 | } 106 | } // else no file, do nothing 107 | } 108 | 109 | if (isset($update_log)) { 110 | if ($saved_files) { 111 | $update_log_info[] = $saved_files.' file(s)'; 112 | } 113 | if (!empty($update_log_info)) { 114 | $update_log->set('info', implode(',', $update_log_info)); 115 | $update_log->save(); 116 | } 117 | } 118 | 119 | if (!$this->data->errors['file']) { 120 | if (headers_sent()) { 121 | $this->data->load(); 122 | $this->data->errors['global_errors'] = 123 | '

    '.__('Changes saved.', 'taskfreak').'

    '; 124 | } else { 125 | wp_redirect(remove_query_arg(array('edit', 'noheader'), add_query_arg('view', $this->pid)."#tfk_task_title")); 126 | exit(); 127 | } 128 | } 129 | } 130 | } elseif (preg_match('/noheader=1/', $_SERVER['REQUEST_URI'])) { 131 | echo '

    '.__('Upload failed. Check file size.', 'taskfreak').'

    '; 132 | return; 133 | } 134 | 135 | // --- DISPLAY FORM (prepare data) ----- 136 | 137 | // load attachments 138 | $this->file = new tfk_item_file(); 139 | $this->file->load_list(array('where' => 'item_id = '.$this->pid.' AND file_tags = "task"')); 140 | 141 | // load projects 142 | $this->projects = new tfk_project_info(); 143 | $this->projects->load_list(array( 144 | 'where' => tfk_user::get_roles_sql('who_post').' AND trashed = 0 ', 145 | 'having' => 'project_status_action_code IN (20, 30)', 146 | 'order' => 'name ASC', 147 | )); 148 | 149 | // load users 150 | $this->users = get_users(); 151 | 152 | // for status list 153 | $this->status = new tfk_item_status(); 154 | 155 | $this->view('front/edit.php'); 156 | -------------------------------------------------------------------------------- /inc/controllers/front/task_history.php: -------------------------------------------------------------------------------- 1 | pid = intval($_REQUEST['hist']); 13 | 14 | $this->data = new tfk_item(); 15 | 16 | if ($this->pid) { 17 | $this->data->set_uid($this->pid); 18 | if (!$this->data->load()) { 19 | echo '

    '.__('Sorry, item not found', 'taskfreak').'

    '; 20 | return; 21 | } 22 | } else { 23 | echo '

    '.__('Missing "view" parameter', 'taskfreak').'

    '; 24 | return; 25 | } 26 | 27 | $this->log = new tfk_item_status(); 28 | $this->log->load_list(array('where' => 'item_id = '.$this->pid.' AND comment_id = 0')); 29 | 30 | $this->view('front/task_history.php'); -------------------------------------------------------------------------------- /inc/controllers/front/task_list.php: -------------------------------------------------------------------------------- 1 | baselink = add_query_arg('mode', 'tasks', tzn_tools::baselink()); 13 | 14 | $this->data = new tfk_item_info(); 15 | 16 | $sort_params = array('priority', 'deadline_date', 'proximity', 'title', 'name', 'display_name', 'log_date', 'item_status_action_code', 'comment_count', 'file_count'); 17 | $sort = (isset($_REQUEST['sort']) && in_array($_REQUEST['sort'], $sort_params) ? $_REQUEST['sort'] : 'proximity'); 18 | $order = (isset($_REQUEST['ord']) && in_array($_REQUEST['ord'], array('asc', 'desc')) ? $_REQUEST['ord'] : 'ASC'); 19 | 20 | $this->page = isset($_REQUEST['pg']) && preg_match('/^\d+$/', $_REQUEST['pg']) ? $_REQUEST['pg'] : 1; 21 | 22 | if (isset($_REQUEST['npg']) && preg_match('/^\d+$/', $_REQUEST['npg'])) { // useful if JS is disabled 23 | if (!headers_sent()) { 24 | setcookie('tfk_page_size', $_REQUEST['npg']); 25 | } 26 | $this->page_size = $_REQUEST['npg']; 27 | } elseif (isset($_COOKIE['tfk_page_size']) && preg_match('/^\d+$/', $_COOKIE['tfk_page_size'])) { 28 | $this->page_size = $_COOKIE['tfk_page_size']; 29 | } elseif ($this->options['tasks_per_page']) { 30 | $this->page_size = $this->options['tasks_per_page']; 31 | } else { 32 | $this->page_size = 5; 33 | } 34 | 35 | // show "new task" or not 36 | $project = new tfk_project(); 37 | $this->user_can_post = $project->load_count(array( 38 | 'where' => tfk_user::get_roles_sql('who_post').' AND trashed = 0', 39 | )); 40 | 41 | // show only drafts to their author and to users who have the right to manage the project 42 | if (is_user_logged_in()) { 43 | $current_user = wp_get_current_user(); 44 | $can_see_drafts = '( item_status_action_code <> 0 OR item.author_id = '.$current_user->ID.' )'; 45 | } else { 46 | $can_see_drafts = 'item_status_action_code <> 0'; 47 | } 48 | 49 | $this->data->load_list( 50 | array( 51 | 'where' => tfk_user::get_roles_sql('who_read').' AND trashed = 0', 52 | 'having' => $can_see_drafts 53 | .($this->filters['filter_task'] == 'all' ? '' : ' AND item_status_action_code = '.$this->filters['filter_task']), 54 | 'order' => $sort.' '.$order.', priority ASC', 55 | 'page_size' => $this->page_size, 56 | 'page' => $this->page, 57 | 'count' => true, 58 | ) 59 | ); 60 | 61 | $this->npages = ceil($this->data->total() / $this->page_size); 62 | 63 | $this->prio_size = !empty($this->options['prio_size']); 64 | 65 | $this->view('front/task_list.php'); 66 | -------------------------------------------------------------------------------- /inc/controllers/front/task_recent.php: -------------------------------------------------------------------------------- 1 | baselink = add_query_arg('mode', 'recent', tzn_tools::baselink()); 13 | 14 | $this->log = new tfk_item_status_info(); 15 | 16 | switch ($this->filters['filter_recent']) { 17 | case 'tasks': 18 | $filter = 'log.item_id <> 0 AND log.comment_id = 0'; 19 | break; 20 | case 'projects': 21 | $filter = 'log.project_id <> 0'; 22 | break; 23 | case 'comments': 24 | $filter = 'log.comment_id <> 0'; 25 | break; 26 | default: 27 | $filter = ''; 28 | } 29 | 30 | if ($this->options['number_updates']) { 31 | $date_delta = $this->options['number_updates']; 32 | } else { 33 | $date_delta = 7; 34 | } 35 | 36 | $this->log->load_list( 37 | array( 38 | 'where' => 'DATEDIFF(NOW(), log_date) < '.$date_delta. 39 | ($filter ? ' AND '.$filter : ''), 40 | ) 41 | ); 42 | 43 | $this->view('front/recent.php'); -------------------------------------------------------------------------------- /inc/controllers/front/task_status.php: -------------------------------------------------------------------------------- 1 | '.__("Sorry, request forbidden for security reasons.", 'taskfreak').'

    '; 14 | return; 15 | } 16 | 17 | $this->pid = intval($_REQUEST['edit']); 18 | 19 | $this->data = new tfk_item(); 20 | $this->status = new tfk_item_status(); 21 | $this->statuses = $this->status->get_status_list(false, 'one task'); 22 | 23 | $status_code = isset($_GET['status']) && isset($this->statuses[$_GET['status']]) ? $_GET['status'] : -1; 24 | 25 | if ($this->pid) { 26 | $this->data->set_uid($this->pid); 27 | if (!$this->data->load()) { 28 | $this->message = 'ERR '.__('No such item', 'taskfreak'); 29 | } else { 30 | if (get_current_user_id() != $this->data->get('author_id') // current user must be the author of the task 31 | && !$this->data->get('project')->check_access('manage')) { // or must be a manager of the project 32 | $this->message = 'ERR '.__("You can't change this status", 'taskfreak'); 33 | } else { 34 | if ($status_code > -1 35 | && ($status_code == $this->data->get_status()->get('action_code') 36 | || $this->data->set_status($status_code)) 37 | ) { 38 | $this->message = 'OK '.$this->statuses[$status_code]; 39 | } else { 40 | $this->message = 'ERR '.__('Unable to set status', 'taskfreak'); 41 | } 42 | } 43 | } 44 | } else { 45 | $this->message = 'ERR '.__('No such item', 'taskfreak'); 46 | } 47 | $this->view('front/status.php'); 48 | -------------------------------------------------------------------------------- /inc/controllers/front/task_view.php: -------------------------------------------------------------------------------- 1 | pid = intval($_REQUEST['view']); 13 | 14 | $this->data = new tfk_item(); 15 | 16 | if ($this->pid) { 17 | $this->data->set_uid($this->pid); 18 | if (!$this->data->load()) { 19 | echo '

    '.__('Sorry, item not found', 'taskfreak').'

    '; 20 | return; 21 | } 22 | } else { 23 | echo '

    '.__('Missing "view" parameter', 'taskfreak').'

    '; 24 | return; 25 | } 26 | 27 | $this->user_can_comment_project = $this->data->get('project')->check_access('comment'); 28 | $this->user_can_edit_task = get_current_user_id() == $this->data->get('author_id') // must be the author of the task 29 | || $this->data->get('project')->check_access('manage'); // or a manager of the project 30 | 31 | if (($this->data->get_status()->get('action_code') == 0 && get_current_user_id() != $this->data->get('author_id')) 32 | || !$this->data->get('project')->check_access('read')) { 33 | echo '

    '.__("Sorry, you can't read this task. Please contact an admin.", 'taskfreak').'

    '; 34 | return; 35 | } 36 | 37 | $this->comment = new tfk_item_comment(); 38 | 39 | if (isset($_POST['edit'])) { 40 | // Submit data, check and save 41 | 42 | // we have to do this because of http://codex.wordpress.org/Function_Reference/stripslashes_deep#Notes 43 | $_POST = stripslashes_deep($_POST); 44 | 45 | $this->comment->set_auto($_POST); 46 | 47 | if (!$this->user_can_comment_project) { 48 | echo '

    '.__("Sorry, you can't comment tasks in this project. Please contact an admin.", 'taskfreak').'

    '; 49 | return; 50 | } elseif ($this->comment->check()) { 51 | $this->comment->set('body', $_POST['body'], 'HTM'); 52 | $this->comment->set('item_id', $this->pid); 53 | $this->comment->set('post_date', 'NOW'); 54 | $this->comment->set('user_id', get_current_user_id()); 55 | $this->comment->insert(); 56 | $log = new tfk_item_status(); 57 | $log->set('log_date', 'NOW'); 58 | $log->set('item_id', $this->pid); 59 | $log->set_object('user', get_current_user_id()); 60 | $log->set('comment_id', $this->comment->get_uid()); 61 | $log->save(); 62 | 63 | $this->comment->errors['file'] = ''; 64 | if ($this->options['comment_upload']) { 65 | $wp_upload_dir = wp_upload_dir(); 66 | for ($i = 1; $i <= 3 ; $i++) { // up to 3 uploads per comment 67 | if (!empty($_FILES["tfk_file_$i"]['name'])) { 68 | $file = new tfk_item_file(); 69 | if ($file->upload("tfk_file_$i")) { 70 | $file->set('item_id', $this->pid); 71 | $file->set('user_id', get_current_user_id()); 72 | $file->set('file_tags', 'comment'); 73 | $file->save(); 74 | $this->comment->set('body', $this->comment->get('body'). 75 | '

    '.__('Uploaded file: ', 'taskfreak') 76 | .'' 77 | .$file->get('file_title') 78 | .'' 79 | .'

    '); 80 | $this->comment->update(); 81 | } else { 82 | $this->comment->errors['file'] .= '

    '.$file->error.'

    '; 83 | } 84 | } // else no file, do nothing 85 | } 86 | } 87 | 88 | if (!$this->comment->errors['file']) { 89 | if (headers_sent()) { 90 | $this->data->load(); 91 | $this->comment->errors['global_errors'] = 92 | '

    ' 93 | .'' 94 | .__('See comment', 'taskfreak') 95 | .'' 96 | .__('Comment saved.', 'taskfreak') 97 | .'

    '; 98 | } else { 99 | wp_redirect(remove_query_arg('noheader', add_query_arg('view', $this->pid)).'#tfk_comment_'.$this->comment->get_uid()); 100 | exit(); 101 | } 102 | } 103 | } 104 | } elseif (preg_match('/noheader=1/', $_SERVER['REQUEST_URI'])) { 105 | echo '

    '.__('Upload failed. Check file size.', 'taskfreak').'

    '; 106 | return; 107 | } 108 | 109 | $this->comment->load_list(array('where' => 'item_id = '.$this->pid)); 110 | 111 | $this->file = new tfk_item_file(); 112 | $this->file->load_list(array('where' => 'item_id = '.$this->pid.' and file_tags = "task"')); 113 | 114 | $this->view('front/task.php'); -------------------------------------------------------------------------------- /inc/install.php: -------------------------------------------------------------------------------- 1 | get_charset_collate(); 18 | 19 | // register options 20 | $arr = array( 21 | 'version' => '1.0', 22 | 'mode' => 'company', // or club 23 | 'page_id' => 0, // page ID to TFWP 24 | 'page_url' => '', // URL to page 25 | 'tfk_all' => 'projects', // projects, tasks, recent, dashboard (PRO) 26 | 'tasks_per_page' => 5, 27 | 'format_date' => 'Y-m-d', 28 | 'format_time' => 'H:i', 29 | 'number_updates' => 7, 30 | 'avatar_size' => 48, 31 | 'proximity' => 0, // 1 to enable 32 | 'prio_size' => 0, // 1 to enable 33 | 'access_read' => 'subscriber', 34 | 'access_comment' => 'contributor', 35 | 'access_post' => 'author', 36 | 'access_manage' => 'editor', 37 | 'comment_upload' => 1, // 0 to disable 38 | ); 39 | add_option('tfk_options', $arr, '', 'no'); // no = not autoloaded 40 | 41 | // create custom tables (if needed) 42 | 43 | require_once(ABSPATH.'wp-admin/includes/upgrade.php'); 44 | 45 | $table = $wpdb->prefix.'tfk_item'; 46 | $sql = "CREATE TABLE $table ( 47 | item_id int(1) UNSIGNED NOT NULL AUTO_INCREMENT, 48 | project_id mediumint(8) unsigned NOT NULL DEFAULT '0', 49 | item_parent_id int(10) unsigned NOT NULL DEFAULT '0', 50 | priority tinyint(3) unsigned NOT NULL DEFAULT '0', 51 | context varchar(64) NOT NULL DEFAULT '0', 52 | title varchar(255) NOT NULL DEFAULT '', 53 | description text NOT NULL, 54 | creation_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 55 | start_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 56 | deadline_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 57 | completion_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 58 | expected_duration smallint(5) unsigned NOT NULL DEFAULT '0', 59 | actual_duration smallint(5) unsigned NOT NULL DEFAULT '0', 60 | show_in_calendar tinyint(1) unsigned NOT NULL DEFAULT '0', 61 | show_private tinyint(1) unsigned NOT NULL DEFAULT '0', 62 | user_id mediumint(8) unsigned NOT NULL DEFAULT '0', 63 | author_id mediumint(8) unsigned NOT NULL DEFAULT '0', 64 | last_change_author_id mediumint(8) unsigned NOT NULL DEFAULT '0', 65 | PRIMARY KEY (item_id), 66 | KEY project_id (project_id), 67 | KEY user_id (user_id), 68 | KEY author_id (author_id) 69 | ) $charset_collate;"; 70 | dbDelta($sql); 71 | 72 | $table = $wpdb->prefix.'tfk_item_status'; 73 | $sql = "CREATE TABLE $table ( 74 | item_id int(10) unsigned NOT NULL DEFAULT '0', 75 | status_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 76 | status_key tinyint(3) unsigned NOT NULL DEFAULT '0', 77 | user_id mediumint(8) unsigned NOT NULL DEFAULT '0', 78 | PRIMARY KEY (item_id,status_date), 79 | KEY itemId (user_id) 80 | ) $charset_collate;"; 81 | dbDelta($sql); 82 | 83 | $table = $wpdb->prefix.'tfk_item_like'; 84 | $sql = "CREATE TABLE $table ( 85 | item_id mediumint(8) unsigned NOT NULL, 86 | user_id mediumint(8) unsigned NOT NULL, 87 | post_date datetime NOT NULL, 88 | PRIMARY KEY (item_id,user_id) 89 | ) $charset_collate;"; 90 | dbDelta($sql); 91 | 92 | $table = $wpdb->prefix.'tfk_item_comment'; 93 | $sql = "CREATE TABLE $table ( 94 | item_comment_id bigint(20) unsigned NOT NULL AUTO_INCREMENT, 95 | item_id int(10) unsigned NOT NULL DEFAULT '0', 96 | user_id mediumint(8) unsigned NOT NULL DEFAULT '0', 97 | post_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 98 | body text NOT NULL, 99 | PRIMARY KEY (item_comment_id), 100 | KEY itemId (item_id) 101 | ) $charset_collate;"; 102 | dbDelta($sql); 103 | 104 | $table = $wpdb->prefix.'tfk_item_comment_like'; 105 | $sql = "CREATE TABLE $table ( 106 | item_comment_id int(10) unsigned NOT NULL, 107 | user_id mediumint(8) unsigned NOT NULL, 108 | post_date datetime NOT NULL, 109 | vote tinyint(1) unsigned NOT NULL, 110 | PRIMARY KEY (item_comment_id,user_id) 111 | ) $charset_collate;"; 112 | dbDelta($sql); 113 | 114 | $table = $wpdb->prefix.'tfk_item_file'; 115 | $sql = "CREATE TABLE $table ( 116 | item_file_id bigint(20) unsigned NOT NULL AUTO_INCREMENT, 117 | item_id int(10) unsigned NOT NULL DEFAULT '0', 118 | user_id mediumint(8) unsigned NOT NULL DEFAULT '0', 119 | file_title varchar(200) NOT NULL DEFAULT '', 120 | file_name varchar(127) NOT NULL DEFAULT '', 121 | file_type varchar(30) NOT NULL DEFAULT '', 122 | file_size bigint(20) NOT NULL DEFAULT '0', 123 | post_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 124 | file_tags varchar(255) NOT NULL DEFAULT '', 125 | PRIMARY KEY (item_file_id), 126 | KEY item_id (item_id) 127 | ) $charset_collate;"; 128 | dbDelta($sql); 129 | 130 | 131 | $table = $wpdb->prefix.'tfk_project'; 132 | $sql = "CREATE TABLE $table ( 133 | project_id mediumint(8) unsigned NOT NULL AUTO_INCREMENT, 134 | name varchar(120) NOT NULL DEFAULT '', 135 | description text NOT NULL, 136 | who_read varchar(64) NOT NULL, 137 | who_comment varchar(64) NOT NULL, 138 | who_post varchar(64) NOT NULL, 139 | who_manage varchar(64) NOT NULL, 140 | creation_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 141 | trashed tinyint(1) unsigned NOT NULL DEFAULT '0', 142 | PRIMARY KEY (project_id) 143 | ) $charset_collate;"; 144 | dbDelta($sql); 145 | 146 | $table = $wpdb->prefix.'tfk_project_user'; 147 | $sql = "CREATE TABLE $table ( 148 | project_id mediumint(8) unsigned NOT NULL DEFAULT '0', 149 | user_id mediumint(8) unsigned NOT NULL DEFAULT '0', 150 | position tinyint(2) unsigned NOT NULL DEFAULT '0', 151 | PRIMARY KEY (project_id,user_id) 152 | ) $charset_collate;"; 153 | dbDelta($sql); 154 | 155 | $table = $wpdb->prefix.'tfk_project_status'; 156 | $sql = "CREATE TABLE $table ( 157 | project_id mediumint(10) unsigned NOT NULL DEFAULT '0', 158 | status_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 159 | status_key tinyint(3) unsigned NOT NULL DEFAULT '0', 160 | user_id mediumint(8) unsigned NOT NULL DEFAULT '0', 161 | PRIMARY KEY (project_id,status_date) 162 | ) $charset_collate;"; 163 | dbDelta($sql); 164 | 165 | $table = $wpdb->prefix.'tfk_log'; 166 | $sql = "CREATE TABLE $table ( 167 | log_date datetime NOT NULL, 168 | user_id mediumint(8) unsigned NOT NULL DEFAULT 0, 169 | action_code varchar(3) NOT NULL DEFAULT '', 170 | item_id int(10) unsigned NOT NULL DEFAULT 0, 171 | project_id mediumint(8) unsigned NOT NULL DEFAULT 0, 172 | comment_id int(10) unsigned NOT NULL DEFAULT 0, 173 | info varchar(255) NOT NULL DEFAULT '', 174 | hidden tinyint(1) unsigned NOT NULL DEFAULT 0, 175 | PRIMARY KEY (log_date,user_id,action_code), 176 | KEY project_id (project_id), 177 | KEY item_id (item_id) 178 | ) $charset_collate"; 179 | dbDelta($sql); 180 | 181 | } // end install 182 | 183 | register_deactivation_hook(TFK_ROOT_FILE, 'tfk_deactivate'); 184 | 185 | /** 186 | * called when deactivating (not deleting) plugin 187 | */ 188 | function tfk_deactivate() { 189 | // remove options 190 | delete_option('tfk_options'); 191 | } 192 | -------------------------------------------------------------------------------- /inc/models/item.php: -------------------------------------------------------------------------------- 1 | Draft 11 | 20 => In Progress 12 | 30 => Suspended 13 | 60 => Closed 14 | 15 | */ 16 | 17 | class tfk_item extends tzn_model 18 | { 19 | 20 | public function __construct() { 21 | parent::__construct( 22 | 'item', 23 | array( 24 | 'item_id' => 'UID', 25 | 'project' => 'OBJ', 26 | 'priority' => 'NUM', 27 | 'title' => 'STR', 28 | 'description' => 'HTM', 29 | 'deadline_date' => 'DTE', 30 | 'creation_date' => 'DTM', 31 | 'user_id' => 'NUM', 32 | 'author_id' => 'NUM' 33 | ) 34 | ); 35 | 36 | $this->errors['global_errors'] = ''; 37 | $this->errors['status'] = ''; 38 | $this->errors['file'] = ''; 39 | foreach ($this->properties as $k => $v) { 40 | $this->errors[$k] = ''; 41 | } 42 | } 43 | 44 | /** 45 | * check before saving task 46 | */ 47 | public function check() { 48 | $msg = array(); 49 | if (empty($this->data['title'])) { 50 | $msg['title'] = __('Title should not be blank', 'taskfreak'); 51 | } 52 | if (!preg_match('/^[123]$/', $this->data['priority'])) { 53 | $msg['priority'] = __('Priority should be 1, 2, or 3', 'taskfreak'); 54 | } 55 | if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $this->data['deadline_date'])) { 56 | $msg['deadline_date'] = __('Invalid date format', 'taskfreak'); 57 | } 58 | if (strlen($this->data['description']) > 65535) { 59 | $msg['description'] = __('Description is too big (maybe you pasted an image into it?)', 'taskfreak'); 60 | } 61 | 62 | foreach ($msg as $k => $v) { 63 | $this->errors[$k] = '

    '.__($v, 'taskfreak').'

    '; 64 | } 65 | return empty($msg); 66 | } 67 | 68 | /** 69 | * load current task status 70 | * @return tfk_item_status 71 | */ 72 | public function get_status() { 73 | $obj = new tfk_item_status(); 74 | if ($pid = $this->get_uid()) { 75 | if ($obj->load_list(array( 76 | 'where' => 'item_id='.$pid.' AND action_code <> "" AND comment_id = 0', 77 | 'order' => 'log_date DESC', 78 | 'limit' => 1 79 | ))) { 80 | return $obj->next(true); 81 | } 82 | } 83 | 84 | // no status (new item or not loaded) 85 | $obj->set('action_code', 0); 86 | $obj->set('log_date', 'NOW'); 87 | return $obj; 88 | } 89 | 90 | /** 91 | * set task status 92 | * @param number $status status code 93 | * @param number $uid (optional) id of the user setting the status 94 | * @param string $info (optional) may contain "creation" for example 95 | * @return boolean 96 | */ 97 | public function set_status($status, $uid=null, $info=null) { 98 | if (is_null($uid)) { 99 | $uid = get_current_user_id(); 100 | } 101 | $obj = new tfk_item_status(); 102 | $obj->set('item_id', $this->get_uid()); 103 | $obj->set('log_date','NOW'); 104 | $obj->set('action_code', $status); 105 | $obj->set_object('user', $uid); 106 | if (!is_null($info)) { 107 | $obj->set('info', $info); 108 | } 109 | return $obj->insert('ignore'); 110 | } 111 | 112 | /** 113 | * get deadline proximity bar width in pixels 114 | * @return number 115 | */ 116 | public function get_deadline_proximity_bar() { 117 | if ($this->data['deadline_date'] == '0000-00-00 00:00:00') { 118 | return 300; 119 | } 120 | $date_diff_in_sec = strtotime($this->data['deadline_date']) + 24*60*60 - time() - tzn_tools::get_user_timezone_offset(); 121 | if ($date_diff_in_sec < 86400) 122 | return 300; // px 123 | elseif ($date_diff_in_sec > 365 * 86400) 124 | return 30; 125 | else 126 | return 300 - ($date_diff_in_sec / 86400 * 270 / 365); 127 | } 128 | 129 | /** 130 | * get deadline proximity in words 131 | * @return string 132 | */ 133 | public function get_deadline_proximity() { 134 | if ($this->data['deadline_date'] == '0000-00-00 00:00:00') 135 | return __('Undefined Deadline', 'taskfreak'); 136 | 137 | $date_diff['sec'] = strtotime($this->data['deadline_date']) + 24*60*60 - time() - tzn_tools::get_user_timezone_offset(); 138 | $date_diff['min'] = round($date_diff['sec'] / 60); 139 | $date_diff['hour'] = round($date_diff['sec'] / 3600); 140 | $date_diff['day'] = round($date_diff['sec'] / 86400); 141 | $date_diff['month'] = round($date_diff['sec'] / 2592000); 142 | if ($date_diff['sec'] < 0) { 143 | return __("Past due !", 'taskfreak'); 144 | } 145 | if ($date_diff['sec'] < 3600) { 146 | return sprintf(_n("%s minute remaining", "%s minutes remaining", $date_diff['min'], 'taskfreak'), $date_diff['min']); 147 | } 148 | if ($date_diff['sec'] < 86400) { 149 | return sprintf(_n("%s hour remaining", "%s hours remaining", $date_diff['hour'], 'taskfreak'), $date_diff['hour']); 150 | } 151 | if ($date_diff['sec'] < 365 * 86400) { 152 | return sprintf(_n("%s day remaining", "%s days remaining", $date_diff['day'], 'taskfreak'), $date_diff['day']); 153 | } 154 | return sprintf(_n("%s month remaining", "%s months remaining", $date_diff['month'], 'taskfreak'), $date_diff['month']); 155 | } 156 | 157 | /** 158 | * get an extract from the description (body) of the task 159 | * useful for the HTML title attribute 160 | * @return string 161 | */ 162 | public function get_description_extract() { 163 | $description_text = preg_replace('/ /', ' ', $this->data['description']); 164 | $description_text = html_entity_decode(strip_tags($description_text)); 165 | if (strlen($description_text) > 140) { 166 | $description_text = substr($description_text, 0, 140); 167 | $description_text = preg_replace('/\w+$/', '', $description_text); // prevent cutting words 168 | $description_text .= '[…]'; 169 | } 170 | return htmlspecialchars($description_text); 171 | } 172 | } 173 | 174 | class tfk_item_status extends tfk_log { 175 | 176 | public function __construct() { 177 | parent::__construct(); 178 | } 179 | 180 | /** 181 | * get a list of statuses in translated strings 182 | * @param mixed $draft include draft status ? 183 | * @param string $context by default, context for translation is "many tasks" 184 | * @return array 185 | */ 186 | public static function get_status_list($draft, $context='many tasks') { 187 | $arr = array( 188 | 0 => _x('Draft', $context, 'taskfreak'), 189 | 20 => _x('In Progress', $context, 'taskfreak'), 190 | 30 => _x('Suspended', $context, 'taskfreak'), 191 | 60 => _x('Closed', $context, 'taskfreak') 192 | ); 193 | if (!$draft) { 194 | unset($arr[0]); 195 | } 196 | return $arr; 197 | } 198 | 199 | /** 200 | * get task current status in string 201 | * @return string 202 | */ 203 | public function get_status() { 204 | $key = $this->get('action_code',0); 205 | switch($key) { 206 | case 0: 207 | return __('Draft', 'taskfreak'); 208 | case 20: 209 | return __('In Progress', 'taskfreak'); 210 | case 30: 211 | return __('Suspended', 'taskfreak'); 212 | case 60: 213 | return __('Closed', 'taskfreak'); 214 | default: 215 | return __('Unknown Status !', 'taskfreak'); 216 | } 217 | } 218 | 219 | /** 220 | * get an HTML select (dropdown list) of task statuses 221 | * @param string $name name of the HTML select element 222 | * @param number $selected (optional) current status, 0 by default 223 | * @param string $xtra (optional) class for the select element 224 | * @param string $draft (optional) include draft status ? 225 | * @return string 226 | */ 227 | public static function list_select($name, $selected=0, $xtra='class="tzn_option_select"', $draft=true) { 228 | return tzn_tools::form_select($name, self::get_status_list($draft), $selected, $xtra); 229 | } 230 | 231 | /** 232 | * get a HTML li (list) of links to task statuses + "All tasks" for filtering status 233 | * @param unknown $url base URL 234 | * @param unknown $name name of the filter argument inside the query 235 | * @param unknown $current current filter 236 | * @return string 237 | */ 238 | public static function list_links($url, $name, $current) { 239 | return tzn_tools::form_links($url, $name, array('all' => _x('All tasks', 'tasks', 'taskfreak')), $current) 240 | .tzn_tools::form_links($url, $name, self::get_status_list(is_user_logged_in()), $current); 241 | } 242 | 243 | } 244 | 245 | class tfk_item_comment extends tzn_model 246 | { 247 | public function __construct() { 248 | parent::__construct( 249 | 'item_comment', 250 | array( 251 | 'item_comment_id' => 'UID', 252 | 'item_id' => 'NUM', 253 | 'user_id' => 'NUM', 254 | 'post_date' => 'DTM', 255 | 'body' => 'HTM', 256 | 'last_change_date' => 'DTM' 257 | ) 258 | ); 259 | 260 | $this->errors['global_errors'] = ''; 261 | $this->errors['file'] = ''; 262 | foreach ($this->properties as $k => $v) { 263 | $this->errors[$k] = ''; 264 | } 265 | } 266 | 267 | /** 268 | * check task before saving 269 | * @return boolean 270 | */ 271 | public function check() { 272 | if (empty($this->data['body'])) { 273 | $this->errors["body"] = '

    '.__('Empty comment not allowed.', 'taskfreak').'

    '; 274 | return false; 275 | } 276 | return true; 277 | } 278 | } 279 | 280 | class tfk_item_file extends tzn_model 281 | { 282 | public function __construct() { 283 | $this->error = ''; 284 | parent::__construct( 285 | 'item_file', 286 | array( 287 | 'item_file_id' => 'UID', 288 | 'item_id' => 'NUM', 289 | 'user_id' => 'NUM', 290 | 'file_title' => 'STR', 291 | 'file_name' => 'STR', 292 | 'file_type' => 'STR', 293 | 'file_size' => 'NUM', 294 | 'post_date' => 'DTM', 295 | 'last_change_date' => 'DTM', 296 | 'file_tags' => 'STR' 297 | ) 298 | ); 299 | } 300 | 301 | /** 302 | * save uploaded file to disk and metadata into database 303 | * @param array $file_input file input information from PHP 304 | * @return boolean 305 | */ 306 | public function upload($file_input) { 307 | $wp_upload_dir = wp_upload_dir(); 308 | if ($_FILES[$file_input]['error'] == UPLOAD_ERR_OK) { 309 | if (preg_match('/(php|phtml|phps)/i', $_FILES[$file_input]['type']) 310 | || preg_match('/\.(php|phtml|phps)$/i', $_FILES[$file_input]['name'])) { 311 | $this->error = __('Forbidden upload: ', 'taskfreak').$_FILES[$file_input]['name']; 312 | return false; 313 | } else { 314 | $file_title = $_FILES[$file_input]['name']; 315 | $file_name = time().'_'.preg_replace('/[^\w\d.-_,]/', '-', $file_title); 316 | if (!@move_uploaded_file($_FILES[$file_input]['tmp_name'], $wp_upload_dir['path'].'/'.$file_name)) { 317 | $this->error = __('Unable to write file to upload directory. Please tell an admin.', 'taskfreak'); 318 | return false; 319 | } else { 320 | @chmod($wp_upload_dir['path'].'/'.$file_name, 0444); 321 | $this->set('file_title', $file_title); 322 | $this->set('file_name', $file_name); 323 | $this->set('file_size', $_FILES[$file_input]['size']); 324 | $this->set('file_type', $_FILES[$file_input]['type']); 325 | $this->set('post_date', 'NOW'); 326 | return true; 327 | } 328 | } 329 | } else { 330 | $this->error = __('Upload of ', 'taskfreak').$_FILES[$file_input]['name'].__(' failed with error code ', 'taskfreak').$_FILES[$file_input]['error']; 331 | return false; 332 | } 333 | } 334 | } 335 | 336 | class tfk_item_info extends tfk_item 337 | { 338 | public function __construct() { 339 | parent::__construct(); 340 | $this->properties['comment_count'] = 'NUM'; 341 | $this->properties['file_count'] = 'NUM'; 342 | $this->properties['item_status'] = 'OBJ'; 343 | $this->properties['item_status_info'] = 'STR'; 344 | $this->properties['item_status_date'] = 'DTM'; 345 | $this->properties['item_status_user_id'] = 'NUM'; 346 | $this->properties['proximity'] = 'NUM'; 347 | } 348 | 349 | /** 350 | * (non-PHPdoc) 351 | * @see tzn_model::load_list() 352 | */ 353 | public function load_list($args=null) { 354 | $sql = array( 355 | 'sql' => 'SELECT '.$this->db_alias().'.*, 356 | IFNULL(DATEDIFF('.$this->db_alias().'.deadline_date, NOW()), 9999999999) AS proximity, 357 | project.project_id, 358 | project.name, 359 | project.description AS project_description, 360 | project.who_read, 361 | project.who_comment, 362 | project.who_post, 363 | project.who_manage, 364 | project.trashed, 365 | user.*, 366 | item_status.log_date AS item_status_date, 367 | item_status.info AS item_status_info, 368 | COUNT(DISTINCT item_comment.item_comment_id) AS comment_count, 369 | COUNT(DISTINCT item_file.item_file_id) AS file_count, 370 | (SELECT action_code 371 | FROM '.$this->db_table('log').' 372 | WHERE item_id = item.item_id 373 | AND comment_id = 0 374 | AND action_code <> "" 375 | ORDER BY log_date DESC 376 | LIMIT 1) AS item_status_action_code, 377 | item_status.user_id AS item_status_user_id 378 | FROM '.$this->db_table().' AS '.$this->db_alias().' 379 | INNER JOIN '.$this->db_table('project').' AS project ON item.project_id = project.project_id 380 | INNER JOIN '.$this->db_table('log').' AS item_status ON item.item_id = item_status.item_id 381 | LEFT JOIN '.$this->db->base_prefix.'users AS user ON user.ID = item.user_id 382 | LEFT JOIN '.$this->db_table('item_comment').' AS item_comment ON item.item_id = item_comment.item_id 383 | LEFT JOIN '.$this->db_table('item_file').' AS item_file ON item.item_id = item_file.item_id AND file_tags = "task" 384 | ', 385 | 'count' => true, 386 | 'group' => $this->db_alias().'.item_id' 387 | ); 388 | 389 | $where = 'item_status.log_date = ( 390 | SELECT MAX(log_date) 391 | FROM '.$this->db_table('log').' 392 | WHERE item_id = item.item_id 393 | AND comment_id = 0 394 | )'; 395 | 396 | if ($args) { 397 | if (isset($args['where'])) { 398 | $where .= ' AND '.$args['where']; 399 | unset($args['where']); 400 | } 401 | $args['where'] = $where; 402 | $args = array_merge($args, $sql); 403 | } else { 404 | $sql['where'] = $where; 405 | $args = $sql; 406 | } 407 | return parent::load_list($args); 408 | } 409 | 410 | /** 411 | * get number of attachments in string (for an HTML title attribute) 412 | * @return string 413 | */ 414 | public function get_attachments() { 415 | return sprintf(_n('%d attachment', '%d attachments', $this->data['file_count'], 'taskfreak'), $this->data['file_count']); 416 | } 417 | } 418 | 419 | class tfk_item_status_info extends tfk_item_status { 420 | 421 | public function __construct() { 422 | parent::__construct(); 423 | $this->properties['type'] = 'STR'; 424 | $this->properties['creation_date'] = 'DTM'; 425 | $this->properties['title_or_name'] = 'STR'; 426 | } 427 | 428 | /** 429 | * (non-PHPdoc) 430 | * @see tzn_model::load_list() 431 | */ 432 | public function load_list($args=null) { 433 | $sql = array( 434 | 'sql'=> 'SELECT log.*, 435 | user.display_name, 436 | IF (log.item_id = 0, "project", "task") AS type, 437 | IFNULL(title, project.name) AS title_or_name, 438 | IFNULL(item.creation_date, project.creation_date) AS creation_date, 439 | IFNULL(who_read, 440 | (SELECT who_read FROM '.$this->db_table('project').' WHERE project_id = item.project_id LIMIT 1) 441 | ) AS who_read, 442 | IF( log.project_id = 0, 443 | (SELECT action_code FROM '.$this->db_table().' 444 | WHERE item_id = item.item_id 445 | AND action_code <> "" 446 | AND comment_id = 0 447 | ORDER BY log_date DESC LIMIT 1), 448 | (SELECT action_code FROM '.$this->db_table().' 449 | WHERE project_id = project.project_id 450 | AND action_code <> "" 451 | AND comment_id = 0 452 | ORDER BY log_date DESC limit 1) 453 | ) AS status 454 | FROM '.$this->db_table().' AS log 455 | LEFT JOIN '.$this->db_table('item').' AS item ON item.item_id = log.item_id 456 | LEFT JOIN '.$this->db_table('project').' AS project ON project.project_id = log.project_id 457 | LEFT JOIN '.$this->db->base_prefix.'users AS user ON user.ID = log.user_id', 458 | 'where' => '(log.item_id <> 0 OR project.trashed = 0)', 459 | 'having'=> tfk_user::get_roles_sql('who_read').' AND status > 0', 460 | 'order' => 'log_date DESC' 461 | ); 462 | 463 | if (isset($args['where'])) 464 | $sql['where'] .= ' AND '.$args['where']; 465 | 466 | return parent::load_list($sql); 467 | } 468 | } 469 | -------------------------------------------------------------------------------- /inc/models/log.php: -------------------------------------------------------------------------------- 1 | 'DTM', 19 | 'user' => 'OBJ', 20 | 'action_code' => 'STR', 21 | 'item_id' => 'NUM', 22 | 'project_id' => 'NUM', 23 | 'comment_id' => 'NUM', 24 | 'info' => 'STR', 25 | 'hidden' => 'BOL' 26 | ), array( 27 | 'log_date','user_id' 28 | ) 29 | ); 30 | } 31 | 32 | public function check() { 33 | if (empty($this->data['log_date'])) { 34 | $this->set('log_date','NOW'); 35 | } 36 | return (strlen($this->data['action_code']) > 0 && !empty($this->data['user']->id))?true:false; 37 | } 38 | 39 | public function get_info() { 40 | $info_raw = preg_split('/,/', $this->data['info']); 41 | $info_cooked = array( 42 | 'user_id' => __('assignee', 'taskfreak'), 43 | 'description' => __('description', 'taskfreak'), 44 | 'deadline' => __('deadline', 'taskfreak'), 45 | 'priority' => __('priority', 'taskfreak'), 46 | 'title' => __('title', 'taskfreak'), 47 | 'project' => __('project', 'taskfreak'), 48 | ); 49 | $info = array(); 50 | foreach ($info_raw as $key) { 51 | if (preg_match('/(\d+) file\(s\)/', $key, $match)) { 52 | $info[] = sprintf(_n('1 file', '%d files', $match[1], 'taskfreak'), $match[1]); 53 | } else { 54 | $info[] = isset($info_cooked[$key]) ? $info_cooked[$key] : "[$key]"; 55 | } 56 | } 57 | return implode(', ', $info); 58 | } 59 | 60 | /** 61 | * list options for filters 62 | */ 63 | public static function get_option_list() { 64 | return array( 65 | 'tasks' => __('Tasks', 'taskfreak'), 66 | 'projects' => __('Projects', 'taskfreak'), 67 | 'comments' => __('Comments', 'taskfreak') 68 | ); 69 | } 70 | 71 | /** 72 | * filter select 73 | */ 74 | public static function list_select($name, $selected='', $xtra='class="tzn_option_select"') { 75 | return tzn_tools::form_select($name, self::get_option_list(), $selected, $xtra); 76 | } 77 | 78 | /** 79 | * filter links 80 | */ 81 | public static function list_links($url, $name, $current) { 82 | return tzn_tools::form_links($url, $name, array('all' => _x('All', 'updates', 'taskfreak')), $current) 83 | .tzn_tools::form_links($url, $name, self::get_option_list(), $current); 84 | } 85 | } -------------------------------------------------------------------------------- /inc/models/project.php: -------------------------------------------------------------------------------- 1 | Draft 18 | 20 => In Progress 19 | 30 => Suspended 20 | 60 => Closed 21 | 22 | + Boolean trashed 23 | 24 | ** MEMBER POSITION ******* 25 | 26 | 10 => Client / Volunteer 27 | 20 => Freelance / Employee 28 | 30 => Staff / Official 29 | 40 => Manager / Moderator 30 | 50 => Leader / Coordinator 31 | 32 | */ 33 | 34 | class tfk_project extends tzn_model 35 | { 36 | 37 | public function __construct() { 38 | parent::__construct( 39 | 'project', 40 | array( 41 | 'project_id' => 'UID', 42 | 'name' => 'STR', 43 | 'description' => 'HTM', 44 | 'who_read' => 'STR', 45 | 'who_comment' => 'STR', 46 | 'who_post' => 'STR', 47 | 'who_manage' => 'STR', 48 | 'creation_date' => 'DTM', 49 | 'trashed' => 'NUM' 50 | ) 51 | ); 52 | } 53 | 54 | /** 55 | * sets default access rights 56 | */ 57 | public function init_rights($opts) { 58 | $this->set('who_read', $opts['access_read']); 59 | $this->set('who_comment', $opts['access_comment']); 60 | $this->set('who_post', $opts['access_post']); 61 | $this->set('who_manage', $opts['access_manage']); 62 | } 63 | 64 | /** 65 | * check before creating new project 66 | */ 67 | public function check() { 68 | // TODO trim 69 | if (!empty($this->data['name'])) { 70 | return true; 71 | } 72 | } 73 | 74 | /** 75 | * load current status (last one set) 76 | * if new project, status is 0 (draft) 77 | */ 78 | public function get_status() { 79 | $obj = new tfk_project_status(); 80 | if ($pid = $this->get_uid()) { 81 | if ($obj->load_list(array( 82 | 'where' => 'project_id='.$pid, 83 | 'order' => 'log_date DESC' 84 | ))) { 85 | return $obj->next(true); 86 | } 87 | } 88 | 89 | // no status (new project or not loaded) 90 | $obj->set('action_code', 0); 91 | $obj->set('log_date', 'NOW'); 92 | return $obj; 93 | } 94 | 95 | /** 96 | * set new status (saves in database) 97 | * @param status new status key to save 98 | * @param uid user ID, if not provided uses current user's ID 99 | */ 100 | public function set_status($status, $uid=null, $info=null) { 101 | if (is_null($uid)) { 102 | $uid = get_current_user_id(); 103 | } 104 | $obj = new tfk_project_status(); 105 | $obj->set('project_id', $this->get_uid()); 106 | $obj->set('log_date','NOW'); 107 | $obj->set('action_code', $status); 108 | $obj->set_object('user', $uid); 109 | if (!is_null($info)) { 110 | $obj->set('info', $info); 111 | } 112 | return $obj->insert('ignore'); 113 | } 114 | 115 | /** 116 | * get project visibility, i.e. who can access project (read) 117 | */ 118 | public function get_visibility() { 119 | if (empty($this->data['who_read'])) { 120 | return __('Any visitor','taskfreak'); 121 | } else { 122 | $role = get_role( $this->data['who_read'] ); 123 | return translate_user_role($role->name); 124 | } 125 | 126 | } 127 | 128 | /** 129 | * check user acces 130 | */ 131 | public function check_access($what='read', $user_id=null) { 132 | $what = 'who_'.$what; 133 | if (empty($user_id)) { 134 | $user = wp_get_current_user(); 135 | } else { 136 | $user = get_user_by('id', $user_id); 137 | } 138 | 139 | if ($r = $this->get($what)) { 140 | if (tfk_user::check_role($r, $user)) { 141 | // all is good 142 | return true; 143 | } else { 144 | // current user does not have sufficient role 145 | return false; 146 | } 147 | } else { 148 | // no specific role required (public) 149 | return true; 150 | } 151 | } 152 | 153 | /** 154 | * delete project 155 | */ 156 | public function delete($args = '') { 157 | // TODO delete files 158 | return $this->db->query('DELETE '.$this->db_table('project').', ' // .$this->db_table('project_user').', ' 159 | .$this->db_table('log').', '.$this->db_table('item').', '.$this->db_table('item_comment') 160 | .', '.$this->db_table('item_file').', '.$this->db_table('item_status') 161 | .' FROM '.$this->db_table('project') 162 | // .' INNER JOIN '.$this->db_table('project_user').' ON '.$this->db_table('project_user').'.project_id = '.$this->db_table('project').'.project_id' 163 | .' LEFT JOIN '.$this->db_table('log').' ON '.$this->db_table('log').'.project_id = '.$this->db_table('project').'.project_id' 164 | .' LEFT JOIN '.$this->db_table('item').' ON '.$this->db_table('item').'.project_id = '.$this->db_table('project').'.project_id' 165 | .' LEFT JOIN '.$this->db_table('item_comment').' ON '.$this->db_table('item_comment').'.item_id = '.$this->db_table('item').'.item_id' 166 | .' LEFT JOIN '.$this->db_table('item_file').' ON '.$this->db_table('item_file').'.item_id = '.$this->db_table('item').'.item_id' 167 | .' LEFT JOIN '.$this->db_table('item_status').' ON '.$this->db_table('item_status').'.item_id = '.$this->db_table('item').'.item_id' 168 | .' WHERE '.$this->db_table('project').'.project_id = '.$this->get_uid()); 169 | } 170 | 171 | } 172 | 173 | class tfk_project_status extends tfk_log 174 | { 175 | 176 | public function __construct() { 177 | parent::__construct(); 178 | } 179 | 180 | public static function get_status_list($draft) { 181 | $arr = array( 182 | 0 => _x('Draft', 'many projects', 'taskfreak'), 183 | 20 => _x('In Progress', 'many projects', 'taskfreak'), 184 | 30 => _x('Suspended', 'many projects', 'taskfreak'), 185 | 60 => _x('Closed', 'many projects', 'taskfreak') 186 | ); 187 | if (!$draft) { 188 | unset($arr[0]); 189 | } 190 | return $arr; 191 | } 192 | 193 | public function get_status() { 194 | $key = $this->get('action_code',0); 195 | switch($key) { 196 | case 0: 197 | return __('Draft', 'taskfreak'); 198 | case 20: 199 | return __('In Progress', 'taskfreak'); 200 | case 30: 201 | return __('Suspended', 'taskfreak'); 202 | case 60: 203 | return __('Closed', 'taskfreak'); 204 | } 205 | } 206 | 207 | public static function list_select($name, $selected=0, $xtra='class="tzn_option_select"', $draft=true) { 208 | return tzn_tools::form_select($name, self::get_status_list($draft), $selected, $xtra); 209 | } 210 | 211 | public static function list_links($url, $name, $current) { 212 | return tzn_tools::form_links($url, $name, array('all' => _x('All', 'projects', 'taskfreak')), $current) 213 | .tzn_tools::form_links($url, $name, self::get_status_list(current_user_can('manage_options')), $current); 214 | } 215 | 216 | } 217 | 218 | class tfk_project_user extends tzn_model 219 | { 220 | 221 | public function __construct() { 222 | parent::__construct( 223 | 'project_user', 224 | array( 225 | 'project' => 'OBJ', 226 | 'user' => 'OBJ', 227 | 'position' => 'NUM' 228 | ), 229 | array( 230 | 'project_id', 'user_id' 231 | ) 232 | ); 233 | } 234 | 235 | public function get_user_id() { 236 | return $this->get('user')->get_uid(); 237 | } 238 | 239 | public function get_user_name() { 240 | return $this->get('user')->get('display_name'); 241 | } 242 | 243 | public function get_position($pos=null) { 244 | if (is_null($pos)) { 245 | $pos = $this->data['position']; 246 | } 247 | switch ($pos) { 248 | case 0: 249 | return __('Guest', 'taskfreak'); 250 | case 1: 251 | return __('Client', 'taskfreak'); 252 | case 2: 253 | return __('Staff', 'taskfreak'); 254 | case 3: 255 | return __('Manager', 'taskfreak'); 256 | case 4: 257 | return __('Project Leader', 'taskfreak'); 258 | } 259 | } 260 | 261 | public function check_rights($level) { 262 | $level--; 263 | return ($GLOBALS['confProjectRights'][$this->position]{$level} == '1'); 264 | } 265 | 266 | public function load_position($pid, $mid) { 267 | $table = $this->db_table(); 268 | $where = $table.'.project_id='.$pid.' AND '.$table.'.user_id='.$mid; 269 | return $this->load(array( 270 | 'where' => $where 271 | )); 272 | } 273 | 274 | /** 275 | * get list of users already associated with project 276 | * returns array with users' IDs 277 | */ 278 | public static function list_already($id) { 279 | $arr = array(); 280 | $obj = new tfk_project_user(); 281 | $obj->load_list(array( 282 | 'where' => 'project_user.project_id='.$id, 283 | 'order' => 'position DESC' 284 | )); 285 | while ($obj->next()) { 286 | $arr[] = $obj->get_user_id(); 287 | } 288 | return $arr; 289 | } 290 | 291 | public static function list_select($id, $name, $selected=0, $max=4) { 292 | $arr = array( 293 | 0 => __('Guest', 'taskfreak'), 294 | 1 => __('Client', 'taskfreak'), 295 | 2 => __('Staff', 'taskfreak'), 296 | 3 => __('Manager', 'taskfreak'), 297 | 4 => __('Project Leader', 'taskfreak') 298 | ); 299 | 300 | $str = ''; 310 | return $str; 311 | } 312 | 313 | /** 314 | * add first user (current user logged in) 315 | */ 316 | public static function list_insert($pid, $pos=4) { 317 | $obj = new tfk_project_user(); 318 | $obj->set_object('project', $pid); 319 | $obj->set_object('user', get_current_user_id()); 320 | $obj->set('position', $pos); 321 | $obj->insert(); 322 | } 323 | 324 | /** 325 | * update list of users 326 | */ 327 | public static function list_update($pid, $data, $list) { 328 | if (empty($data) || empty($pid)) { 329 | return false; 330 | } 331 | $i=0; 332 | // update existing users 333 | while ($obj = $list->next(true)) { 334 | $uid = $obj->get_user_id(); 335 | if (isset($data[$uid]) && $data[$uid] != $obj->value('position')) { 336 | // position changed, update 337 | $obj->set('position', $data[$uid]); 338 | $obj->update(); 339 | $i++; 340 | } 341 | unset($data[$uid]); 342 | } 343 | // add new users 344 | foreach ($data as $uid => $pos) { 345 | $obj = new tfk_project_user(); 346 | $obj->set_object('project', $pid); 347 | $obj->set_object('user', $uid); 348 | $obj->set('position', $pos); 349 | $obj->insert(); 350 | } 351 | return $i; 352 | } 353 | 354 | /** 355 | * remove users 356 | */ 357 | public static function list_remove($pid, $data) { 358 | if (empty($data) || empty($pid)) { 359 | return false; 360 | } 361 | $sql = 'project_id='.$pid.' AND user_id'; 362 | if (preg_match('/,/', $data)) { 363 | $sql .= ' IN ('.$data.')'; 364 | } else { 365 | $sql .= '='.$data; 366 | } 367 | $obj = new tfk_project_user(); 368 | return $obj->delete(array('where'=>$sql)); 369 | } 370 | 371 | } 372 | 373 | class tfk_project_info extends tfk_project 374 | { 375 | 376 | public function __construct() { 377 | parent::__construct(); 378 | $this->properties['project_status'] = 'OBJ'; 379 | } 380 | 381 | public function load_list($args=null) { 382 | $sql = array( 383 | 'sql' => 'SELECT 384 | project.project_id, 385 | project.name, 386 | project.description, 387 | project.who_read, 388 | project.who_comment, 389 | project.who_post, 390 | project.who_manage, 391 | project.trashed, 392 | project_status.log_date AS project_status_log_date, 393 | project_status.action_code AS project_status_action_code, 394 | project_status.project_id AS project_status_project_id, 395 | project_status.user_id AS project_status_user_id 396 | FROM '.$this->db_table().' AS '.$this->db_alias().' 397 | INNER JOIN '.$this->db_table('log').' AS project_status 398 | ON project.project_id = project_status.project_id 399 | AND project_status.item_id = 0 400 | ', 401 | 'count' => true, 402 | 'group' => 'project_status.project_id' 403 | ); 404 | 405 | $where = 'project_status.log_date = ( 406 | SELECT MAX(log_date) 407 | FROM '.$this->db_table('log').' 408 | WHERE project.project_id = project_id 409 | AND item_id = 0 410 | )'; 411 | 412 | if ($args) { 413 | if (isset($args['where'])) { 414 | $where = $where .= ' AND '.$args['where']; 415 | unset($args['where']); 416 | } 417 | $args['where'] = $where; 418 | $args = array_merge($args, $sql); 419 | } else { 420 | $sql['where'] = $where; 421 | $args = $sql; 422 | } 423 | return parent::load_list($args); 424 | } 425 | 426 | } -------------------------------------------------------------------------------- /inc/models/user.php: -------------------------------------------------------------------------------- 1 | 'UID', 26 | 'user_login' => 'STR', 27 | 'user_nicename' => 'STR', 28 | 'user_email' => 'EML', 29 | 'user_url' => 'STR', 30 | 'display_name' => 'STR', 31 | 'status' => 'NUM' 32 | ), 33 | 'ID' 34 | ); 35 | } 36 | 37 | public function db_table($table = '') { 38 | return $this->db->base_prefix.'users'; 39 | } 40 | 41 | // --- ACCESS RIGHTS ------- 42 | 43 | public static function get_role($user=null) { 44 | global $wp_roles; 45 | if (empty($user)) { 46 | $user = wp_get_current_user(); 47 | } 48 | $roles = $user->roles; 49 | return array_shift($roles); 50 | } 51 | 52 | public static function get_role_list($user=null) { 53 | 54 | $arr = array(); 55 | 56 | if (is_user_logged_in()) { 57 | switch (self::get_role($user)) { 58 | case 'administrator': 59 | $arr[] = 'administrator'; 60 | case 'editor': 61 | $arr[] = 'editor'; 62 | case 'author': 63 | $arr[] = 'author'; 64 | case 'contributor': 65 | $arr[] = 'contributor'; 66 | case 'subscriber': 67 | $arr[] = 'subscriber'; 68 | } 69 | } 70 | return $arr; 71 | } 72 | 73 | public static function get_roles_sql($p) { 74 | $arr = self::get_role_list(); 75 | if (!count($arr)) { 76 | return "$p=''"; // no minimum role required 77 | } else { 78 | return "$p IN ('','".implode("','", $arr)."')"; 79 | } 80 | } 81 | 82 | public static function check_role($r, $user=null) { 83 | return (in_array($r, self::get_role_list($user))); 84 | } 85 | 86 | // --- DISABLE INSERT, UPDATE and DELETE from DB --- 87 | 88 | public function update($args='') { 89 | return false; 90 | } 91 | 92 | public function insert($option='') { 93 | return false; 94 | } 95 | 96 | public function delete($args='') { 97 | return false; 98 | } 99 | 100 | } 101 | 102 | class tfk_author extends tfk_user 103 | { 104 | 105 | function __construct() { 106 | parent::__construct(); 107 | } 108 | 109 | public function db_alias() { 110 | return 'author'; 111 | } 112 | 113 | } -------------------------------------------------------------------------------- /inc/views/admin/about.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 |

    About TaskFreak!

    4 |

    More info on taskfreak's website

    5 |
    -------------------------------------------------------------------------------- /inc/views/admin/dashboard.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 |

    4 | 5 |
    6 |
    7 |

    TaskFreak! Wordpress Free Plugin version options['version']; ?>

    8 |

    9 | TaskFreak! website.', 'taskfreak' ), 'http://www.taskfreak.com'); ?> 10 |

    11 |
    12 |
    13 | options['page_id']) { 15 | ?> 16 | 17 |
      18 |
    • 19 | 22 |
    • 23 | 26 |
    • 27 |
    28 | 31 |

    [tfk_all] in one of your pages.','taskfreak'), 'edit.php?post_type=page'); ?>

    32 |

    creating projects', 'taskfreak'), $this->linkprj); ?>

    33 | 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 | 69 | 70 | 71 | 72 |
    tskusr[20]; ?>tskusr[30]; ?>tskusr[60]; ?>
    tskall[20]; ?>tskall[30]; ?>tskall[60]; ?>
    prjusr[20]; ?>prjusr[30]; ?>prjusr[60]; ?>
    prjall[20]; ?>prjall[30]; ?>prjall[60]; ?>
    73 |
    74 |
    75 |

    76 | making a donation if you enjoy using TFWP plugin.', 'taskfreak'), 'http://www.taskfreak.com'); ?> 77 |

    78 |
    79 |
    80 | 81 |
    -------------------------------------------------------------------------------- /inc/views/admin/project_edit.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 |

    4 | pid)?__('Edit Project','taskfreak'):__('New Project','taskfreak'); ?> 5 |

    6 | saveok) { 8 | ?> 9 |

    10 | saverror) { 12 | ?> 13 |

    14 | 17 |
    18 | 19 | 22 |
    23 | 24 |
    29 |
    30 |
    31 |
    32 | 33 | 34 |
    35 |
    36 |
    37 | data->value('description'), 'description', array( 39 | 'media_buttons'=> false, // no media button 40 | 'wpautop' => false, // no

    41 | 'teeny' => true, // minimal editor 42 | 'dfw' => false, 43 | // 'tabfocus_elements' => 'sample-permalink,post-preview', 44 | 'editor_height' => 360 45 | )); 46 | ?> 47 |

    48 |
    49 | 50 |
    51 |
    52 |

    53 |

    54 |
    55 |
    56 |
    57 | data->get_status(); 59 | $statuskey = $objstatus->get('action_code'); 60 | ?> 61 | 62 | get_status(), 'one project', 'taskfreak'); 64 | ?> 65 | 66 |
    67 | 68 | 71 | 72 | 73 |
    74 |
    75 |
    76 | 77 | data->get_visibility(); 79 | ?> 80 | 81 |
    82 |
      83 |
    • 84 |
      85 | 89 |
    • 90 |
    • 91 |
      92 | 93 |
    • 94 |
    • 95 |
      96 | 97 |
    • 98 |
    • 99 |
      100 | 101 |
    • 102 |
    103 | 104 | 105 |
    106 |
    107 |
    108 |
    109 |
    110 | 113 |
    114 |
    115 | 116 | 117 |
    118 |
    119 |
    120 |
    121 |
    122 |
    123 | pid) { 128 | ?> 129 | 130 |
    131 |

    132 |

    133 |
    134 | status->count()) { 136 | while ($this->status->next()) { 137 | echo '

    '._x($this->status->get_status(), 'one project', 'taskfreak').'
    ' 138 | .$this->status->html('log_date') 139 | .' '.__(' by ', 'taskfreak').' '.$this->status->get('user')->get('display_name').'

    '; 140 | } 141 | } else { 142 | ?> 143 |

    144 | 145 |

    146 | 149 |
    150 |
    151 | 156 |
    157 | 158 |
    159 | 160 |
    161 |
    162 |
    -------------------------------------------------------------------------------- /inc/views/admin/project_list.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |

    5 | is_manager) { 8 | ?> 9 | 10 | 11 | 12 | 15 |

    16 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | data->count()) { 78 | 79 | while ($this->data->next()) { 80 | 81 | $pid = $this->data->get_uid(); 82 | 83 | $lnkfrt = add_query_arg(array( 84 | 'mode' => 'projects', 85 | 'proj' => $pid 86 | ), $this->linkfront); 87 | 88 | $lnkadm = add_query_arg(array( 89 | 'id' => $pid, 90 | 'noheader' => 'true' 91 | ), $this->linkadmin); 92 | 93 | echo ''; 94 | echo ''; 95 | echo ''; 124 | echo ''; 125 | echo ''; 126 | } 127 | } else { 128 | echo ''; 129 | } 130 | ?> 131 | 132 |
    ID
    ID
    '.$pid.''; 96 | if ($this->is_manager) { 97 | // editor and admin can edit and delete projects 98 | if ($this->filter == 'trash') { 99 | // project is in trash 100 | echo ''.$this->data->get('name').''; 101 | echo ''; 107 | } else { 108 | // project is alive 109 | echo ''.$this->data->get('name').''; 110 | echo ''; 114 | } 115 | } else { 116 | // contributors can list and view projects they are associated with 117 | echo ''.$this->data->get('name').''; 118 | echo ''; 122 | } 123 | echo ''._x($this->data->get('project_status')->get_status(), 'one project', 'taskfreak').'
    No project found
    133 | data->pagination_wp_links('admin.php?page=taskfreak_projects'); 135 | ?> 136 |
    -------------------------------------------------------------------------------- /inc/views/admin/settings.php: -------------------------------------------------------------------------------- 1 |
    2 | 3 |

    4 | message) { 6 | ?> 7 |

    message; ?>

    8 | 11 |
    12 | 15 |

    16 | 17 | 18 | 19 | 27 | 28 |
    20 |
    25 | 26 |
    29 |

    30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 52 | 53 | 54 | 55 | 61 | 62 | 63 | 64 | 71 | 72 | 73 | 74 | 81 | 82 | 83 | 84 | 99 | 100 | 101 | 102 | 109 | 110 |
    34 | 41 |
    46 | 51 |
    56 | 60 |
    65 | options['proximity'], 1); ?> /> 66 | 67 |   68 | options['proximity'], 0); ?> /> 69 | 70 |
    75 | options['prio_size'], 1); ?> /> 76 | 77 |   78 | options['prio_size'], 0); ?> /> 79 | 80 |
    85 | 97 | 98 |
    103 | 108 |
    111 |

    112 | 113 | 114 | 115 | 119 | 120 | 121 | 122 | 125 | 126 | 127 | 128 | 131 | 132 | 133 | 134 | 137 | 138 |
    139 |

    140 | 141 | 142 | 143 | 150 | 151 |
    144 | options['comment_upload'], 1); ?> /> 145 | 146 |   147 | options['comment_upload'], 0); ?> /> 148 | 149 |
    152 | 153 |

    154 |
    155 |
    156 | -------------------------------------------------------------------------------- /inc/views/front/edit.php: -------------------------------------------------------------------------------- 1 | data->errors['global_errors'] ?> 2 | data->value('item_id') ?> 3 |
    4 | 5 | 6 |
    7 | 8 | 9 |

    pid ? __('Edit Task', 'taskfreak') : __('New Task', 'taskfreak') ?>

    10 | 11 |
      12 |
    1. 13 | 14 | 15 | data->errors['title'] ?> 16 |
    2. 17 |
    3. 18 | 19 | projects->count()): ?> 20 | 30 | 31 | 32 | 33 | data->errors['project'] ?> 34 |
    4. 35 |
    5. 36 | 37 |
       
      38 | 43 | data->errors['priority'] ?> 44 |
    6. 45 |
    7. 46 | 47 | 49 | <?php _e('Calendar', 'taskfreak') ?> 55 | 56 | data->errors['deadline_date'] ?> 57 |
    8. 58 |
    9. 59 | 60 | 66 | data->errors['user_id'] ?> 67 |
    10. 68 |
    11. 69 | 70 | 80 | data->errors['status'] ?> 81 |
    12. 82 |
    83 | 84 | data->value('description'), 'description', array( 86 | 'media_buttons'=> false, // no media button 87 | 'wpautop' => false, // no

    88 | 'teeny' => true, // minimal editor 89 | 'dfw' => false, 90 | // 'tabfocus_elements' => 'sample-permalink,post-preview', 91 | 'editor_height' => 360 92 | )); 93 | ?> 94 | data->errors['description'] ?> 95 |

    96 |
      97 |
    • 98 |
    • 99 |
    • 100 |
    • 101 |
    • 102 |
    103 | data->errors['file'] ?> 104 | 105 | file->count()): 106 | $wp_upload_dir = wp_upload_dir(); ?> 107 |

    108 |
      109 | file->next()) : ?> 110 |
    1. 111 | 113 | file->get('file_title') ?> 114 | 115 |
    2. 116 | 117 |
    118 | 119 |
    120 | 121 | 122 |
    -------------------------------------------------------------------------------- /inc/views/front/list.php: -------------------------------------------------------------------------------- 1 | user_can_post): ?> 2 | 3 | 4 | 7 | data->count()) { 9 | ?> 10 |
    11 | data->total().' '._n('task', 'tasks', $this->data->total(), 'taskfreak') ?> 12 |
    13 |
    14 | 15 |
      16 | 25 | > 27 | 28 | 29 | 30 |
    31 |
    32 |
      33 | data->next()): 35 | $iid = $this->data->get_uid(); 36 | $user_id = $this->data->get('user_id'); 37 | $user = get_userdata($user_id); 38 | $last_mod_user = get_userdata($this->data->get('item_status_user_id')); 39 | $deadline_date = $this->data->html('deadline_date'); 40 | ?> 41 |
    1. data->get('deadline_date') < date("Y-m-d H:i:s", time()+7*24*60*60)) { // TODO 7 days = urgent => setting ? 47 | echo "tfk_urg"; 48 | } 49 | } else { 50 | echo "tfk_none"; 51 | } 52 | ?>"> 53 |
        54 |
      • 58 | options['avatar_size']); ?> 59 |
      • 60 |
      • 61 |
          62 | data->get('item_status')->get('action_code') == 60 ? _x('Closed', 'one task', 'taskfreak') : $this->data->get_deadline_proximity(); 64 | if ($this->options['proximity']): 65 | ?> 66 |
        • 67 | 68 |
        • 69 | 70 |
        • "> 71 | 73 | data->html('title'); ?> 74 | 75 |
        • 76 |
        • data->get('project')->html('name'); ?>
        • 77 |
        • data->get('priority'); ?>
        • 78 |
        • display_name : __('[deleted user]', 'taskfreak')) : __('Unassigned', 'taskfreak') ; ?>
        • 79 |
        • data->get('item_status_info') == 'creation') { 81 | echo __('Add.: ', 'taskfreak') 82 | .$this->data->html('creation_date'); 83 | } else { 84 | echo _e('Mod.: ', 'taskfreak') 85 | .$this->data->html('item_status_date'); 86 | } 87 | echo __(' by ', 'taskfreak') .($last_mod_user ? $last_mod_user->display_name : __('[deleted user]', 'taskfreak')); ?>
        • 88 |
        89 |
      • 90 |
      • 91 | 110 |
      • 111 |
      • 112 | 140 |
      • 141 |
      142 |
    2. 143 | 146 |
    147 | 148 |
    149 | 150 |
      151 | npages ; $p++): ?> 152 | page == $p ? ' class="tfk_current_page"' : '' ?>> 153 | 154 | 155 | 156 |
    157 |
    158 | 159 |
    160 | 161 |
      162 | 163 | page_size == $npg ? ' class="tfk_selected_page_size"' : '' ?>> 164 | 165 | 166 | 167 |
    168 |
    169 | 170 | 173 |

    174 | 175 |

    176 | 179 | -------------------------------------------------------------------------------- /inc/views/front/nav.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /inc/views/front/project_list.php: -------------------------------------------------------------------------------- 1 | view_inc('front/nav.php'); 3 | ?> 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 | 23 | data->count()) { 25 | while ($this->data->next()) { 26 | $pid = $this->data->get_uid(); 27 | echo ''; 28 | echo ''; 29 | echo ''; 32 | echo ''; 33 | echo ''; 34 | } 35 | } else { 36 | echo ''; 37 | } 38 | ?> 39 | 40 |
    '.$pid.''; 30 | echo ''.$this->data->get('name').''; 31 | echo ''._x($this->data->get('project_status')->get_status(), 'one project', 'taskfreak').'
    '.__('No project found', 'taskfreak').'
    41 | -------------------------------------------------------------------------------- /inc/views/front/project_view.php: -------------------------------------------------------------------------------- 1 | view_inc('front/nav.php'); 3 | ?> 4 | 8 | project->has('description')) { 10 | echo '
    '.$this->project->html('description').'
    '; // already all in HTML, with

    11 | } 12 | 13 | // list tasks (with filter links) 14 | $this->view_inc('front/list.php'); 15 | -------------------------------------------------------------------------------- /inc/views/front/recent.php: -------------------------------------------------------------------------------- 1 | view_inc('front/nav.php'); 3 | ?> 4 |

    7 |
    8 | 9 | log->count()): ?> 10 | log->next()): ?> 11 | log->get('log_date'),0,10)): ?> 12 | 13 | 14 | 15 | log->get('log_date'),0,10) ?> 16 |

    17 |
      18 | 19 |
    • log->get('info') == 'creation') { 23 | echo "cre"; 24 | } elseif ($this->log->get('action_code') != '') { 25 | echo "set"; 26 | } else { 27 | echo "mod"; 28 | } 29 | ?>"> 30 | log->get('user')->get('display_name') ? $this->log->get('user')->get('display_name') : __('[deleted user]', 'taskfreak')).' ' ?> 31 | 32 | log->get('type') == "task") { 34 | $task_link = esc_url(remove_query_arg('mode', add_query_arg('view', $this->log->get('item_id'), $this->baselink))); 35 | if ($this->log->get('comment_id')) 36 | echo __('commented task', 'taskfreak').' '.$this->log->get('title_or_name').''; 37 | elseif ($this->log->get('info') == 'creation') 38 | echo __('created task', 'taskfreak').' '.$this->log->get('title_or_name').''; 39 | elseif ($this->log->get('action_code') != '') 40 | echo __('set task', 'taskfreak').' '.$this->log->get('title_or_name').' '.__('to', 'taskfreak').' '._x($this->log->get_status(), 'one task', 'taskfreak'); 41 | else 42 | echo __('modified task', 'taskfreak').' '.$this->log->get('title_or_name').' ('.$this->log->get_info().')'; 43 | } else { 44 | $project_link = esc_url(remove_query_arg('mode', add_query_arg('proj', $this->log->get('project_id'), $this->baselink))); 45 | if ($this->log->get('info') == 'creation') 46 | echo __('created project', 'taskfreak').' '.$this->log->get('title_or_name').''; 47 | elseif ($this->log->get('action_code') != '') 48 | echo __('set project', 'taskfreak').' '.$this->log->get('title_or_name').' '.__('to', 'taskfreak').' '._x($this->log->get_status(), 'one project', 'taskfreak'); 49 | else 50 | echo __('modified project', 'taskfreak').' '.$this->log->get('title_or_name').''; 51 | } 52 | ?> 53 |
    • 54 | 55 | 56 | 57 | 58 |
    -------------------------------------------------------------------------------- /inc/views/front/status.php: -------------------------------------------------------------------------------- 1 | 2 |

    3 | message, 0, 2) == 'OK') { 5 | _e('OK ! Task has been set to ', 'taskfreak'); 6 | } else { 7 | _e('Sorry, an error has occured.', 'taskfreak'); 8 | } 9 | echo ' '.substr($this->message, 4).'.'; 10 | ?> 11 |

    12 | -------------------------------------------------------------------------------- /inc/views/front/task.php: -------------------------------------------------------------------------------- 1 | data->get_uid(); 3 | $assignee_id = $this->data->get('user_id'); 4 | $assignee = get_userdata($assignee_id); 5 | $author_id = $this->data->get('author_id'); 6 | $author = get_userdata($this->data->get('author_id')); 7 | $last_mod_user = get_userdata($this->data->get('item_status_user_id')); 8 | $status_action_code = $this->data->get_status()->get('action_code'); 9 | ?> 10 | 11 |

    12 | 13 | data->get('title') ?> 14 | 15 |

    16 |

    data->get('project')->html('name') ?>

    17 | comment->errors['global_errors'] ?> 18 |
    19 | user_can_edit_task): ?> 20 | 21 | 22 | 23 | 24 | user_can_comment_project): ?> 25 | 26 | 27 |
    28 | 31 | options['avatar_size']); ?> 32 | 33 |

    display_name : __('[deleted user]', 'taskfreak')) ?>

    34 |

    data->html('creation_date') ?>

    35 |
    36 |

    data->get('priority') ?>

    37 |

    data->html('deadline_date') ? $this->data->html('deadline_date') : '—' ?>

    38 |

    display_name : __('[deleted user]', 'taskfreak')) : __('Unassigned', 'taskfreak'); ?>

    39 |

    40 | 41 | 42 | 46 | 47 | 51 | 52 | 56 | 57 | 58 | data->get_status()->get_status(), 'one task', 'taskfreak') ?> 59 |

    60 |

    61 | 65 |

    66 |
    67 |
    68 |
    data->get('description') ?>
    69 | file->count()): 70 | $wp_upload_dir = wp_upload_dir(); ?> 71 |

    72 |
      73 | file->next()): ?> 74 |
    1. 75 | 76 | file->get('file_title') ?> 77 | 78 |
    2. 79 | 80 |
    81 | 82 |

    comment->count().' '._n('comment', 'comments', $this->comment->count() > 1 ? 2 : 1, 'taskfreak') ?>

    83 | 102 | user_can_comment_project): ?> 103 |
    104 | 105 |
    106 |

    107 | false, // no media button 110 | 'wpautop' => false, // no

    111 | 'teeny' => true, // minimal editor 112 | 'dfw' => false, 113 | // 'tabfocus_elements' => 'sample-permalink,post-preview', 114 | 'editor_height' => 360 115 | )); 116 | ?> 117 | comment->errors['body'] ?> 118 |

    119 | options['comment_upload']): ?> 120 |
    121 |

    122 |
      123 |
    • 124 |
    • 125 |
    • 126 |
    127 | comment->errors['file'] ?> 128 |
    129 | 130 | 131 |
    132 | 133 | -------------------------------------------------------------------------------- /inc/views/front/task_history.php: -------------------------------------------------------------------------------- 1 | data->get_uid(); 3 | ?> 4 | 5 |

    6 | 7 | data->get('title') ?> 8 | 9 |

    10 |

    data->get('project')->html('name'); ?>

    11 | 12 |

    13 | 14 | log->count()): ?> 15 | 16 | 17 | 18 | 19 | 20 | 21 | log->next()): ?> 22 | 23 | 24 | 27 | 35 | 36 | 37 |
    log->html('log_date') ?> 25 | log->get('user')->get('display_name') ? $this->log->get('user')->get('display_name') : __('[deleted user]', 'taskfreak') ?> 26 | 28 | log->get('action_code') != '') 30 | echo _x($this->log->get_status(), 'one task', 'taskfreak'); 31 | else 32 | echo __('Modified', 'taskfreak').' ('.$this->log->get_info().')'; 33 | ?> 34 |
    38 | 39 | -------------------------------------------------------------------------------- /inc/views/front/task_list.php: -------------------------------------------------------------------------------- 1 | view_inc('front/nav.php'); 3 | $this->view_inc('front/list.php'); 4 | ?> 5 | 6 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | '); 4 | jQuery('#tfk_page_size a').each(function() { 5 | var selected = jQuery(this).parent().attr('class') && jQuery(this).parent().attr('class').match('tfk_selected_page_size'); 6 | jQuery('#tfk_page_size_js').append(''); 10 | }); 11 | jQuery('#tfk_page_size_js').bind('change', function () { 12 | var target = jQuery(this).val(); 13 | jQuery.ajax({ 14 | url: tznfrontjs_vars.plugins_url + '/taskfreak/ajax-npage.php?target=' + encodeURIComponent(target), 15 | }).done(function(data) { 16 | window.location = target; 17 | }); 18 | }); 19 | // list sort criteria 20 | jQuery('#tfk_sort_criteria ul').hide().after(''); 161 | $file_input_2.change(tfk_file_input_reset); 162 | $file_input.after($file_input_2).remove(); 163 | return false; 164 | }); 165 | } 166 | 167 | function tfk_update_select_prio_color() { 168 | jQuery('#tfk_select_prio_color').removeClass().addClass('tfk_pr' + jQuery('#tfk_select_prio option:selected').val()); 169 | } 170 | -------------------------------------------------------------------------------- /languages/taskfreak-ar.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/languages/taskfreak-ar.mo -------------------------------------------------------------------------------- /languages/taskfreak-de_DE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/languages/taskfreak-de_DE.mo -------------------------------------------------------------------------------- /languages/taskfreak-es_ES.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/languages/taskfreak-es_ES.mo -------------------------------------------------------------------------------- /languages/taskfreak-fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/languages/taskfreak-fr_FR.mo -------------------------------------------------------------------------------- /languages/taskfreak-it_IT.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/languages/taskfreak-it_IT.mo -------------------------------------------------------------------------------- /languages/taskfreak-pl_PL.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/languages/taskfreak-pl_PL.mo -------------------------------------------------------------------------------- /languages/taskfreak-pt_BR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/languages/taskfreak-pt_BR.mo -------------------------------------------------------------------------------- /languages/taskfreak-ru_RU.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/languages/taskfreak-ru_RU.mo -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === [UNMAINTAINED] TaskFreak! Free === 2 | Contributors: taskfreak, HerveRenault 3 | Donate link: http://www.taskfreak.com/ 4 | Tags: projects, tasks, management, todo, list, GTD, manage projects, project management, task management, team, planning, tracking 5 | Requires at least: 3.3 6 | Tested up to: 4.2.1 7 | Stable tag: 1.0.19 8 | Version: 1.0.19 9 | License: GPLv2 or later 10 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 11 | 12 | Sorry, this plugin is no longer maintained. 13 | 14 | == Description == 15 | 16 | ** THIS PLUGIN IS NO LONGER MAINTAINED ** 17 | 18 | Developers can fork it on GitHub https://github.com/taskfreak/tfwp/ 19 | 20 | Manage tasks for yourself or within a group, company, organization, etc. 21 | Create projects or teams, then add tasks and assign to users. 22 | Add attachements, discuss tasks and follow their status. 23 | 24 | WordPress version of the standalone [TaskFreak!](http://taskfreak.com/) web application. 25 | 26 | Available in Arabic, English, French, German, Italian, Polish, Portuguese, Russian, Spanish. 27 | (thanks to contributors) 28 | 29 | Advantages : 30 | 31 | - Easy to install 32 | - Full integration with Wordpress users and roles 33 | - Create public and private projects 34 | - Add attachments and comment your tasks 35 | - Mobile devices friendly (smartphones, tablets) 36 | - Integrates seamlessly with your WP theme 37 | - Users are associated to projects by WP roles 38 | 39 | [vimeo http://vimeo.com/77401609] 40 | 41 | == Installation == 42 | 43 | ** THIS PLUGIN IS NO LONGER MAINTAINED ** 44 | 45 | Developers can fork it on GitHub https://github.com/taskfreak/tfwp/ 46 | 47 | 1. Upload `taskfreak.zip` to the `/wp-content/plugins/` directory 48 | 2. Unzip taskfreak (should create taskfreak/ folder within plugins/) 49 | 3. Activate the plugin through the 'Plugins' menu in WordPress 50 | 4. Insert shortcode `[tfk_all]` in any page or post (or insert `` in your template) 51 | 5. Create at least one project through the 'TaskFreak!' menu in WordPress dashboard. 52 | 53 | Note: if your theme inserts unwanted line breaks in TaskFreak's output, then use this shortcode combination: `[raw][tfk_all][/raw]` 54 | 55 | == Frequently Asked Questions == 56 | 57 | ** THIS PLUGIN IS NO LONGER MAINTAINED ** 58 | 59 | Developers can fork it on GitHub https://github.com/taskfreak/tfwp/ 60 | 61 | = Where can I get some help ? = 62 | 63 | Go to http://www.taskfreak.com/wordpress-plugin/ for more information. 64 | 65 | = Can I get professionnal support = 66 | 67 | If you want personnal support or have specific requests, please go to 68 | http://www.taskfreak.com and see details about the pro version 69 | 70 | == Screenshots == 71 | 72 | ** THIS PLUGIN IS NO LONGER MAINTAINED ** 73 | 74 | Developers can fork it on GitHub https://github.com/taskfreak/tfwp/ 75 | 76 | 1. After installing the plugin, go to the 'TaskFreak!' menu 77 | 2. Create your first project 78 | 3. Set project title, description (optional), status (or leave in 'Draft'), access rights 79 | 4. Insert the `[tfk_all]` shortcode in a page or post 80 | 5. View your page (or post) 81 | 6. Your project contains no task, time to create one: click 'New Task' 82 | 7. Enter title, set priority (1 to 3), deadline (optional), assign the task (optional), set status, ... 83 | 8. Here's what your task list looks like if you enable the 'Proximity bar' in TaskFreak! settings 84 | 9. View updates (depends on access rights for the viewer) 85 | 10. Settings 86 | 87 | == Changelog == 88 | 89 | ** THIS PLUGIN IS NO LONGER MAINTAINED ** 90 | 91 | Developers can fork it on GitHub https://github.com/taskfreak/tfwp/ 92 | 93 | = 1.0.18 = 94 | Security fix (XSS) 95 | See http://arstechnica.com/security/2015/04/21/swarm-of-wordpress-plugins-susceptible-to-potentially-dangerous-exploits/ 96 | 97 | = 1.0.17 = 98 | Improved way of retrieving status via Ajax (no impact on users) 99 | Russian translation thanks to Алексей Моцык (Aleksei Motsyk) 100 | 101 | = 1.0.16 = 102 | Added Arabic translation thanks to Ahmed Ghanem https://twitter.com/A91G 103 | 104 | = 1.0.15 = 105 | Added: Spanish translation thanks to Jose Miranda http://www.webboricua.com 106 | Bug fix: missing charset collation in install (thanks to ViseirA) 107 | Bug fix: strict standards notice with E_STRICT error reporting enabled (thanks to sirmacik) 108 | Bug fix: improper enqueuing prevented MinQueue plugin from concatenating styles. 109 | 110 | = 1.0.14 = 111 | Bug fix: URL of localized datepickers changed + remove http(s) and let the browser use the proper protocol. 112 | Thanks becki for advising us! 113 | 114 | = 1.0.13 = 115 | Bug fix: in the dashboard, the "Start working" link could contain unwanted parameters after changing task status. 116 | Documentation: Some themes insert unwanted line breaks. In this case, use this shortcode combination `[raw][tfk_all][/raw]` 117 | 118 | = 1.0.12 = 119 | Bug fix for multisite setup. 120 | 121 | = 1.0.11 = 122 | Replaced the "Show/Hide History" link by an icon for better compatibility with the 2014 theme. 123 | Force reload if it does not show up after the update. 124 | 125 | = 1.0.10 = 126 | Italian translation provided by Giorgio Draghetti 127 | 128 | = 1.0.9 = 129 | Polish (Poland) translation provided by tretos53@github (adam.g@outlook.com) 130 | 131 | = 1.0.8 = 132 | Portuguese (Brazil) translation provided by Theodoro Caliari 133 | 134 | = 1.0.7 = 135 | Fixed uninstall script. 136 | 137 | = 1.0.6 = 138 | German translation provided by Ian Diggance 139 | 140 | = 1.0.5 = 141 | Bugfix : Removed unused unprefixed dbg() function which clashes with WYSIJA as notified by users time4novelty and SeanBanksBliss (thank you) 142 | 143 | = 1.0.4 = 144 | Bugfix : Details section was a bit too wide, which triggered scrollbars on "overflow: auto" containers 145 | 146 | = 1.0.3 = 147 | Bugfix : Added missing french translation for "No project found" 148 | 149 | = 1.0.2 = 150 | Added link to video in the readme 151 | 152 | = 1.0.1 = 153 | Added screenshots 154 | 155 | = 1.0 = 156 | Initial version 157 | -------------------------------------------------------------------------------- /tests/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TF!WP automated tests output 5 | 6 | 14 | 15 | 16 |

    TF!WP automated tests output

    17 | 18 | 19 | '.PHP_EOL; 43 | } 44 | 45 | ?> 46 |
    TestResultReason'.$file.''.$result.''.$msg[1].'
    47 | 48 | 49 | -------------------------------------------------------------------------------- /tests/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | file="output/tfwp-test-`date +%F_%T`.txt" 4 | touch $file 5 | if [ "`ps | grep -c run-tests.sh`" != "2" ]; then 6 | echo "ABORTED (already running)" > $file 7 | else 8 | phpunit tfwp-test.php > $file 9 | fi 10 | -------------------------------------------------------------------------------- /tests/tfwp-test.mysql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.13 Distrib 5.1.61, for debian-linux-gnu (i486) 2 | -- 3 | -- Host: localhost Database: wordpress_tfwp_test 4 | -- ------------------------------------------------------ 5 | -- Server version 5.1.61-0ubuntu0.10.04.1-log 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `wp_tfk_item` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `wp_tfk_item`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `wp_tfk_item` ( 26 | `item_id` int(1) unsigned NOT NULL AUTO_INCREMENT, 27 | `project_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 28 | `item_parent_id` int(10) unsigned NOT NULL DEFAULT '0', 29 | `priority` tinyint(3) unsigned NOT NULL DEFAULT '0', 30 | `context` varchar(64) NOT NULL DEFAULT '0', 31 | `title` varchar(255) NOT NULL DEFAULT '', 32 | `description` text NOT NULL, 33 | `creation_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 34 | `start_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 35 | `deadline_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 36 | `completion_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 37 | `expected_duration` smallint(5) unsigned NOT NULL DEFAULT '0', 38 | `actual_duration` smallint(5) unsigned NOT NULL DEFAULT '0', 39 | `show_in_calendar` tinyint(1) unsigned NOT NULL DEFAULT '0', 40 | `show_private` tinyint(1) unsigned NOT NULL DEFAULT '0', 41 | `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 42 | `author_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 43 | `last_change_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 44 | `last_change_author_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 45 | PRIMARY KEY (`item_id`), 46 | KEY `project_id` (`project_id`), 47 | KEY `user_id` (`user_id`), 48 | KEY `author_id` (`author_id`) 49 | ) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=latin1; 50 | /*!40101 SET character_set_client = @saved_cs_client */; 51 | 52 | -- 53 | -- Dumping data for table `wp_tfk_item` 54 | -- 55 | 56 | LOCK TABLES `wp_tfk_item` WRITE; 57 | /*!40000 ALTER TABLE `wp_tfk_item` DISABLE KEYS */; 58 | INSERT INTO `wp_tfk_item` VALUES (1,1,0,1,'0','Any visitor can read this task','','2013-09-12 16:31:37','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00',0,0,0,0,0,1,'2013-09-13 08:42:59',0),(2,1,0,1,'0','Nobody but admin can read this draft','

    Nobody but admin can read this draft.

    ','2013-09-12 16:32:56','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00',0,0,0,0,0,1,'0000-00-00 00:00:00',0),(3,1,0,1,'0','Any visitor can read this closed task','

    Any visitor can read this closed task but only admin can edit it.

    ','2013-09-12 16:48:24','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00',0,0,0,0,0,1,'2013-09-13 08:00:37',0),(4,2,0,1,'0','Access to this task is restricted to subscribers and above','

    Access to this task is restricted to subscribers and above.

    ','2013-09-12 17:02:23','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00',0,0,0,0,0,1,'0000-00-00 00:00:00',0),(5,3,0,1,'0','Access to this task is restricted to contributors and above','

    Access to this task is restricted to contributors and above.

    ','2013-09-12 18:32:01','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00',0,0,0,0,0,1,'0000-00-00 00:00:00',0),(6,5,0,1,'0','Access to this task is restricted to authors and above','

    Access to this task is restricted to authors and above.

    ','2013-09-12 18:32:46','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00',0,0,0,0,0,1,'0000-00-00 00:00:00',0),(7,6,0,1,'0','Access to this task is restricted to editors and above','

    Access to this task is restricted to editors and above.

    ','2013-09-12 18:33:11','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00',0,0,0,0,0,1,'0000-00-00 00:00:00',0),(8,7,0,1,'0','Access to this task is restricted to admin','

    Access to this task is restricted to admin.

    ','2013-09-12 18:33:43','0000-00-00 00:00:00','0000-00-00 00:00:00','0000-00-00 00:00:00',0,0,0,0,0,1,'0000-00-00 00:00:00',0); 59 | /*!40000 ALTER TABLE `wp_tfk_item` ENABLE KEYS */; 60 | UNLOCK TABLES; 61 | 62 | -- 63 | -- Table structure for table `wp_tfk_item_comment` 64 | -- 65 | 66 | DROP TABLE IF EXISTS `wp_tfk_item_comment`; 67 | /*!40101 SET @saved_cs_client = @@character_set_client */; 68 | /*!40101 SET character_set_client = utf8 */; 69 | CREATE TABLE `wp_tfk_item_comment` ( 70 | `item_comment_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 71 | `item_id` int(10) unsigned NOT NULL DEFAULT '0', 72 | `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 73 | `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 74 | `body` text NOT NULL, 75 | `last_change_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 76 | PRIMARY KEY (`item_comment_id`), 77 | KEY `itemId` (`item_id`) 78 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1; 79 | /*!40101 SET character_set_client = @saved_cs_client */; 80 | 81 | -- 82 | -- Dumping data for table `wp_tfk_item_comment` 83 | -- 84 | 85 | LOCK TABLES `wp_tfk_item_comment` WRITE; 86 | /*!40000 ALTER TABLE `wp_tfk_item_comment` DISABLE KEYS */; 87 | /*!40000 ALTER TABLE `wp_tfk_item_comment` ENABLE KEYS */; 88 | UNLOCK TABLES; 89 | 90 | -- 91 | -- Table structure for table `wp_tfk_item_comment_like` 92 | -- 93 | 94 | DROP TABLE IF EXISTS `wp_tfk_item_comment_like`; 95 | /*!40101 SET @saved_cs_client = @@character_set_client */; 96 | /*!40101 SET character_set_client = utf8 */; 97 | CREATE TABLE `wp_tfk_item_comment_like` ( 98 | `item_comment_id` int(10) unsigned NOT NULL, 99 | `user_id` mediumint(8) unsigned NOT NULL, 100 | `post_date` datetime NOT NULL, 101 | `vote` tinyint(1) unsigned NOT NULL, 102 | PRIMARY KEY (`item_comment_id`,`user_id`) 103 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1; 104 | /*!40101 SET character_set_client = @saved_cs_client */; 105 | 106 | -- 107 | -- Dumping data for table `wp_tfk_item_comment_like` 108 | -- 109 | 110 | LOCK TABLES `wp_tfk_item_comment_like` WRITE; 111 | /*!40000 ALTER TABLE `wp_tfk_item_comment_like` DISABLE KEYS */; 112 | /*!40000 ALTER TABLE `wp_tfk_item_comment_like` ENABLE KEYS */; 113 | UNLOCK TABLES; 114 | 115 | -- 116 | -- Table structure for table `wp_tfk_item_file` 117 | -- 118 | 119 | DROP TABLE IF EXISTS `wp_tfk_item_file`; 120 | /*!40101 SET @saved_cs_client = @@character_set_client */; 121 | /*!40101 SET character_set_client = utf8 */; 122 | CREATE TABLE `wp_tfk_item_file` ( 123 | `item_file_id` bigint(20) unsigned NOT NULL AUTO_INCREMENT, 124 | `item_id` int(10) unsigned NOT NULL DEFAULT '0', 125 | `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 126 | `file_title` varchar(200) NOT NULL DEFAULT '', 127 | `file_name` varchar(127) NOT NULL DEFAULT '', 128 | `file_type` varchar(30) NOT NULL DEFAULT '', 129 | `file_size` bigint(20) NOT NULL DEFAULT '0', 130 | `post_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 131 | `last_change_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 132 | `file_tags` varchar(255) NOT NULL DEFAULT '', 133 | PRIMARY KEY (`item_file_id`), 134 | KEY `item_id` (`item_id`) 135 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1; 136 | /*!40101 SET character_set_client = @saved_cs_client */; 137 | 138 | -- 139 | -- Dumping data for table `wp_tfk_item_file` 140 | -- 141 | 142 | LOCK TABLES `wp_tfk_item_file` WRITE; 143 | /*!40000 ALTER TABLE `wp_tfk_item_file` DISABLE KEYS */; 144 | /*!40000 ALTER TABLE `wp_tfk_item_file` ENABLE KEYS */; 145 | UNLOCK TABLES; 146 | 147 | -- 148 | -- Table structure for table `wp_tfk_item_like` 149 | -- 150 | 151 | DROP TABLE IF EXISTS `wp_tfk_item_like`; 152 | /*!40101 SET @saved_cs_client = @@character_set_client */; 153 | /*!40101 SET character_set_client = utf8 */; 154 | CREATE TABLE `wp_tfk_item_like` ( 155 | `item_id` mediumint(8) unsigned NOT NULL, 156 | `user_id` mediumint(8) unsigned NOT NULL, 157 | `post_date` datetime NOT NULL, 158 | PRIMARY KEY (`item_id`,`user_id`) 159 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1; 160 | /*!40101 SET character_set_client = @saved_cs_client */; 161 | 162 | -- 163 | -- Dumping data for table `wp_tfk_item_like` 164 | -- 165 | 166 | LOCK TABLES `wp_tfk_item_like` WRITE; 167 | /*!40000 ALTER TABLE `wp_tfk_item_like` DISABLE KEYS */; 168 | /*!40000 ALTER TABLE `wp_tfk_item_like` ENABLE KEYS */; 169 | UNLOCK TABLES; 170 | 171 | -- 172 | -- Table structure for table `wp_tfk_item_status` 173 | -- 174 | 175 | DROP TABLE IF EXISTS `wp_tfk_item_status`; 176 | /*!40101 SET @saved_cs_client = @@character_set_client */; 177 | /*!40101 SET character_set_client = utf8 */; 178 | CREATE TABLE `wp_tfk_item_status` ( 179 | `item_id` int(10) unsigned NOT NULL DEFAULT '0', 180 | `status_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 181 | `status_key` tinyint(3) unsigned NOT NULL DEFAULT '0', 182 | `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 183 | PRIMARY KEY (`item_id`,`status_date`), 184 | KEY `itemId` (`user_id`) 185 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1; 186 | /*!40101 SET character_set_client = @saved_cs_client */; 187 | 188 | -- 189 | -- Dumping data for table `wp_tfk_item_status` 190 | -- 191 | 192 | LOCK TABLES `wp_tfk_item_status` WRITE; 193 | /*!40000 ALTER TABLE `wp_tfk_item_status` DISABLE KEYS */; 194 | /*!40000 ALTER TABLE `wp_tfk_item_status` ENABLE KEYS */; 195 | UNLOCK TABLES; 196 | 197 | -- 198 | -- Table structure for table `wp_tfk_log` 199 | -- 200 | 201 | DROP TABLE IF EXISTS `wp_tfk_log`; 202 | /*!40101 SET @saved_cs_client = @@character_set_client */; 203 | /*!40101 SET character_set_client = utf8 */; 204 | CREATE TABLE `wp_tfk_log` ( 205 | `log_date` datetime NOT NULL, 206 | `user_id` mediumint(8) unsigned NOT NULL, 207 | `action_code` varchar(3) NOT NULL, 208 | `item_id` int(10) unsigned NOT NULL DEFAULT '0', 209 | `project_id` mediumint(8) unsigned NOT NULL, 210 | `comment_id` int(10) unsigned NOT NULL DEFAULT '0', 211 | `info` varchar(255) NOT NULL DEFAULT '', 212 | `hidden` tinyint(1) unsigned NOT NULL DEFAULT '0', 213 | PRIMARY KEY (`log_date`,`user_id`,`action_code`), 214 | KEY `project_id` (`project_id`), 215 | KEY `item_id` (`item_id`) 216 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1; 217 | /*!40101 SET character_set_client = @saved_cs_client */; 218 | 219 | -- 220 | -- Dumping data for table `wp_tfk_log` 221 | -- 222 | 223 | LOCK TABLES `wp_tfk_log` WRITE; 224 | /*!40000 ALTER TABLE `wp_tfk_log` DISABLE KEYS */; 225 | INSERT INTO `wp_tfk_log` VALUES ('2013-09-12 09:41:50',1,'20',0,1,0,'creation',0),('2013-09-12 09:45:40',1,'20',0,2,0,'creation',0),('2013-09-12 10:08:41',1,'20',0,3,0,'creation',0),('2013-09-12 11:36:02',1,'20',0,4,0,'creation',0),('2013-09-12 11:36:52',1,'20',0,5,0,'creation',0),('2013-09-12 11:37:25',1,'20',0,6,0,'creation',0),('2013-09-12 11:43:42',1,'20',0,7,0,'creation',0),('2013-09-12 16:31:37',1,'20',1,0,0,'creation',0),('2013-09-12 16:32:56',1,'0',2,0,0,'creation',0),('2013-09-12 16:48:24',1,'60',3,0,0,'creation',0),('2013-09-12 17:02:23',1,'20',4,0,0,'creation',0),('2013-09-12 18:32:01',1,'20',5,0,0,'creation',0),('2013-09-12 18:32:46',1,'20',6,0,0,'creation',0),('2013-09-12 18:33:11',1,'20',7,0,0,'creation',0),('2013-09-12 18:33:43',1,'20',8,0,0,'creation',0),('2013-09-13 08:00:07',5,'',1,0,0,'title',0),('2013-09-13 08:00:37',5,'',3,0,0,'title',0); 226 | /*!40000 ALTER TABLE `wp_tfk_log` ENABLE KEYS */; 227 | UNLOCK TABLES; 228 | 229 | -- 230 | -- Table structure for table `wp_tfk_project` 231 | -- 232 | 233 | DROP TABLE IF EXISTS `wp_tfk_project`; 234 | /*!40101 SET @saved_cs_client = @@character_set_client */; 235 | /*!40101 SET character_set_client = utf8 */; 236 | CREATE TABLE `wp_tfk_project` ( 237 | `project_id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT, 238 | `name` varchar(120) NOT NULL DEFAULT '', 239 | `description` text NOT NULL, 240 | `who_read` varchar(64) NOT NULL, 241 | `who_comment` varchar(64) NOT NULL, 242 | `who_post` varchar(64) NOT NULL, 243 | `who_manage` varchar(64) NOT NULL, 244 | `creation_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 245 | `trashed` tinyint(1) unsigned NOT NULL DEFAULT '0', 246 | PRIMARY KEY (`project_id`) 247 | ) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=latin1; 248 | /*!40101 SET character_set_client = @saved_cs_client */; 249 | 250 | -- 251 | -- Dumping data for table `wp_tfk_project` 252 | -- 253 | 254 | LOCK TABLES `wp_tfk_project` WRITE; 255 | /*!40000 ALTER TABLE `wp_tfk_project` DISABLE KEYS */; 256 | INSERT INTO `wp_tfk_project` VALUES (1,'Any visitor has access to this project','

    Any visitor has access to this project.

    \r\n

    Subscribers can comment tasks.

    \r\n

    Authors can create tasks.

    \r\n

    Editors can manage the project (and as such, can see drafts !)

    ','','subscriber','author','editor','2013-09-12 09:41:50',0),(2,'Project access level : subscribers and above','

    Project access level : subscribers and above

    ','subscriber','contributor','author','editor','2013-09-12 09:45:40',0),(3,'Project access level : contributors and above','

    Project access level : contributors and above

    ','contributor','author','editor','administrator','2013-09-12 10:08:41',0),(4,'Project access level : contributors and above','

    Project access level : contributors and above

    ','contributor','author','editor','administrator','2013-09-12 11:36:02',1),(5,'Project access level : authors and above','

    Project access level : authors and above

    ','author','editor','administrator','administrator','2013-09-12 11:36:52',0),(6,'Project access level : editors and above','

    Project access level : editors and above

    ','editor','administrator','administrator','administrator','2013-09-12 11:37:25',0),(7,'Project access level : admin','

    Project access level : admin

    ','administrator','administrator','administrator','administrator','2013-09-12 11:43:42',0); 257 | /*!40000 ALTER TABLE `wp_tfk_project` ENABLE KEYS */; 258 | UNLOCK TABLES; 259 | 260 | -- 261 | -- Table structure for table `wp_tfk_project_status` 262 | -- 263 | 264 | DROP TABLE IF EXISTS `wp_tfk_project_status`; 265 | /*!40101 SET @saved_cs_client = @@character_set_client */; 266 | /*!40101 SET character_set_client = utf8 */; 267 | CREATE TABLE `wp_tfk_project_status` ( 268 | `project_id` mediumint(10) unsigned NOT NULL DEFAULT '0', 269 | `status_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 270 | `status_key` tinyint(3) unsigned NOT NULL DEFAULT '0', 271 | `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 272 | PRIMARY KEY (`project_id`,`status_date`) 273 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1; 274 | /*!40101 SET character_set_client = @saved_cs_client */; 275 | 276 | -- 277 | -- Dumping data for table `wp_tfk_project_status` 278 | -- 279 | 280 | LOCK TABLES `wp_tfk_project_status` WRITE; 281 | /*!40000 ALTER TABLE `wp_tfk_project_status` DISABLE KEYS */; 282 | /*!40000 ALTER TABLE `wp_tfk_project_status` ENABLE KEYS */; 283 | UNLOCK TABLES; 284 | 285 | -- 286 | -- Table structure for table `wp_tfk_project_user` 287 | -- 288 | 289 | DROP TABLE IF EXISTS `wp_tfk_project_user`; 290 | /*!40101 SET @saved_cs_client = @@character_set_client */; 291 | /*!40101 SET character_set_client = utf8 */; 292 | CREATE TABLE `wp_tfk_project_user` ( 293 | `project_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 294 | `user_id` mediumint(8) unsigned NOT NULL DEFAULT '0', 295 | `position` tinyint(2) unsigned NOT NULL DEFAULT '0', 296 | PRIMARY KEY (`project_id`,`user_id`) 297 | ) ENGINE=MyISAM DEFAULT CHARSET=latin1; 298 | /*!40101 SET character_set_client = @saved_cs_client */; 299 | 300 | -- 301 | -- Dumping data for table `wp_tfk_project_user` 302 | -- 303 | 304 | LOCK TABLES `wp_tfk_project_user` WRITE; 305 | /*!40000 ALTER TABLE `wp_tfk_project_user` DISABLE KEYS */; 306 | /*!40000 ALTER TABLE `wp_tfk_project_user` ENABLE KEYS */; 307 | UNLOCK TABLES; 308 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 309 | 310 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 311 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 312 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 313 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 314 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 315 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 316 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 317 | 318 | -- Dump completed on 2013-09-13 10:50:53 319 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | query("DROP TABLE IF EXISTS ".$wpdb->prefix.'tfk_'.$table_name); 32 | } --------------------------------------------------------------------------------