├── 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 | -------------------------------------------------------------------------------- /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 | 90 | -------------------------------------------------------------------------------- /img/bubble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taskfreak/tfwp/daff39432d02bff115ae84c37c2bfc45f35a13ee/img/bubble.png -------------------------------------------------------------------------------- /img/bubble.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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
'; 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 = '9 | TaskFreak! website.', 'taskfreak' ), 'http://www.taskfreak.com'); ?> 10 |
11 |[tfk_all] in one of your pages.','taskfreak'), 'edit.php?post_type=page'); ?>
32 |creating projects', 'taskfreak'), $this->linkprj); ?>
33 | 36 |42 | | 43 | | 44 | | 45 | |
---|---|---|---|
49 | | tskusr[20]; ?> | 50 |tskusr[30]; ?> | 51 |tskusr[60]; ?> | 52 |
55 | | tskall[20]; ?> | 56 |tskall[30]; ?> | 57 |tskall[60]; ?> | 58 |
61 | | prjusr[20]; ?> | 62 |prjusr[30]; ?> | 63 |prjusr[60]; ?> | 64 |
67 | | prjall[20]; ?> | 68 |prjall[30]; ?> | 69 |prjall[60]; ?> | 70 |
76 | making a donation if you enjoy using TFWP plugin.', 'taskfreak'), 'http://www.taskfreak.com'); ?> 77 |
78 |ID | 64 |65 | | 66 | |
---|---|---|
ID | 71 |72 | | 73 | |
'.$pid.' | '; 95 | echo '';
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 ' '
102 | .''.__('Restore', 'taskfreak').' | '
104 | .''.__('Delete Permanently', 'taskfreak').' ';
107 | } else {
108 | // project is alive
109 | echo ''.$this->data->get('name').'';
110 | echo ''
111 | .''.__('Edit', 'taskfreak').' | '
112 | .''.__('View', 'taskfreak').''
113 | .' ';
114 | }
115 | } else {
116 | // contributors can list and view projects they are associated with
117 | echo ''.$this->data->get('name').'';
118 | echo ''
119 | // .''.__('Project details', 'taskfreak').' | '
120 | .''.__('View', 'taskfreak').''
121 | .' ';
122 | }
123 | echo ' | ';
124 | echo ''._x($this->data->get('project_status')->get_status(), 'one project', 'taskfreak').' | '; 125 | echo '
No project found |
message; ?>
174 | 175 |
176 | 179 | -------------------------------------------------------------------------------- /inc/views/front/nav.php: -------------------------------------------------------------------------------- 1 |11 | | 12 | | 13 | |
---|---|---|
'.$pid.' | '; 29 | echo ''; 30 | echo ''.$this->data->get('name').''; 31 | echo ' | '; 32 | echo ''._x($this->data->get('project_status')->get_status(), 'one project', 'taskfreak').' | '; 33 | echo '
'.__('No project found', 'taskfreak').' |
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 |
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 |display_name : __('[deleted user]', 'taskfreak')) ?>
34 |data->html('creation_date') ?>
35 |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 | 66 |18 | | 19 | | 20 | |
---|---|---|
log->html('log_date') ?> | 24 |25 | log->get('user')->get('display_name') ? $this->log->get('user')->get('display_name') : __('[deleted user]', 'taskfreak') ?> 26 | | 27 |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 | | 35 |
Test | Result | Reason | 19 |'.$file.' | '.$result.' | '.$msg[1].' |
---|
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\nSubscribers can comment tasks.
\r\nAuthors can create tasks.
\r\nEditors 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 | } --------------------------------------------------------------------------------