feat: Add wiki integration to task management
- Implemented a multi-select dropdown for associating tasks with wiki entries in the task edit form. - Enhanced task popup to display related wiki entries with visibility controls based on user permissions. - Updated the wiki main view to support bulk actions for categories, including deletion and search functionality. - Created a new database migration for establishing many-to-many relationships between tasks and wiki entries. - Improved styling for wiki components to enhance user experience. - Added a new AGENTS.md file to outline communication and change management protocols.
This commit is contained in:
28
.vscode/ftp-kr.sync.cache.json
vendored
28
.vscode/ftp-kr.sync.cache.json
vendored
@@ -311,6 +311,16 @@
|
|||||||
"lmtime": 0,
|
"lmtime": 0,
|
||||||
"modified": true
|
"modified": true
|
||||||
},
|
},
|
||||||
|
"docs": {
|
||||||
|
"migrations": {
|
||||||
|
"2026-03-01-tasks-recursive-parent-id.sql": {
|
||||||
|
"type": "-",
|
||||||
|
"size": 542,
|
||||||
|
"lmtime": 1772360740310,
|
||||||
|
"modified": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
".htaccess": {
|
".htaccess": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 1055,
|
"size": 1055,
|
||||||
@@ -319,8 +329,8 @@
|
|||||||
},
|
},
|
||||||
"index.php": {
|
"index.php": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 6268,
|
"size": 6563,
|
||||||
"lmtime": 1772276742587,
|
"lmtime": 1772386724937,
|
||||||
"modified": false
|
"modified": false
|
||||||
},
|
},
|
||||||
"layout": {
|
"layout": {
|
||||||
@@ -634,8 +644,8 @@
|
|||||||
},
|
},
|
||||||
"task_popup.php": {
|
"task_popup.php": {
|
||||||
"type": "-",
|
"type": "-",
|
||||||
"size": 32627,
|
"size": 36954,
|
||||||
"lmtime": 1772282729802,
|
"lmtime": 1772395947362,
|
||||||
"modified": false
|
"modified": false
|
||||||
},
|
},
|
||||||
"task_single.php": {
|
"task_single.php": {
|
||||||
@@ -786,16 +796,6 @@
|
|||||||
"size": 230708,
|
"size": 230708,
|
||||||
"lmtime": 1771920013460,
|
"lmtime": 1771920013460,
|
||||||
"modified": false
|
"modified": false
|
||||||
},
|
|
||||||
"docs": {
|
|
||||||
"migrations": {
|
|
||||||
"2026-03-01-tasks-recursive-parent-id.sql": {
|
|
||||||
"type": "-",
|
|
||||||
"size": 542,
|
|
||||||
"lmtime": 1772360740310,
|
|
||||||
"modified": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
10
AGENTS.md
Normal file
10
AGENTS.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## Sposób pracy
|
||||||
|
- Pisz do mnie po polsku, zwięźle i krótko, ale merytorycznie
|
||||||
|
|
||||||
|
## Wprowadzanie zmian
|
||||||
|
- Przeanalizuj wprowadzone zadanie
|
||||||
|
- Jeżeli masz jakieś wątpliwości pytaj
|
||||||
|
- Przedstaw plan
|
||||||
|
- Po akceptacji wdróź plan
|
||||||
@@ -458,10 +458,15 @@ class Tasks
|
|||||||
$task['id'] = isset( $task['id'] ) ? (int)$task['id'] : $task_id;
|
$task['id'] = isset( $task['id'] ) ? (int)$task['id'] : $task_id;
|
||||||
$task_id_for_attachments = (int)$task['id'];
|
$task_id_for_attachments = (int)$task['id'];
|
||||||
|
|
||||||
|
$wiki_categories = \factory\Wiki::get_categories_for_user( (int)$user['id'] );
|
||||||
|
if ( !is_array( $wiki_categories ) or !count( $wiki_categories ) )
|
||||||
|
$wiki_categories = \factory\Wiki::get_all_categories();
|
||||||
|
|
||||||
return \Tpl::view( 'tasks/task_edit', [
|
return \Tpl::view( 'tasks/task_edit', [
|
||||||
'projects' => \factory\Projects::user_projects( $user['id'] ),
|
'projects' => \factory\Projects::user_projects( $user['id'] ),
|
||||||
'priorities' => \factory\Tasks::$priorities,
|
'priorities' => \factory\Tasks::$priorities,
|
||||||
'task' => $task,
|
'task' => $task,
|
||||||
|
'wiki_categories' => $wiki_categories,
|
||||||
'task_attachments' => $task_id_for_attachments ? $attachments_repository -> listByTaskId( $task_id_for_attachments ) : [],
|
'task_attachments' => $task_id_for_attachments ? $attachments_repository -> listByTaskId( $task_id_for_attachments ) : [],
|
||||||
'parent_tasks' => \factory\Tasks::parent_tasks( $user['id'] ),
|
'parent_tasks' => \factory\Tasks::parent_tasks( $user['id'] ),
|
||||||
'users' => \factory\Users::users_list(),
|
'users' => \factory\Users::users_list(),
|
||||||
@@ -493,6 +498,12 @@ class Tasks
|
|||||||
$values['parent_id'] = null;
|
$values['parent_id'] = null;
|
||||||
$values['priority'] = isset( $values['priority'] ) && $values['priority'] !== '' ? $values['priority'] : ( empty( $values['id'] ) ? 1 : 0 );
|
$values['priority'] = isset( $values['priority'] ) && $values['priority'] !== '' ? $values['priority'] : ( empty( $values['id'] ) ? 1 : 0 );
|
||||||
$values['recursive_last_date'] = isset( $values['recursive_last_date'] ) ? $values['recursive_last_date'] : null;
|
$values['recursive_last_date'] = isset( $values['recursive_last_date'] ) ? $values['recursive_last_date'] : null;
|
||||||
|
if ( isset( $values['wiki_ids'] ) )
|
||||||
|
$values['wiki_ids'] = $values['wiki_ids'];
|
||||||
|
else if ( isset( $values['wiki_ids[]'] ) )
|
||||||
|
$values['wiki_ids'] = $values['wiki_ids[]'];
|
||||||
|
else
|
||||||
|
$values['wiki_ids'] = [];
|
||||||
$status = \Controllers\TasksController::resolveTaskStatusForSave( $values );
|
$status = \Controllers\TasksController::resolveTaskStatusForSave( $values );
|
||||||
$recursive_parent_id = null;
|
$recursive_parent_id = null;
|
||||||
|
|
||||||
@@ -504,7 +515,7 @@ class Tasks
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ( $id = \factory\Tasks::task_save(
|
if ( $id = \factory\Tasks::task_save(
|
||||||
$values['id'], $values['parent_id'], $recursive_parent_id, $user['id'], $values['name'], $values['text'], $values['date_start'], $values['date_end'], $values['project_id'], $values['client_id'], $values['pay_rate'], $values['reminders_interval'], $values['recursively'], $values['frequency'], $values['period'], $values['users'], null, null, $values['send_email_notification'], $values['status_change_mail'], false, $status, $values['show_in_calendar'], $values['priority'], $values['recursive_last_date']
|
$values['id'], $values['parent_id'], $recursive_parent_id, $user['id'], $values['name'], $values['text'], $values['date_start'], $values['date_end'], $values['project_id'], $values['client_id'], $values['pay_rate'], $values['reminders_interval'], $values['recursively'], $values['frequency'], $values['period'], $values['users'], null, null, $values['send_email_notification'], $values['status_change_mail'], false, $status, $values['show_in_calendar'], $values['priority'], $values['recursive_last_date'], $values['wiki_ids']
|
||||||
) )
|
) )
|
||||||
{
|
{
|
||||||
\factory\Tasks::clear_task_opened( $id );
|
\factory\Tasks::clear_task_opened( $id );
|
||||||
@@ -527,11 +538,16 @@ class Tasks
|
|||||||
$attachments_repository = new \Domain\Tasks\TaskAttachmentRepository();
|
$attachments_repository = new \Domain\Tasks\TaskAttachmentRepository();
|
||||||
|
|
||||||
\factory\Tasks::set_task_opened_by_user( \S::get( 'task_id' ), $user['id'] );
|
\factory\Tasks::set_task_opened_by_user( \S::get( 'task_id' ), $user['id'] );
|
||||||
|
$wiki_data = \factory\Tasks::task_wiki_entries_for_user( (int)\S::get( 'task_id' ), (int)$user['id'] );
|
||||||
|
|
||||||
echo \Tpl::view( 'tasks/task_popup', [
|
echo \Tpl::view( 'tasks/task_popup', [
|
||||||
'task' => \factory\Tasks::task_details( \S::get( 'task_id' ), $user['id'] ),
|
'task' => \factory\Tasks::task_details( \S::get( 'task_id' ), $user['id'] ),
|
||||||
'task_works' => \factory\Tasks::task_works( \S::get( 'task_id' ) ),
|
'task_works' => \factory\Tasks::task_works( \S::get( 'task_id' ) ),
|
||||||
'task_attachments' => $attachments_repository -> listByTaskId( \S::get( 'task_id' ) ),
|
'task_attachments' => $attachments_repository -> listByTaskId( \S::get( 'task_id' ) ),
|
||||||
|
'task_wiki_entries' => $wiki_data['entries'],
|
||||||
|
'task_wiki_all_count' => $wiki_data['all_count'],
|
||||||
|
'task_wiki_visible_count' => $wiki_data['visible_count'],
|
||||||
|
'task_wiki_hidden_count' => $wiki_data['hidden_count'],
|
||||||
'clients' => \factory\Crm::get_client_list(),
|
'clients' => \factory\Crm::get_client_list(),
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
'statuses' => \factory\Tasks::get_statuses(),
|
'statuses' => \factory\Tasks::get_statuses(),
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ class Wiki
|
|||||||
|
|
||||||
if ( !$user or!\controls\Users::permissions( $user[ 'id' ], 'wiki' ) )
|
if ( !$user or!\controls\Users::permissions( $user[ 'id' ], 'wiki' ) )
|
||||||
return false;
|
return false;
|
||||||
|
if ( (int)$user['id'] !== 1 and (int)$user['id'] !== 3 )
|
||||||
|
return false;
|
||||||
|
|
||||||
if ( \factory\Wiki::category_delete( \S::get( 'id' ) ) )
|
if ( \factory\Wiki::category_delete( \S::get( 'id' ) ) )
|
||||||
\S::alert( 'Kategoria została usunięta.' );
|
\S::alert( 'Kategoria została usunięta.' );
|
||||||
@@ -17,6 +19,30 @@ class Wiki
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function categories_delete_bulk()
|
||||||
|
{
|
||||||
|
global $user;
|
||||||
|
|
||||||
|
if ( !$user or!\controls\Users::permissions( $user[ 'id' ], 'wiki' ) )
|
||||||
|
return false;
|
||||||
|
if ( (int)$user['id'] !== 1 and (int)$user['id'] !== 3 )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$ids = \S::get( 'ids' );
|
||||||
|
if ( !is_array( $ids ) )
|
||||||
|
$ids = [];
|
||||||
|
|
||||||
|
$deleted_count = \factory\Wiki::categories_delete_bulk( $ids );
|
||||||
|
|
||||||
|
if ( $deleted_count > 0 )
|
||||||
|
\S::alert( 'Usunięto wpisy: ' . $deleted_count . '.' );
|
||||||
|
else
|
||||||
|
\S::alert( 'Nie wybrano poprawnych wpisów do usunięcia.' );
|
||||||
|
|
||||||
|
header( 'Location: /wiki/main_view/' );
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
public static function category_save()
|
public static function category_save()
|
||||||
{
|
{
|
||||||
global $user;
|
global $user;
|
||||||
|
|||||||
@@ -589,10 +589,123 @@ class Tasks
|
|||||||
if ( $user_id )
|
if ( $user_id )
|
||||||
$task['is_open'] = self::is_task_open( $task_id, $user_id );
|
$task['is_open'] = self::is_task_open( $task_id, $user_id );
|
||||||
$task['comments'] = $mdb -> select( 'tasks_comments', '*', [ 'task_id' => $task_id, 'ORDER' => [ 'date_add' => 'DESC' ] ] );
|
$task['comments'] = $mdb -> select( 'tasks_comments', '*', [ 'task_id' => $task_id, 'ORDER' => [ 'date_add' => 'DESC' ] ] );
|
||||||
|
$task['wiki_ids'] = self::task_wiki_ids( $task_id );
|
||||||
|
|
||||||
return $task;
|
return $task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function normalize_wiki_ids( $wiki_ids )
|
||||||
|
{
|
||||||
|
if ( $wiki_ids === null or $wiki_ids === '' )
|
||||||
|
return [];
|
||||||
|
|
||||||
|
if ( is_string( $wiki_ids ) and strpos( $wiki_ids, ',' ) !== false )
|
||||||
|
$wiki_ids = explode( ',', $wiki_ids );
|
||||||
|
|
||||||
|
if ( !is_array( $wiki_ids ) )
|
||||||
|
$wiki_ids = [ $wiki_ids ];
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
foreach ( $wiki_ids as $wiki_id )
|
||||||
|
{
|
||||||
|
$wiki_id = (int)$wiki_id;
|
||||||
|
if ( $wiki_id > 0 )
|
||||||
|
$result[] = $wiki_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values( array_unique( $result ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function task_wiki_ids( $task_id )
|
||||||
|
{
|
||||||
|
global $mdb;
|
||||||
|
|
||||||
|
$task_id = (int)$task_id;
|
||||||
|
if ( !$task_id )
|
||||||
|
return [];
|
||||||
|
|
||||||
|
$ids = $mdb -> select( 'task_wiki', 'wiki_id', [ 'task_id' => $task_id ] );
|
||||||
|
if ( !is_array( $ids ) )
|
||||||
|
return [];
|
||||||
|
|
||||||
|
return array_values( array_unique( array_map( 'intval', $ids ) ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function set_task_wiki_links( $task_id, $wiki_ids, $user_id )
|
||||||
|
{
|
||||||
|
global $mdb;
|
||||||
|
|
||||||
|
$task_id = (int)$task_id;
|
||||||
|
$user_id = (int)$user_id;
|
||||||
|
|
||||||
|
if ( !$task_id or !$user_id )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$wiki_ids = self::normalize_wiki_ids( $wiki_ids );
|
||||||
|
$visible_wiki_ids = [];
|
||||||
|
foreach ( $wiki_ids as $wiki_id )
|
||||||
|
{
|
||||||
|
if ( \factory\Wiki::is_category_visible_for_user( (int)$wiki_id, $user_id ) )
|
||||||
|
$visible_wiki_ids[] = (int)$wiki_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$existing_ids = self::task_wiki_ids( $task_id );
|
||||||
|
$hidden_existing_ids = [];
|
||||||
|
foreach ( $existing_ids as $existing_id )
|
||||||
|
{
|
||||||
|
if ( !\factory\Wiki::is_category_visible_for_user( (int)$existing_id, $user_id ) )
|
||||||
|
$hidden_existing_ids[] = (int)$existing_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$final_ids = array_values( array_unique( array_merge( $visible_wiki_ids, $hidden_existing_ids ) ) );
|
||||||
|
|
||||||
|
$mdb -> delete( 'task_wiki', [ 'task_id' => $task_id ] );
|
||||||
|
|
||||||
|
foreach ( $final_ids as $wiki_id )
|
||||||
|
$mdb -> insert( 'task_wiki', [ 'task_id' => $task_id, 'wiki_id' => $wiki_id ] );
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function task_wiki_entries_for_user( $task_id, $user_id )
|
||||||
|
{
|
||||||
|
global $mdb;
|
||||||
|
|
||||||
|
$task_id = (int)$task_id;
|
||||||
|
$user_id = (int)$user_id;
|
||||||
|
|
||||||
|
$response = [
|
||||||
|
'all_count' => 0,
|
||||||
|
'visible_count' => 0,
|
||||||
|
'hidden_count' => 0,
|
||||||
|
'entries' => []
|
||||||
|
];
|
||||||
|
|
||||||
|
if ( !$task_id or !$user_id )
|
||||||
|
return $response;
|
||||||
|
|
||||||
|
$response['all_count'] = (int)$mdb -> count( 'task_wiki', [ 'task_id' => $task_id ] );
|
||||||
|
|
||||||
|
if ( $user_id == 1 or $user_id == 3 )
|
||||||
|
$sql = 'SELECT w.* FROM task_wiki tw INNER JOIN wiki w ON w.id = tw.wiki_id WHERE tw.task_id = ' . $task_id . ' ORDER BY w.name ASC';
|
||||||
|
else
|
||||||
|
$sql = 'SELECT DISTINCT w.* FROM task_wiki tw '
|
||||||
|
. 'INNER JOIN wiki w ON w.id = tw.wiki_id '
|
||||||
|
. 'LEFT JOIN wiki_users wu ON wu.wiki_id = w.id '
|
||||||
|
. 'WHERE tw.task_id = ' . $task_id . ' AND (wu.user_id = ' . $user_id . ' OR NOT EXISTS (SELECT 1 FROM wiki_users wu2 WHERE wu2.wiki_id = w.id)) '
|
||||||
|
. 'ORDER BY w.name ASC';
|
||||||
|
|
||||||
|
$entries = $mdb -> query( $sql ) -> fetchAll( \PDO::FETCH_ASSOC );
|
||||||
|
if ( !is_array( $entries ) )
|
||||||
|
$entries = [];
|
||||||
|
|
||||||
|
$response['entries'] = $entries;
|
||||||
|
$response['visible_count'] = count( $entries );
|
||||||
|
$response['hidden_count'] = max( 0, $response['all_count'] - $response['visible_count'] );
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
static public function task_total_time( $task_id, $month = '' )
|
static public function task_total_time( $task_id, $month = '' )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
@@ -633,7 +746,7 @@ class Tasks
|
|||||||
}
|
}
|
||||||
|
|
||||||
// przy zmianach pamiętać o zadaniach z CRON
|
// przy zmianach pamiętać o zadaniach z CRON
|
||||||
static public function task_save( $task_id, $parent_id = null, $recursive_parent_id = null, $user_id, $name, $text, $date_start, $date_end, $project_id, $client_id, $pay_rate, $reminders_interval, $recursively, $frequency, $period, $users, $date_end_month_day = null, $date_start_month_day = null, $send_email_notification = false, $status_change_mail, $rescursive = false, $status = 0, $show_in_calendar, $priority = 0, $recursive_last_date = null )
|
static public function task_save( $task_id, $parent_id = null, $recursive_parent_id = null, $user_id, $name, $text, $date_start, $date_end, $project_id, $client_id, $pay_rate, $reminders_interval, $recursively, $frequency, $period, $users, $date_end_month_day = null, $date_start_month_day = null, $send_email_notification = false, $status_change_mail, $rescursive = false, $status = 0, $show_in_calendar, $priority = 0, $recursive_last_date = null, $wiki_ids = [] )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
|
||||||
@@ -685,6 +798,7 @@ class Tasks
|
|||||||
\factory\Projects::send_email_notification( $id, $users );
|
\factory\Projects::send_email_notification( $id, $users );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::set_task_wiki_links( $id, $wiki_ids, $user_id );
|
||||||
return $id;
|
return $id;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -730,6 +844,7 @@ class Tasks
|
|||||||
}
|
}
|
||||||
|
|
||||||
$mdb -> delete( 'tasks_reminders', [ 'task_id' => $task_id ] );
|
$mdb -> delete( 'tasks_reminders', [ 'task_id' => $task_id ] );
|
||||||
|
self::set_task_wiki_links( (int)$task_id, $wiki_ids, $user_id );
|
||||||
|
|
||||||
return $task_id;
|
return $task_id;
|
||||||
}
|
}
|
||||||
@@ -779,6 +894,7 @@ class Tasks
|
|||||||
|
|
||||||
$attachments_repository = new \Domain\Tasks\TaskAttachmentRepository( $mdb );
|
$attachments_repository = new \Domain\Tasks\TaskAttachmentRepository( $mdb );
|
||||||
$attachments_repository -> purgeByTaskId( $task_id );
|
$attachments_repository -> purgeByTaskId( $task_id );
|
||||||
|
$mdb -> delete( 'task_wiki', [ 'task_id' => (int)$task_id ] );
|
||||||
|
|
||||||
$mdb -> delete( 'tasks', [ 'id' => $task_id ] );
|
$mdb -> delete( 'tasks', [ 'id' => $task_id ] );
|
||||||
$mdb -> update( 'tasks', [ 'recursive_parent_id' => null ], [ 'recursive_parent_id' => $task_id ] );
|
$mdb -> update( 'tasks', [ 'recursive_parent_id' => null ], [ 'recursive_parent_id' => $task_id ] );
|
||||||
|
|||||||
@@ -3,11 +3,47 @@ namespace factory;
|
|||||||
|
|
||||||
class Wiki
|
class Wiki
|
||||||
{
|
{
|
||||||
|
static public function get_all_categories()
|
||||||
|
{
|
||||||
|
global $mdb;
|
||||||
|
return $mdb -> select( 'wiki', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||||
|
}
|
||||||
|
|
||||||
static public function category_delete( int $category_id ) {
|
static public function category_delete( int $category_id ) {
|
||||||
global $mdb;
|
global $mdb;
|
||||||
|
|
||||||
|
$category_id = (int)$category_id;
|
||||||
|
if ( !$category_id )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
$mdb -> delete( 'wiki_users', [ 'wiki_id' => $category_id ] );
|
||||||
|
$mdb -> delete( 'task_wiki', [ 'wiki_id' => $category_id ] );
|
||||||
return $mdb -> delete( 'wiki', [ 'id' => $category_id ] );
|
return $mdb -> delete( 'wiki', [ 'id' => $category_id ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static public function categories_delete_bulk( array $category_ids )
|
||||||
|
{
|
||||||
|
global $mdb;
|
||||||
|
|
||||||
|
$ids = [];
|
||||||
|
foreach ( $category_ids as $category_id )
|
||||||
|
{
|
||||||
|
$category_id = (int)$category_id;
|
||||||
|
if ( $category_id > 0 )
|
||||||
|
$ids[] = $category_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ids = array_values( array_unique( $ids ) );
|
||||||
|
if ( !count( $ids ) )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
$mdb -> delete( 'wiki_users', [ 'wiki_id' => $ids ] );
|
||||||
|
$mdb -> delete( 'task_wiki', [ 'wiki_id' => $ids ] );
|
||||||
|
$mdb -> delete( 'wiki', [ 'id' => $ids ] );
|
||||||
|
|
||||||
|
return count( $ids );
|
||||||
|
}
|
||||||
|
|
||||||
public static function category_save( $category_id, $name, $text, $text_admin = '', $users )
|
public static function category_save( $category_id, $name, $text, $text_admin = '', $users )
|
||||||
{
|
{
|
||||||
global $mdb;
|
global $mdb;
|
||||||
@@ -59,9 +95,38 @@ class Wiki
|
|||||||
{
|
{
|
||||||
global $mdb, $user;
|
global $mdb, $user;
|
||||||
|
|
||||||
if ( $user['id'] == 1 or $user['id'] == 3 )
|
return self::get_categories_for_user( (int)$user['id'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function get_categories_for_user( int $user_id )
|
||||||
|
{
|
||||||
|
global $mdb;
|
||||||
|
|
||||||
|
if ( $user_id == 1 or $user_id == 3 )
|
||||||
return $mdb -> select( 'wiki', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
return $mdb -> select( 'wiki', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
|
||||||
else
|
|
||||||
return $mdb -> query( 'SELECT w.* FROM wiki AS w INNER JOIN wiki_users AS wu ON wu.wiki_id = w.id WHERE user_id = ' . $user['id'] ) -> fetchAll( \PDO::FETCH_ASSOC );
|
return $mdb -> query(
|
||||||
|
'SELECT DISTINCT w.* FROM wiki AS w '
|
||||||
|
. 'LEFT JOIN wiki_users AS wu ON wu.wiki_id = w.id '
|
||||||
|
. 'WHERE wu.user_id = ' . $user_id . ' OR NOT EXISTS (SELECT 1 FROM wiki_users wu2 WHERE wu2.wiki_id = w.id) '
|
||||||
|
. 'ORDER BY w.name ASC'
|
||||||
|
) -> fetchAll( \PDO::FETCH_ASSOC );
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function is_category_visible_for_user( int $category_id, int $user_id ): bool
|
||||||
|
{
|
||||||
|
global $mdb;
|
||||||
|
|
||||||
|
if ( !$category_id or !$user_id )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if ( $user_id == 1 or $user_id == 3 )
|
||||||
|
return (bool)$mdb -> count( 'wiki', [ 'id' => $category_id ] );
|
||||||
|
|
||||||
|
$visibility_rows = (int)$mdb -> count( 'wiki_users', [ 'wiki_id' => $category_id ] );
|
||||||
|
if ( $visibility_rows === 0 )
|
||||||
|
return (bool)$mdb -> count( 'wiki', [ 'id' => $category_id ] );
|
||||||
|
|
||||||
|
return (bool)$mdb -> count( 'wiki_users', [ 'AND' => [ 'wiki_id' => $category_id, 'user_id' => $user_id ] ] );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
docs/migrations/2026-03-03-task-wiki-relations.sql
Normal file
10
docs/migrations/2026-03-03-task-wiki-relations.sql
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
-- 2026-03-03: relacja wiele-do-wielu zadania <-> wpisy wiki
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `task_wiki` (
|
||||||
|
`task_id` INT NOT NULL,
|
||||||
|
`wiki_id` INT NOT NULL,
|
||||||
|
`date_add` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`task_id`, `wiki_id`),
|
||||||
|
KEY `idx_task_wiki_wiki` (`wiki_id`)
|
||||||
|
);
|
||||||
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1044,19 +1044,27 @@ $sidebar-hover-bg: rgba(255, 255, 255, 0.08);
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
|
||||||
i { font-size: 13px; }
|
i {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.task-title-save {
|
a.task-title-save {
|
||||||
background: $cGreen;
|
background: $cGreen;
|
||||||
border-color: $cGreen;
|
border-color: $cGreen;
|
||||||
i { color: #fff; }
|
|
||||||
|
i {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a.task-title-cancel {
|
a.task-title-cancel {
|
||||||
background: $cRed;
|
background: $cRed;
|
||||||
border-color: $cRed;
|
border-color: $cRed;
|
||||||
i { color: #fff; }
|
|
||||||
|
i {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,23 @@ if ( is_array( $this -> clients ) )
|
|||||||
foreach ( $this -> clients as $client )
|
foreach ( $this -> clients as $client )
|
||||||
$clients[ $client[ 'id' ] ] = $client[ 'firm' ];
|
$clients[ $client[ 'id' ] ] = $client[ 'firm' ];
|
||||||
|
|
||||||
|
$wiki_categories = ( isset( $this -> wiki_categories ) and is_array( $this -> wiki_categories ) ) ? $this -> wiki_categories : [];
|
||||||
|
$current_user_id = isset( $this -> user['id'] ) ? (int)$this -> user['id'] : 0;
|
||||||
|
if ( !count( $wiki_categories ) and $current_user_id > 0 )
|
||||||
|
$wiki_categories = \factory\Wiki::get_categories_for_user( $current_user_id );
|
||||||
|
if ( !count( $wiki_categories ) )
|
||||||
|
$wiki_categories = \factory\Wiki::get_all_categories();
|
||||||
|
$task_wiki_ids = isset( $this -> task['wiki_ids'] ) && is_array( $this -> task['wiki_ids'] ) ? array_map( 'intval', $this -> task['wiki_ids'] ) : [];
|
||||||
|
$wiki_values = [];
|
||||||
|
if ( is_array( $wiki_categories ) )
|
||||||
|
foreach ( $wiki_categories as $wiki_category )
|
||||||
|
{
|
||||||
|
$wiki_id = isset( $wiki_category['id'] ) ? (int)$wiki_category['id'] : 0;
|
||||||
|
if ( !$wiki_id )
|
||||||
|
continue;
|
||||||
|
$wiki_values[ $wiki_id ] = isset( $wiki_category['name'] ) ? (string)$wiki_category['name'] : (string)$wiki_id;
|
||||||
|
}
|
||||||
|
|
||||||
$parent_tasks = [ 0 => '--- wybierz zadanie nadrzędne ---' ];
|
$parent_tasks = [ 0 => '--- wybierz zadanie nadrzędne ---' ];
|
||||||
if ( is_array( $this -> parent_tasks ) )
|
if ( is_array( $this -> parent_tasks ) )
|
||||||
foreach ( $this -> parent_tasks as $parent_task )
|
foreach ( $this -> parent_tasks as $parent_task )
|
||||||
@@ -130,6 +147,20 @@ ob_start();
|
|||||||
'values' => $parent_tasks
|
'values' => $parent_tasks
|
||||||
] );
|
] );
|
||||||
?>
|
?>
|
||||||
|
<div class="form_group">
|
||||||
|
<label class="label">Powiazane Wiki:</label>
|
||||||
|
<div class="input">
|
||||||
|
<? if ( is_array( $wiki_categories ) and count( $wiki_categories ) ):?>
|
||||||
|
<select name="wiki_ids[]" id="wiki_ids" class="form-control" multiple>
|
||||||
|
<? foreach ( $wiki_values as $wiki_id => $wiki_name ):?>
|
||||||
|
<option value="<?= (int)$wiki_id;?>" <? if ( in_array( (int)$wiki_id, $task_wiki_ids ) ):?>selected="selected"<? endif;?>><?= htmlspecialchars( $wiki_name );?></option>
|
||||||
|
<? endforeach;?>
|
||||||
|
</select>
|
||||||
|
<? else:?>
|
||||||
|
<div class="task-wiki-empty">Brak dostepnych wpisow Wiki.</div>
|
||||||
|
<? endif;?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<?= \Html::select( [
|
<?= \Html::select( [
|
||||||
'label' => 'Status',
|
'label' => 'Status',
|
||||||
'name' => 'status',
|
'name' => 'status',
|
||||||
@@ -434,6 +465,29 @@ ob_start();
|
|||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#wiki_ids + .select2-container {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki_ids + .select2-container .select2-selection--multiple {
|
||||||
|
min-height: 38px;
|
||||||
|
border: 1px solid #cfd8e3;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#wiki_ids + .select2-container .select2-selection__choice {
|
||||||
|
margin-top: 4px;
|
||||||
|
max-width: 100%;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-wiki-empty {
|
||||||
|
color: #6b7280;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
.task-edit-message {
|
.task-edit-message {
|
||||||
margin: 12px 0 16px 0;
|
margin: 12px 0 16px 0;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
@@ -957,12 +1011,12 @@ echo $grid -> draw();
|
|||||||
height: '100'
|
height: '100'
|
||||||
});
|
});
|
||||||
|
|
||||||
$( '#project_id, #client_id, #status, #parent_id, #priority' ).select2({
|
$( '#project_id, #client_id, #status, #parent_id, #priority, #wiki_ids' ).select2({
|
||||||
theme: 'bootstrap-5',
|
theme: 'bootstrap-5',
|
||||||
minimumResultsForSearch: 0
|
minimumResultsForSearch: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
$( '#project_id, #client_id, #status, #parent_id, #priority' ).on( 'select2:open', function() {
|
$( '#project_id, #client_id, #status, #parent_id, #priority, #wiki_ids' ).on( 'select2:open', function() {
|
||||||
setTimeout( function() {
|
setTimeout( function() {
|
||||||
var search_field = document.querySelector( '.select2-container--open .select2-search__field' );
|
var search_field = document.querySelector( '.select2-container--open .select2-search__field' );
|
||||||
if ( search_field )
|
if ( search_field )
|
||||||
|
|||||||
@@ -26,6 +26,10 @@
|
|||||||
$checklist_count = is_array( $this -> task['actions'] ) ? count( $this -> task['actions'] ) : 0;
|
$checklist_count = is_array( $this -> task['actions'] ) ? count( $this -> task['actions'] ) : 0;
|
||||||
$comments_count = is_array( $this -> task['comments'] ) ? count( $this -> task['comments'] ) : 0;
|
$comments_count = is_array( $this -> task['comments'] ) ? count( $this -> task['comments'] ) : 0;
|
||||||
$attachments_count = is_array( $this -> task_attachments ) ? count( $this -> task_attachments ) : 0;
|
$attachments_count = is_array( $this -> task_attachments ) ? count( $this -> task_attachments ) : 0;
|
||||||
|
$popup_wiki_data = \factory\Tasks::task_wiki_entries_for_user( (int)$this -> task['id'], (int)$this -> user['id'] );
|
||||||
|
$task_wiki_entries = isset( $popup_wiki_data['entries'] ) && is_array( $popup_wiki_data['entries'] ) ? $popup_wiki_data['entries'] : [];
|
||||||
|
$wiki_visible_count = isset( $popup_wiki_data['visible_count'] ) ? (int)$popup_wiki_data['visible_count'] : 0;
|
||||||
|
$wiki_hidden_count = isset( $popup_wiki_data['hidden_count'] ) ? (int)$popup_wiki_data['hidden_count'] : 0;
|
||||||
?>
|
?>
|
||||||
<?
|
<?
|
||||||
$task_description_html = htmlspecialchars_decode( (string)$this -> task['text'] );
|
$task_description_html = htmlspecialchars_decode( (string)$this -> task['text'] );
|
||||||
@@ -74,6 +78,7 @@
|
|||||||
<a href="#" class="js-task-tab-btn" data-tab="checklist" role="tab" aria-selected="false">Lista kontrolna (<?= (int)$checklist_count;?>)</a>
|
<a href="#" class="js-task-tab-btn" data-tab="checklist" role="tab" aria-selected="false">Lista kontrolna (<?= (int)$checklist_count;?>)</a>
|
||||||
<a href="#" class="js-task-tab-btn" data-tab="comments" role="tab" aria-selected="false">Komentarze (<?= (int)$comments_count;?>)</a>
|
<a href="#" class="js-task-tab-btn" data-tab="comments" role="tab" aria-selected="false">Komentarze (<?= (int)$comments_count;?>)</a>
|
||||||
<a href="#" class="js-task-tab-btn" data-tab="attachments" role="tab" aria-selected="false">Załączniki (<?= (int)$attachments_count;?>)</a>
|
<a href="#" class="js-task-tab-btn" data-tab="attachments" role="tab" aria-selected="false">Załączniki (<?= (int)$attachments_count;?>)</a>
|
||||||
|
<a href="#" class="js-task-tab-btn" data-tab="wiki" role="tab" aria-selected="false">Wiki (<?= (int)$wiki_visible_count;?>)</a>
|
||||||
<? if ( $this -> user['id'] == 1 ):?>
|
<? if ( $this -> user['id'] == 1 ):?>
|
||||||
<a href="#" class="js-task-tab-btn" data-tab="users" role="tab" aria-selected="false">Uczestnicy</a>
|
<a href="#" class="js-task-tab-btn" data-tab="users" role="tab" aria-selected="false">Uczestnicy</a>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
@@ -177,6 +182,35 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="task-tab-panel" data-tab="wiki">
|
||||||
|
<div class="box">
|
||||||
|
<h3>Powiazane wpisy Wiki</h3>
|
||||||
|
<? if ( $wiki_hidden_count > 0 ):?>
|
||||||
|
<div class="alert alert-warning" style="margin-bottom: 12px;">
|
||||||
|
Nie masz dostepu do <?= (int)$wiki_hidden_count;?> powiazanych wpisow Wiki.
|
||||||
|
</div>
|
||||||
|
<? endif;?>
|
||||||
|
<? if ( is_array( $task_wiki_entries ) and count( $task_wiki_entries ) ):?>
|
||||||
|
<div class="task-wiki-list">
|
||||||
|
<? foreach ( $task_wiki_entries as $wiki_entry ):?>
|
||||||
|
<div class="task-wiki-entry">
|
||||||
|
<h4><?= htmlspecialchars( (string)$wiki_entry['name'] );?></h4>
|
||||||
|
<div class="task-wiki-content">
|
||||||
|
<?= (string)$wiki_entry['text'];?>
|
||||||
|
<? if ( $this -> user['id'] == 1 or $this -> user['id'] == 3 ):?>
|
||||||
|
<? if ( isset( $wiki_entry['text_admin'] ) and (string)$wiki_entry['text_admin'] !== '' ):?>
|
||||||
|
<div class="task-wiki-admin"><?= (string)$wiki_entry['text_admin'];?></div>
|
||||||
|
<? endif;?>
|
||||||
|
<? endif;?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<? endforeach;?>
|
||||||
|
</div>
|
||||||
|
<? else:?>
|
||||||
|
<div class="task-wiki-empty">Brak widocznych wpisow Wiki dla tego zadania.</div>
|
||||||
|
<? endif;?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<? if ( $this -> user['id'] == 1 ):?>
|
<? if ( $this -> user['id'] == 1 ):?>
|
||||||
<div class="task-tab-panel" data-tab="users">
|
<div class="task-tab-panel" data-tab="users">
|
||||||
<div class="task-users-edit">
|
<div class="task-users-edit">
|
||||||
@@ -407,6 +441,36 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task_popup .task_details .task-wiki-list {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.task_popup .task_details .task-wiki-entry {
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.task_popup .task_details .task-wiki-entry h4 {
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1f3d72;
|
||||||
|
}
|
||||||
|
.task_popup .task_details .task-wiki-content {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
.task_popup .task_details .task-wiki-admin {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
border-top: 1px dashed #d1d5db;
|
||||||
|
}
|
||||||
|
.task_popup .task_details .task-wiki-empty {
|
||||||
|
color: #6b7280;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
/* Lightbox - powiększanie zdjęć w opisie */
|
/* Lightbox - powiększanie zdjęć w opisie */
|
||||||
.task_popup .task_details .description img {
|
.task_popup .task_details .description img {
|
||||||
cursor: zoom-in;
|
cursor: zoom-in;
|
||||||
@@ -676,8 +740,16 @@
|
|||||||
if ( !tab_panels.length )
|
if ( !tab_panels.length )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var allowed_tabs = [ 'description', 'checklist', 'comments', 'attachments', 'users' ];
|
var available_tabs = [];
|
||||||
var selected_tab = allowed_tabs.indexOf( tab_name ) >= 0 ? tab_name : 'description';
|
tab_buttons.each( function() {
|
||||||
|
var tab = $( this ).attr( 'data-tab' );
|
||||||
|
if ( tab )
|
||||||
|
available_tabs.push( tab );
|
||||||
|
});
|
||||||
|
|
||||||
|
var selected_tab = available_tabs.indexOf( tab_name ) >= 0 ? tab_name : 'description';
|
||||||
|
if ( available_tabs.indexOf( selected_tab ) < 0 && available_tabs.length )
|
||||||
|
selected_tab = available_tabs[0];
|
||||||
|
|
||||||
tab_panels.each( function() {
|
tab_panels.each( function() {
|
||||||
var panel = $( this );
|
var panel = $( this );
|
||||||
|
|||||||
@@ -1,71 +1,303 @@
|
|||||||
<div class="row block-header">
|
<? $is_admin = ( (int)$this -> user['id'] === 1 or (int)$this -> user['id'] === 3 );?>
|
||||||
|
<div class="row block-header wiki-header">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<h2>Wiki</h2>
|
<h2>Wiki</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="box">
|
|
||||||
<? if ( $this -> user['id'] == 1 or $this -> user['id'] == 3 ):?>
|
<div class="box wiki-main">
|
||||||
<div class="menu-buttons">
|
<div class="wiki-toolbar">
|
||||||
<a href="/wiki/category_edit/" class="btn btn-success btn-sm" title="Dodaj kategorię">
|
<div class="wiki-toolbar-left">
|
||||||
<i class="fa fa-plus"></i>Dodaj kategorię
|
<input type="text" class="form-control" id="wiki-search-name" placeholder="Szukaj wpisu po nazwie...">
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
<? endif;?>
|
<div class="wiki-toolbar-right">
|
||||||
<table class="table table-striped table-hover table-sm">
|
<? if ( $is_admin ):?>
|
||||||
<tr>
|
<a href="/wiki/category_edit/" class="btn btn-success btn-sm" title="Dodaj kategorię">
|
||||||
<td>
|
<i class="fa fa-plus"></i> Dodaj wpis
|
||||||
<input type="text" class="form-control" id="search-name">
|
</a>
|
||||||
</td>
|
<? endif;?>
|
||||||
</tr>
|
</div>
|
||||||
</table>
|
</div>
|
||||||
<div class="wiki-categories">
|
|
||||||
<? foreach ( $this -> categories as $category ):?>
|
<? if ( $is_admin ):?>
|
||||||
<div class="category">
|
<form method="POST" action="/wiki/categories_delete_bulk/" id="wiki-bulk-delete-form">
|
||||||
<div class="title">
|
<div class="wiki-bulk-actions">
|
||||||
<a href="/wiki/category_preview/id=<?= $category['id'];?>"><?= $category['name'];?></a>
|
<label class="wiki-select-all-label">
|
||||||
</div>
|
<input type="checkbox" class="g-checkbox" id="wiki-select-all">
|
||||||
<? if ( $this -> user['id'] == 1 or $this -> user['id'] == 3 ):?>
|
Zaznacz wszystko
|
||||||
<div class="row">
|
</label>
|
||||||
<div class="col-md-6">
|
<span class="wiki-selected-count">Zaznaczono: <strong id="wiki-selected-count-value">0</strong></span>
|
||||||
<div class="users">
|
<button type="button" class="btn btn-danger btn-sm" id="wiki-bulk-delete-btn" disabled>
|
||||||
<? $category_users = \factory\Wiki::category_users( $category['id'] );?>
|
<i class="fa fa-trash"></i> Usuń zaznaczone
|
||||||
<? if ( is_array( $category_users ) ): foreach ( $category_users as $user_tmp ):?>
|
</button>
|
||||||
<?
|
</div>
|
||||||
$user = \factory\Users::user_details( $user_tmp );
|
<div class="wiki-categories-grid">
|
||||||
echo '<div class="user" style="background: ' . $user['color'] . '" title="' . $user['name'] . ' ' . $user['surname'] . '">' . $user['name'][0] . $user['surname'][0] . '</div>';
|
<? if ( is_array( $this -> categories ) and count( $this -> categories ) ):?>
|
||||||
?>
|
<? foreach ( $this -> categories as $category ):?>
|
||||||
<? endforeach; endif;?>
|
<? $category_name = isset( $category['name'] ) ? (string)$category['name'] : '';?>
|
||||||
|
<? $category_text = isset( $category['text'] ) ? strip_tags( (string)$category['text'] ) : '';?>
|
||||||
|
<? $category_preview = trim( $category_text );?>
|
||||||
|
<? if ( strlen( $category_preview ) > 170 ) $category_preview = substr( $category_preview, 0, 170 ) . '...';?>
|
||||||
|
<article class="wiki-card" data-name="<?= htmlspecialchars( strtolower( $category_name ) );?>">
|
||||||
|
<div class="wiki-card-top">
|
||||||
|
<label class="wiki-select-one-label">
|
||||||
|
<input type="checkbox" class="g-checkbox wiki-select-one" name="ids[]" value="<?= (int)$category['id'];?>">
|
||||||
|
</label>
|
||||||
|
<a href="/wiki/category_preview/id=<?= (int)$category['id'];?>" class="wiki-card-title"><?= htmlspecialchars( $category_name );?></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<? if ( $category_preview !== '' ):?>
|
||||||
<div class="col-md-6">
|
<div class="wiki-card-preview"><?= htmlspecialchars( $category_preview );?></div>
|
||||||
<div class="actions">
|
<? else:?>
|
||||||
<a href="/wiki/category_edit/id=<?= $category['id'];?>">edytuj</a>
|
<div class="wiki-card-preview wiki-card-preview-empty">Brak podglądu treści.</div>
|
||||||
<a href="/wiki/category_delete/id=<?= $category['id'];?>" class="category-delete">usuń</a>
|
<? endif;?>
|
||||||
|
<div class="wiki-card-bottom">
|
||||||
|
<div class="users">
|
||||||
|
<? $category_users = \factory\Wiki::category_users( (int)$category['id'] );?>
|
||||||
|
<? if ( is_array( $category_users ) ): foreach ( $category_users as $user_tmp ):?>
|
||||||
|
<?
|
||||||
|
$user = \factory\Users::user_details( $user_tmp );
|
||||||
|
echo '<div class="user" style="background:' . htmlspecialchars( $user['color'] ) . ';" title="' . htmlspecialchars( $user['name'] . ' ' . $user['surname'] ) . '">' . htmlspecialchars( $user['name'][0] . $user['surname'][0] ) . '</div>';
|
||||||
|
?>
|
||||||
|
<? endforeach; endif;?>
|
||||||
|
</div>
|
||||||
|
<div class="wiki-card-actions">
|
||||||
|
<a href="/wiki/category_edit/id=<?= (int)$category['id'];?>">edytuj</a>
|
||||||
|
<a href="/wiki/category_delete/id=<?= (int)$category['id'];?>" class="category-delete">usuń</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</article>
|
||||||
</div>
|
<? endforeach;?>
|
||||||
|
<? else:?>
|
||||||
|
<div class="wiki-empty-state">Brak wpisów Wiki.</div>
|
||||||
<? endif;?>
|
<? endif;?>
|
||||||
</div>
|
</div>
|
||||||
<? endforeach;?>
|
</form>
|
||||||
</div>
|
<? else:?>
|
||||||
|
<div class="wiki-categories-grid">
|
||||||
|
<? if ( is_array( $this -> categories ) and count( $this -> categories ) ):?>
|
||||||
|
<? foreach ( $this -> categories as $category ):?>
|
||||||
|
<? $category_name = isset( $category['name'] ) ? (string)$category['name'] : '';?>
|
||||||
|
<? $category_text = isset( $category['text'] ) ? strip_tags( (string)$category['text'] ) : '';?>
|
||||||
|
<? $category_preview = trim( $category_text );?>
|
||||||
|
<? if ( strlen( $category_preview ) > 170 ) $category_preview = substr( $category_preview, 0, 170 ) . '...';?>
|
||||||
|
<article class="wiki-card" data-name="<?= htmlspecialchars( strtolower( $category_name ) );?>">
|
||||||
|
<div class="wiki-card-top">
|
||||||
|
<a href="/wiki/category_preview/id=<?= (int)$category['id'];?>" class="wiki-card-title"><?= htmlspecialchars( $category_name );?></a>
|
||||||
|
</div>
|
||||||
|
<? if ( $category_preview !== '' ):?>
|
||||||
|
<div class="wiki-card-preview"><?= htmlspecialchars( $category_preview );?></div>
|
||||||
|
<? else:?>
|
||||||
|
<div class="wiki-card-preview wiki-card-preview-empty">Brak podglądu treści.</div>
|
||||||
|
<? endif;?>
|
||||||
|
</article>
|
||||||
|
<? endforeach;?>
|
||||||
|
<? else:?>
|
||||||
|
<div class="wiki-empty-state">Brak wpisów Wiki.</div>
|
||||||
|
<? endif;?>
|
||||||
|
</div>
|
||||||
|
<? endif;?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
.wiki-main {
|
||||||
|
--wiki-bg-soft: #f4f8ff;
|
||||||
|
--wiki-border: #d8e2f6;
|
||||||
|
--wiki-title: #274985;
|
||||||
|
--wiki-text: #425466;
|
||||||
|
--wiki-muted: #728197;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
padding: 12px;
|
||||||
|
border: 1px solid var(--wiki-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: linear-gradient(180deg, #ffffff 0%, var(--wiki-bg-soft) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-toolbar-left {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 420px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-bulk-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid var(--wiki-border);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-select-all-label,
|
||||||
|
.wiki-main .wiki-select-one-label {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--wiki-text);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-selected-count {
|
||||||
|
color: var(--wiki-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-categories-grid {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
border: 1px solid var(--wiki-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #fff;
|
||||||
|
transition: border-color .2s ease, box-shadow .2s ease, transform .2s ease;
|
||||||
|
min-width: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card:hover {
|
||||||
|
border-color: #b8c9eb;
|
||||||
|
box-shadow: 0 8px 20px rgba(31, 61, 114, .08);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card-top {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card-title {
|
||||||
|
color: var(--wiki-title);
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
text-decoration: none;
|
||||||
|
line-height: 1.3;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card-title:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card-preview {
|
||||||
|
color: var(--wiki-text);
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.45;
|
||||||
|
min-height: 38px;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card-preview-empty {
|
||||||
|
color: var(--wiki-muted);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card-bottom {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card-actions a {
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .users {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .users .user {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 11px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0, 0, 0, .22);
|
||||||
|
border: 1px solid rgba(255, 255, 255, .45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-card-actions a:first-child {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-empty-state {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
border: 1px dashed var(--wiki-border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #fff;
|
||||||
|
color: var(--wiki-muted);
|
||||||
|
text-align: center;
|
||||||
|
padding: 24px 12px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.wiki-main .wiki-toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiki-main .wiki-toolbar-left {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$( function()
|
$( function()
|
||||||
{
|
{
|
||||||
$( 'body' ).on( 'click', '.category-delete', function(e) {
|
function updateBulkState() {
|
||||||
e.preventDefault();
|
var selected = $( '.wiki-select-one:checked' ).length;
|
||||||
var href = $( this ).attr( 'href' );
|
$( '#wiki-selected-count-value' ).text( selected );
|
||||||
|
$( '#wiki-bulk-delete-btn' ).prop( 'disabled', selected === 0 );
|
||||||
|
|
||||||
|
var all_count = $( '.wiki-select-one' ).length;
|
||||||
|
var all_selected = all_count > 0 && selected === all_count;
|
||||||
|
$( '#wiki-select-all' ).prop( 'checked', all_selected );
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmRedirect( href, content_text ) {
|
||||||
$.confirm({
|
$.confirm({
|
||||||
title: 'Potwierdź',
|
title: 'Potwierdź',
|
||||||
type: 'orange',
|
type: 'orange',
|
||||||
columnClass: 'col-md-8 col-md-offset-2 col-12',
|
columnClass: 'col-md-8 col-md-offset-2 col-12',
|
||||||
closeIcon: true,
|
closeIcon: true,
|
||||||
closeIconClass: 'fa fa-close',
|
closeIconClass: 'fa fa-close',
|
||||||
content: 'Na pewno chcesz usunąć wybrany wpis?',
|
content: content_text,
|
||||||
theme: 'modern',
|
theme: 'modern',
|
||||||
buttons: {
|
buttons: {
|
||||||
submit: {
|
submit: {
|
||||||
text: '<i class="fa fa-check"></i>Zatwierdź',
|
text: '<i class="fa fa-check"></i>Zatwierdź',
|
||||||
btnClass: 'btn-success',
|
btnClass: 'btn-success',
|
||||||
action: function () {
|
action: function () {
|
||||||
document.location.href = href;
|
document.location.href = href;
|
||||||
@@ -74,36 +306,76 @@
|
|||||||
cancel: {
|
cancel: {
|
||||||
text: '<i class="fa fa-close"></i>Anuluj',
|
text: '<i class="fa fa-close"></i>Anuluj',
|
||||||
btnClass: 'btn-danger',
|
btnClass: 'btn-danger',
|
||||||
keys: ['enter'],
|
keys: [ 'enter' ],
|
||||||
|
action: function() {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$( 'body' ).on( 'click', '.category-delete', function( e ) {
|
||||||
|
e.preventDefault();
|
||||||
|
confirmRedirect( $( this ).attr( 'href' ), 'Na pewno chcesz usunąć wybrany wpis?' );
|
||||||
|
});
|
||||||
|
|
||||||
|
$( '#wiki-select-all' ).on( 'change', function() {
|
||||||
|
var is_checked = $( this ).is( ':checked' );
|
||||||
|
$( '.wiki-select-one' ).prop( 'checked', is_checked );
|
||||||
|
updateBulkState();
|
||||||
|
});
|
||||||
|
|
||||||
|
$( 'body' ).on( 'change', '.wiki-select-one', function() {
|
||||||
|
updateBulkState();
|
||||||
|
});
|
||||||
|
|
||||||
|
$( '#wiki-bulk-delete-btn' ).on( 'click', function() {
|
||||||
|
var selected = $( '.wiki-select-one:checked' ).length;
|
||||||
|
if ( selected <= 0 )
|
||||||
|
{
|
||||||
|
$.alert( 'Najpierw zaznacz wpisy do usunięcia.' );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.confirm({
|
||||||
|
title: 'Potwierdź',
|
||||||
|
type: 'orange',
|
||||||
|
columnClass: 'col-md-8 col-md-offset-2 col-12',
|
||||||
|
closeIcon: true,
|
||||||
|
closeIconClass: 'fa fa-close',
|
||||||
|
content: 'Na pewno chcesz usunąć zaznaczone wpisy (' + selected + ')?',
|
||||||
|
theme: 'modern',
|
||||||
|
buttons: {
|
||||||
|
submit: {
|
||||||
|
text: '<i class="fa fa-check"></i>Zatwierdź',
|
||||||
|
btnClass: 'btn-success',
|
||||||
|
action: function () {
|
||||||
|
$( '#wiki-bulk-delete-form' ).trigger( 'submit' );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cancel: {
|
||||||
|
text: '<i class="fa fa-close"></i>Anuluj',
|
||||||
|
btnClass: 'btn-danger',
|
||||||
|
keys: [ 'enter' ],
|
||||||
action: function() {}
|
action: function() {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
var timer = '';
|
var timer = null;
|
||||||
$( '#search-name' ).keyup( function()
|
$( '#wiki-search-name' ).on( 'keyup', function()
|
||||||
{
|
{
|
||||||
var _this = $( this);
|
var phrase = String( $( this ).val() || '' ).toLowerCase().trim();
|
||||||
var category = _this.val();
|
|
||||||
clearTimeout( timer );
|
clearTimeout( timer );
|
||||||
timer = setTimeout( function()
|
timer = setTimeout( function() {
|
||||||
{
|
$( '.wiki-card' ).each( function() {
|
||||||
category = category.toLowerCase();
|
var card = $( this );
|
||||||
|
var name = String( card.attr( 'data-name' ) || '' );
|
||||||
$( '.category' ).hide();
|
card.toggle( phrase === '' || name.indexOf( phrase ) >= 0 );
|
||||||
$( ".category .title a" ).each( function ( index )
|
|
||||||
{
|
|
||||||
var text = $( this ).text();
|
|
||||||
text = text.trim().toLowerCase(); console.log( text );
|
|
||||||
|
|
||||||
if ( text.indexOf( category ) >= 0 )
|
|
||||||
$( this ).parents( '.category' ).show();
|
|
||||||
});
|
});
|
||||||
|
}, 180 );
|
||||||
if ( category == '' )
|
|
||||||
$( '.category' ).show();
|
|
||||||
}, 500 );
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateBulkState();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
Reference in New Issue
Block a user