Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues/209 #211

Merged
merged 4 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#### Added

- New shortcode attribute `layout` to specify a custom layout for the `[ptc_asana_project]` shortcode. Note this is only useful for extenders and third-party customizations. Completionist always displays projects in `list` layout by default.
- New shortcode attribute `sort_tasks_by` to sort tasks by the specified field. Common values are `due_on`, `assignee`, `completed_at`, and `name`. Unlike Asana, subtasks are also sorted.
- `.pdf` video attachments on tasks are now supported.
- External attachments (eg. Vimeo and YouTube embeds) are now displayed as their oEmbed HTML representations when available.

Expand Down
106 changes: 105 additions & 1 deletion src/includes/class-asana-interface.php
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,7 @@ public static function get_project_data(
'show_tasks_due' => true,
'show_tasks_attachments' => true,
'show_tasks_tags' => true,
'sort_tasks_by' => '',
)
);

Expand Down Expand Up @@ -835,7 +836,7 @@ public static function get_project_data(
$task_fields .= ',html_notes';
}
if ( $args['show_tasks_assignee'] ) {
$task_fields .= ',assignee,this.assignee.name,this.assignee.photo.image_36x36';
$task_fields .= ',assignee,assignee.name,assignee.photo.image_36x36';
}
if ( $args['show_tasks_due'] ) {
$task_fields .= ',due_on';
Expand Down Expand Up @@ -867,6 +868,20 @@ public static function get_project_data(
$args
);

$do_remove_tasks_sort_field = false;
if (
$args['sort_tasks_by'] &&
false === in_array(
$args['sort_tasks_by'],
explode( ',', $task_fields )
)
) {
// Ensure sorting field is returned.
// Always add "name" subfield in case its an object.
$task_fields .= ",{$args['sort_tasks_by']},{$args['sort_tasks_by']}.name";
$do_remove_tasks_sort_field = true;
}

$tasks = $asana->tasks->getTasksForProject(
$project_gid,
$task_request_params,
Expand All @@ -889,6 +904,10 @@ public static function get_project_data(
self::load_subtasks( $tasks, $subtask_fields );
}

if ( $args['sort_tasks_by'] ) {
static::sort_tasks_by( $tasks, $args['sort_tasks_by'] );
}

// Clean data and map tasks to project sections.

foreach ( $tasks as &$task ) {
Expand Down Expand Up @@ -931,6 +950,12 @@ public static function get_project_data(
if ( ! $args['show_tasks_completed'] ) {
$task->subtasks = array_values( $task->subtasks );
}

// Asana doesn't currently sort subtasks when the
// view's sort is changed, but we will.
if ( $args['sort_tasks_by'] ) {
static::sort_tasks_by( $task->subtasks, $args['sort_tasks_by'] );
}
}

// Clone in case the task appears in another membership.
Expand All @@ -942,6 +967,14 @@ public static function get_project_data(
}
}
}

if (
$args['sort_tasks_by'] &&
true === $do_remove_tasks_sort_field
) {
// Remove extra field only used for sorting, not for display.
Util::deep_unset_prop( $project, $args['sort_tasks_by'] );
}
}

// Commit all buffered request tokens.
Expand Down Expand Up @@ -972,6 +1005,77 @@ public static function get_project_data(
return $project;
}

/**
* Sorts tasks by the given field.
*
* @since [unreleased]
*
* @param \stdClass[] $tasks The tasks to be sorted.
* @param string $sort_field The task attribute to sort tasks by.
*/
public static function sort_tasks_by( array &$tasks, string $sort_field ) {
usort(
$tasks,
function ( $task1, $task2 ) use ( $sort_field ) {

// Ensure the specified field exists in both tasks.
if (
isset( $task1->{$sort_field} ) &&
isset( $task2->{$sort_field} )
) {

$value1 = &$task1->{$sort_field};
$value2 = &$task2->{$sort_field};

if ( is_bool( $value1 ) && is_bool( $value2 ) ) {
if (
true === $value1 &&
true === $value2 &&
isset( $task1->name ) &&
isset( $task2->name )
) {
// If both true, sort alphabetically by task name.
return strcmp( $task1->name, $task2->name );
}
// Sort true values first.
return $value2 - $value1;
} elseif ( is_object( $value1 ) && is_object( $value2 ) ) {
if (
isset( $value1->name ) &&
isset( $value2->name )
) {
// Sort by the objects' name fields, such as "assignee".
return strcmp( $value1->name, $value2->name );
}
// Don't know how to sort by object.
return 0;
} elseif ( is_numeric( $value1 ) && is_numeric( $value2 ) ) {
return $value1 - $value2; // Numeric comparison.
} elseif ( is_string( $value1 ) && is_string( $value2 ) ) {
return strcmp( $value1, $value2 ); // String comparison.
} else {
return 0; // Not sure how to sort.
}
} elseif (
isset( $task1->{$sort_field} ) &&
! isset( $task2->{$sort_field} )
) {
// If the first task has the field, then put it first.
return -1;
} elseif (
! isset( $task1->{$sort_field} ) &&
isset( $task2->{$sort_field} )
) {
// If the second task has the field, then put it first.
return 1;
}

// No opinion when both tasks are missing the specified field.
return 0;
}
);
}

/**
* Sanitizes, localizes, and tidies a task object.
*
Expand Down
1 change: 1 addition & 0 deletions src/public/class-shortcodes.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class Shortcodes {
'show_tasks_due' => 'true',
'show_tasks_attachments' => 'true',
'show_tasks_tags' => 'true',
'sort_tasks_by' => '',
),
'script_handles' => array(
'ptc-completionist-shortcode-asana-project',
Expand Down
Loading