Skip to content

Commit

Permalink
Merge pull request #211 from /issues/209
Browse files Browse the repository at this point in the history
Issues/209
  • Loading branch information
MichelleBlanchette authored Jan 15, 2024
2 parents 366fff3 + 4b30dc3 commit 4942883
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 1 deletion.
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

0 comments on commit 4942883

Please sign in to comment.