Integration of Project Tasks to the Activities Calendar

bsoremsugar —  November 29, 2011 — 2 Comments

Editor’s Note: This post comes from Matthew Frost of Highland Solutions, a SugarCRM Gold Partner in Chicago. The origins of this post come from a Twitter exchange a few weeks back talking about doing an integration of Project Tasks to the Activities Calendar in Sugar. He kindly volunteered to do a writeup on his solution to this problem, which works with the newly released Sugar 6.3.

The activities calendar was built to display the 3 modules that fall under the Activities umbrella; calls, meetings, and tasks. The one area that’s handled a bit differently is Project Tasks, because they are integrated under the Projects package along with Project Resources, Project Holidays, etc. They are viewable via the Gantt View in the Project, but what happens when you want your project tasks to show up on the calendar with your other activities. You could convert all your project tasks to tasks via a logic hook, and that would probably work, assuming you closed the project task when the corresponding task was closed. I have made some modifications to the Calendar class that allows you to pull in the Project Tasks the same way the other Activities are pulled in.

If you open your Calendar.php file, you’ll notice there are 3 classes defined in this file: Calendar, Slice, and CalendarActivity. The CalendarActivity constructor is where we are going to make the first change. You’ll see a conditional that says: if($sugar_bean->object_name == ‘Task’). If you read through that block you’ll see there’s an else. That’s because Meetings/Calls have a time element that goes along with the start date and end date. A task is represented as a daily item, something that is due on a certain date and the time isn’t as important. The other big thing to take into account here is that in the Activity beans, there a is date formatting happening to give the date a ‘m/d/Y g:ia’ – this doesn’t happen in Project Tasks, it comes out as Y-m-d H:i:s or MySQL datetime format. So you’ll need to add an elseif($sugar_bean->object_name == “ProjectTask”). There is a timedate instance here that is used to set the start and end dates of the task. The code for these conditions is below:

if ($sugar_bean->object_name == 'Task') {
    $this->start_time = $timedate->fromUser($this->sugar_bean->date_due);
    if ( empty($this->start_time)) {
        return null;
    }

    $this->end_time = $timedate->fromUser($this->sugar_bean->date_due);
}
elseif($sugar_bean->object_name == "ProjectTask") {
    $this->start_time = $timedate->fromUser(date('m/d/Y g:ia',strtotime($this->sugar_bean->date_start . "10:00:00")));

    if ( empty($this->start_time)){
        return null;
    }
    $this->end_time = $timedate->fromUser(date('m/d/Y g:ia',strtotime($this->sugar_bean->date_finish . " 23:59:59")));
}
else {

As I mentioned, you’ll want to leave the else code alone for calls and meetings, but what I did here was take the date_start and date_end fields and convert them to the bean format and placed them in the fromUser method. If $this->start_date is not a TimeDate object, the code will not work, so don’t think you can just set $this->start_date = date(‘m/d/Y g:ia’,strtotime($bean->start_date), it will not work.

As far as the constructor, that’s all you really need to modify, I went into some detail there. It’s an important step though.

The get_activities method is our next victim…again this is broken out very nicely into conditionals for each type of Activity. There’s no Project Tasks block, so we’ll create one. I copied the calls block and changed all the appropriate objects from Call to ProjectTask(); There’s some references you’ll need to change, depending on if you change the variable names to correspond with the new object type. I did just to make it clear for future reference. Now, this is the part where the “where” part of the query is built, one of the other major differences with ProjectTasks and the activities is the use of the users tables. This is what gives the Calls and Meetings the Many to Many functionality. Tasks, again as you’d expect are handled a little bit differently, tasks utilize the get_full_list method. Here’s where we’re going to edit the next file {CRMROOT/include/utils/activity_utils.php}. I copied the build_related_list_by_user_id function and created the following:

function project_tasks_query_format($bean, $user_id,$where) {
    $bean_id_name = strtolower($bean->object_name).'_id';

    $select = "SELECT {$bean->table_name}.* from {$bean->table_name} ";

    $auto_where = ' WHERE ';
    if(!empty($where)) {
        $auto_where .= $where. ' AND ';
    }

    $auto_where .= " {$bean->table_name}.resource_id='{$user_id}' AND {$bean->table_name}.deleted=0 ";

    $bean->add_team_security_where_clause($select);

    $query = $select.$auto_where;

    $result = $bean->db->query($query, true);

    $list = array();

    while($row = $bean->db->fetchByAssoc($result)) {
        foreach($bean->column_fields as $field) {
            if(isset($row[$field])) {
                $bean->$field = $row[$field];
            } else {
                $bean->$field = '';
            }
        }

        $bean->processed_dates_times = array();
        $bean->check_date_relationships_load();
        $bean->fill_in_additional_detail_fields();

        /**
         * PHP  5+ always treats objects as passed by reference
         * Need to clone it if we're using 5.0+
         * clone() not supported by 4.x
         */
        if(version_compare(phpversion(), "5.0", ">=")) {
            $newBean = clone($bean);
        } else {
            $newBean = $bean;
        }
        $list[] = $newBean;
    }
    return $list;
}

I implement it into the calendar code this way:

        if(ACLController::checkAccess('Project Tasks', 'list',$current_user->id  == $user_id)) {
            $projectTask = new ProjectTask();

            if($current_user->id  == $user_id) {
                $projectTask->disable_row_level_security = true;
            }

            $where = CalendarActivity::get_occurs_within_where_clause($projectTask->table_name, '', $view_start_time, $view_end_time, 'date_start', $view);

            $focus_project_task_list = project_tasks_query_format($projectTask,$user_id,$where);

            foreach($focus_project_task_list as $pTask) {
                if(isset($seen_ids[$pTask->id])) {
                    continue;
                }
                $seen_ids[$pTask->id] = 1;

                $act = new CalendarActivity($pTask);

                if(!empty($act)) {
                    $act_list[] = $act;
                }
            }
        }

        usort($act_list,'sort_func_by_act_date');
        return $act_list;
    }
}

This is where you’ll get an error thrown if you don’t have the datetime object, it’ll be on the creation of the $act_list array. You’ll see I changed the build_related_list_by_user_id function to project_tasks_query_format and I pass the same arguments. So at this point, you have an act_list array with Calls,Meetings,Tasks, and Project Tasks. The last part is getting them to show up on the calendar.

Go to your templates folder in the Calendar module, there’s the template_echo_slice_activities function and the template_echo_slice_activities_shared function, this is more or less just another conditional where you have to provide some instructions for the ProjectTask object. This is where you format the link that appears on the calendar making sure you change to module=ProjectTask. I used the tasks icon, this is pretty much the same change though on both functions, below is the shared version.
I’ve included the conditional for task too for reference.

} else if($act->sugar_bean->object_name == 'Task') {
    echo "<td>".
    SugarThemeRegistry::current()->getImage('Tasks','alt="'.$app_list_strings['task_status_dom'][$act->sugar_bean->status].': '.$act->sugar_bean->name.'"');
    echo "</td>";

    if(empty($act->sugar_bean->name)) {
        echo "<td width="100%">".
            $timedate->getTimePart($act->sugar_bean->date_due);
            echo "</td></tr>";
        } else {
            echo "<td width="100%">
            <a href="index.php?module=Tasks&action=DetailView&record=".$act->sugar_bean->id."">".
            $app_list_strings['task_status_dom'][$act->sugar_bean->status].': '.$act->sugar_bean-                    >name."<br>(".

            $timedate->getTimePart($act->sugar_bean->date_due).")</a></td></tr>";
        }
}else if($act->sugar_bean->object_name == 'ProjectTask') {
    echo "<td>". SugarThemeRegistry::current()->getImage('Tasks','alt="'.$app_list_strings['task_status_dom'][$act-        >sugar_bean->status].': '.$act->sugar_bean->name.'"');
    echo "</td>";

    if(empty($act->sugar_bean->name)) {
        echo "<td width="100%">".
        $timedate->getTimePart($act->sugar_bean->date_due);
        echo "</td></tr>";
    } else {
        echo "<td width="100%">
        <a href="index.php?module=ProjectTask&action=DetailView&record=".$act->sugar_bean->id."">". $act->sugar_bean->name ."</a></td></tr>";
    }
}

I’m sure there are some things that I missed, we’ve not gone through the some of the display things with the client yet, so I’m sure there are probably some things in this code that would need to be corrected (for instance the time/date on the hover of the project task) I just didn’t have time to do that yet.. The downside to this is that it’s not upgrade safe, to my knowledge the calendar just wasn’t created that way…so if you upgrade, you’ll have to do this again (and again that’s all dependent on what changes if any are made to the calendar, as far as I can tell, there haven’t been many). If Sugar does address this, my guess would be that it would take this functionality into account and implement it in a new version. There really aren’t many code changes here though, it’s much quicker to implement than it was to figure it all out, but there’s nothing here that should be surprising. Plus going through this will give you a pretty solid idea on how the Activities modules were built and how the calendar is put together.

2 responses to Integration of Project Tasks to the Activities Calendar

  1. 

    I’m not a “Programmer” (I’m just a self taught code “editor”)as such, I am able to edit code to achieve a goal with clear instructions. That being said, I would really like to be able to see the “Project Tasks” in the “Calendar” and I really appreciate any help I can get help with this.

    I am currently working with a version; (6.5.16) Community Edition. The file content differs from that above. Can someone perhaps guide me in the right direction to add the “Project Tasks” to the “Calendar”?

    Thank you in advance for your kind consideration.

  2. 

    I can’t find where to put the last codes.. Any help is highly appreciated.. :D thanks

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s