function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
EMHDevEMHDev 

Task list controller - possible?

I'm new to VF, coming from scontrols.  I have some custom objects related to Opportunities which can each have tasks related to them.  The salesguys want to see one list of tasks, no matter which of the custom objects or indeed the Opportunity itself they are related to.  I've been trying to write a list controller for tasks with no luck at all.  I know there is no standard controller for listing tasks but is there any reason why a custom controller would not work?  Does anyone have some template code I could use as I'm new to Apex as well and I can't get my code accepted by the IDE?

Apex Code is as follows (still needs the query to be filtered but am trying to get proof of concept first):

 

public class OppTasks { public ApexPages.StandardSetController setCon { get { if(setCon == null) { setCon = new ApexPages.StandardSetController(Database.getQueryLocator([Select WhatId, Subject, Status, OwnerId, Id, Description, ActivityDate From Task])); } return setCon; } set; } public List<Task> getActivities() { return (List<Task>) setCon.getRecords(); } }

 Visualforce page:

 

<apex:page controller="OppTasks"> <apex:form > <apex:pageblock id="thePage"> <apex:pageblocktable value="{!activity}" var="t" > <apex:column value="{!t.Subject}"/> <apex:column value="{!t.WhatId}"/> <apex:column value="{!t.Status}"/> <apex:column value="{!t.ActivityDate}"/> </apex:pageblocktable> </apex:pageblock> </apex:form> </apex:page>

 I have tried a variety of values  including {!tasks}, I get the error "Unknown property OppTasks.activity".

 

Can anyone help a newbie?  Many thanks - Erica

 

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
lamayclamayc

All the commented stuff is ideas that I had that didn't seem to work, so you can remove them.

As you can also see, my plan was/is to put this in an abstract base class, so that sub-classes can use this same functionality by just using a variable (say customSQL) that will be the input to the SOQL expression in the getTasks method (willl have to use Database.Query (I believe that's the call)).

For anyone who is interested, I re-route the save task to a VF page (saveComplete) which just says "saving ... wait while next record loads." And then the Ajax stuff hits and automatically loads the next task. When all tasks are completed, the whole page is refreshed (code that I have not listed -- but parent.refreshPage() should give an idea -- yes I had to do it all in iframes because I couldn't figure out a better way with the current code base we have). Anyway, the idea being that it pushes tasks (events, cases, what have you) to people instead of them having to select them (prevent them from cherry-picking their tasks and giving everyone else the crappy one :)

 

 

 

public class DashboardTaskController { //extends DashboardAbstractController {

//private String dt = null;
public String recordType {get; set;}
public String teamLeaderId {get; set;}
public String objectId {get; set;}
//public Task currentObject {get; set;}
//public String updatedItemStatus{get; set;}
public Integer numLeft {get; set;}
public Integer total {get; set;}
private Integer startNdx = 0;
private static Integer PAGESIZE = 3;
private List<Task> fullTaskList = new List<Task>();
private List<Task> displayedTaskList = new List<Task>();
//private String prefetchedNextObjectId;

public DashboardTaskController() {
// dt means done today
//this.dt = ApexPages.currentPage().getParameters().get('dt');
recordType = ApexPages.currentPage().getParameters().get('recordType');
//this.teamLeaderId = ApexPages.currentPage().getParameters().get('teamid');
//customQuery = Database.getQueryLocator([select name, id, closedate, stageName from Opportunity where stageName = :qualifier]);
}

public PageReference getDetailPage() {
getTasks();
System.debug('getting detail page for object with id : ' + this.objectId);
PageReference pg;
if (this.objectId != null) {
for (Task t : fullTaskList) {
if (this.objectId.equals(t.id)) {
pg = new ApexPages.StandardController(t).edit();
}
}
//pg = new ApexPages.StandardController(currentObject).edit();
pg.getParameters().put('saveURL', '/apex/saveComplete');
//get next open item to use as page to show up after saving
/*Task nextObject = nextTask();
this.prefetchedNextObjectId = nextObject.id;
PageReference nextPg = new ApexPages.StandardController(nextObject).edit();
pg.getParameters().put('saveURL', nextPg.getUrl());*/
pg.getParameters().put('isdtp','mn');
pg.getParameters().put('nooverride', '1');
}

System.debug('returning : ' + pg);
return pg;
}

public List<Task> getTasks() {
if (fullTaskList.isEmpty()) {
fullTaskList = [select id, subject, status, isClosed from Task where RecordType.Name = :recordType and isClosed = false];
numLeft = fullTaskList.size();
total = numLeft;
this.objectId = ((Task) fullTaskList[0]).id;
//this.currentObject = (Task) fullTaskList[0];
}

displayedTaskList.clear();
Integer endNdx = startNdx + PAGESIZE;
if (endNdx > total)
endNdx = total;

for (Integer i=startNdx; i<endNdx; i++)
displayedTaskList.add(fullTaskList.get(i));

return displayedTaskList;
}

private void updateTaskStatus() {
System.debug('before : ' + fullTaskList);
Integer i = 0;
for (i=0; i<fullTaskList.size(); i++) {
Task t = fullTaskList.get(i);
if (this.objectId.equals(t.id)) {
System.debug('updating status of ' + t);
Task tmp = [select id, subject, status, isClosed from Task where id = :t.id];
fullTaskList.set(i, tmp);
System.debug('updated to ' + tmp);
//this.updatedItemStatus = tmp.status;
break;
}
}

System.debug('after : ' + fullTaskList);
}

/*private Task nextTask() {
System.debug('currently using object : ' + this.objectId);
Task retVal;
for (Task t : fullTaskList) {
System.debug('looking at object with id ' + t.id);
if (!t.isClosed && !this.objectId.equals(t.id)) {
System.debug('found non-closed object with id ' + t.id);
retVal = t;
break;
}
}

return retVal;
}*/

private void nextTask() {
for (Task t : fullTaskList) {
if (!t.isClosed) {
System.debug('found non-closed object with id ' + t.id);
this.objectId = t.id;
break;
}
}
}

/*private void nextTask() {
//System.debug('currently using object : ' + this.currentObject);
//Task retVal;
for (Task t : fullTaskList) {
System.debug('looking at object ' + t);
if (!t.isClosed) {
System.debug('found non-closed object');
this.currentObject = t;
break;
}
}

//return retVal;
} */

public PageReference showDetail() {
// since I can't get the assignTo to work in the VF page/component
this.objectId = System.currentPageReference().getParameters().get('objectId');

/*
bizarre ... if I call setObjectId, the id returned from the detailPage method is null
but if I directly set the id in this method, everything works correctly
setObjectId(System.currentPageReference().getParameters().get('objectId'));
*/
return null;
}

public void previous() {
startNdx -= PAGESIZE;
}

public void next() {
startNdx += PAGESIZE;
}

public void refreshNumbers() {
// the only time this method should be called is when a submit is done
// on a record -- we assume the submit closes the record (or at least
// puts the record in a state where we are not interested in seeing
// it in our todo list anymore
updateTaskStatus();
nextTask();
this.numLeft = 0;
for (Task t : fullTaskList) {
if (!t.isClosed) {
this.numLeft++;
}
}
//this.objectId = prefetchedNextObjectId;
}

public Boolean getHasNext() {
return total > (startNdx + PAGESIZE);
}

public Boolean getHasPrevious() {
return startNdx > 0;
}

public Integer getNum() {
return total;
}
}

 

 

<apex:page controller="DashboardTaskController" standardStylesheets="true" showHeader="false">
<!-- close date will be null since we will only display open ones ...
but might want to be more flexible here so management view will look different ... see class -->
{!recordType}---{!num}
<apex:form id="taskList">
<script>
saved = 0;

function refreshToDoNumbers() {
//alert('checking : ' + saved);
if (saved == 1) {
saved = 0;
refreshNumbers();
}
}

function checkIfNeedRefresh() {
//alert('checking for full refresh');
var left = document.getElementById('{!$Component.taskList.numberOfItemsLeft}');
//alert('found items left todo : ' + left.innerHTML);
var tmp = parseInt(left.innerHTML);
if (tmp == NaN || tmp == 0) {
parent.refreshPage();
}
}

function taskSaved() {
saved = 1;
}
</script>
<apex:pageBlock id="pageBlock">
<apex:pageBlockTable value="{!tasks}" var="o" id="table">
<apex:column >
<apex:commandLink action="{!showDetail}" value="{!o.subject}" reRender="detail">
<apex:param value="{!o.id}" name="objectId" assignTo="{!objectId}"></apex:param>
</apex:commandLink>
</apex:column>
<apex:column value="{!o.subject}"/>
<apex:column id="status" value="{!o.status}"/>
</apex:pageBlockTable>
</apex:pageBlock>
<apex:panelGrid columns="2">
<apex:commandLink action="{!previous}" rendered="{!hasPrevious}">Previous</apex:commandlink>
<apex:commandLink action="{!next}" rendered="{!hasNext}">Next</apex:commandlink>
</apex:panelGrid>
<apex:outputText id="numberOfItemsLeft" value="{!numLeft}" style="visibility:hidden"/>
<apex:actionFunction action="{!refreshNumbers}" immediate="true" name="refreshNumbers" rerender="numberOfItemsLeft, detail, table" oncomplete="checkIfNeedRefresh();"/>
</apex:form>

<apex:outputPanel layout="block" id="detail">
<!--unfortunately can't use apex:iframe since onload is not supported-->
<!--apex:iframe rendered="{!linkSelected}" id="editFrame" scrolling="no" src="{!detailPage}" height="3000" frameborder="true">apex:iframe-->
<iframe onload="addOnCompleteEvent();" id="editFrame" scrolling="no" src="{!detailPage}" height="3000" width="100%" frameborder="true"/>
<script type="text/javascript">
function addOnCompleteEvent() {
// first see if all items complete and so need to refresh the page
refreshToDoNumbers();

// find the save button(s) on the edit form and add the oncomplete event to them
var editFrame = window.frames[0];
var inputs = editFrame.document.getElementsByTagName('input');
for (i=0; i<inputs.length; i++) {
if (inputs[i].type == 'submit' && inputs[i].name == 'save') {
inputs[i].setAttribute("onclick", "parent.taskSaved()");
}
}
}
</script>
</apex:outputPanel>
</apex:page>

 

All Answers

lamayclamayc

probably does more than you need, but you can cut what you don't want

 

public class DashboardTaskController {

 

//private String dt = null;

public String recordType {get; set;}

public String teamLeaderId {get; set;}

public String objectId {get; set;}

//public Task currentObject {get; set;}

//public String updatedItemStatus{get; set;}

public Integer numLeft {get; set;}

public Integer total {get; set;}

private Integer startNdx = 0;

private static Integer PAGESIZE = 3;

private List<Task> fullTaskList = new List<Task>();

private List<Task> displayedTaskList = new List<Task>();

//private String prefetchedNextObjectId;

 

public DashboardTaskController() {

// dt means done today

//this.dt = ApexPages.currentPage().getParameters().get('dt');

recordType = ApexPages.currentPage().getParameters().get('recordType');

//this.teamLeaderId = ApexPages.currentPage().getParameters().get('teamid');

//customQuery = Database.getQueryLocator([select name, id, closedate, stageName from Opportunity where stageName = :qualifier]);

}

 

public PageReference getDetailPage() {

getTasks();

System.debug('getting detail page for object with id : ' + this.objectId);

PageReference pg;

if (this.objectId != null) {

for (Task t : fullTaskList) {

if (this.objectId.equals(t.id)) {

pg = new ApexPages.StandardController(t).edit();

}

}

//pg = new ApexPages.StandardController(currentObject).edit();

pg.getParameters().put('saveURL', '/apex/saveComplete');

//get next open item to use as page to show up after saving

/*Task nextObject = nextTask();

this.prefetchedNextObjectId = nextObject.id;

PageReference nextPg = new ApexPages.StandardController(nextObject).edit();

pg.getParameters().put('saveURL', nextPg.getUrl());*/

pg.getParameters().put('isdtp','mn');

pg.getParameters().put('nooverride', '1');

}

 

System.debug('returning : ' + pg);

return pg;

}

 

public List<Task> getTasks() {

if (fullTaskList.isEmpty()) {

fullTaskList = [select id, subject, status, isClosed from Task where RecordType.Name = :recordType and isClosed = false];

numLeft = fullTaskList.size();

total = numLeft;

this.objectId = ((Task) fullTaskList[0]).id;

//this.currentObject = (Task) fullTaskList[0];

}

 

displayedTaskList.clear();

Integer endNdx = startNdx + PAGESIZE;

if (endNdx > total)

endNdx = total;

 

for (Integer i=startNdx; i<endNdx; i++)

displayedTaskList.add(fullTaskList.get(i));

 

return displayedTaskList;

}

 

private void updateTaskStatus() {

System.debug('before : ' + fullTaskList);

Integer i = 0;

for (i=0; i<fullTaskList.size(); i++) {

Task t = fullTaskList.get(i);

if (this.objectId.equals(t.id)) {

System.debug('updating status of ' + t);

Task tmp = [select id, subject, status, isClosed from Task where id = :t.id];

fullTaskList.set(i, tmp);

System.debug('updated to ' + tmp);

//this.updatedItemStatus = tmp.status;

break;

}

}

 

System.debug('after : ' + fullTaskList);

}

 

/*private Task nextTask() {

System.debug('currently using object : ' + this.objectId);

Task retVal;

for (Task t : fullTaskList) {

System.debug('looking at object with id ' + t.id);

if (!t.isClosed && !this.objectId.equals(t.id)) {

System.debug('found non-closed object with id ' + t.id);

retVal = t;

break;

}

}

 

return retVal;

}*/

private void nextTask() {

for (Task t : fullTaskList) {

if (!t.isClosed) {

System.debug('found non-closed object with id ' + t.id);

this.objectId = t.id;

break;

}

}

}

 

/*private void nextTask() {

//System.debug('currently using object : ' + this.currentObject);

//Task retVal;

for (Task t : fullTaskList) {

System.debug('looking at object ' + t);

if (!t.isClosed) {

System.debug('found non-closed object');

this.currentObject = t;

break;

}

}

 

//return retVal;

} */

 

public PageReference showDetail() {

// since I can't get the assignTo to work in the VF page/component

this.objectId = System.currentPageReference().getParameters().get('objectId');

/*

bizarre ... if I call setObjectId, the id returned from the detailPage method is null

but if I directly set the id in this method, everything works correctly

setObjectId(System.currentPageReference().getParameters().get('objectId'));

*/

return null;

}

public void previous() {

startNdx -= PAGESIZE;

}

 

public void next() {

startNdx += PAGESIZE;

}

 

public void refreshNumbers() {

// the only time this method should be called is when a submit is done

// on a record -- we assume the submit closes the record (or at least

// puts the record in a state where we are not interested in seeing

// it in our todo list anymore

updateTaskStatus();

nextTask();

this.numLeft = 0;

for (Task t : fullTaskList) {

if (!t.isClosed) {

this.numLeft++;

}

}

//this.objectId = prefetchedNextObjectId;

}

 

public Boolean getHasNext() {

return total > (startNdx + PAGESIZE);

}

 

public Boolean getHasPrevious() {

return startNdx > 0;

}

public Integer getNum() {

return total;

}

}

 

 

 

 

<apex:page controller="DashboardTaskController" standardStylesheets="true" showHeader="false">

<!-- close date will be null since we will only display open ones ...

but might want to be more flexible here so management view will look different ... see class -->

{!recordType}---{!num}

<apex:form id="taskList">

<script>

saved = 0;

 

function refreshToDoNumbers() {

//alert('checking : ' + saved);

if (saved == 1) {

saved = 0;

refreshNumbers();

}

}

 

function checkIfNeedRefresh() {

//alert('checking for full refresh');

var left = document.getElementById('{!$Component.taskList.numberOfItemsLeft}');

//alert('found items left todo : ' + left.innerHTML);

var tmp = parseInt(left.innerHTML);

if (tmp == NaN || tmp == 0) {

parent.refreshPage();

}

}

 

function taskSaved() {

saved = 1;

}

</script>

<apex:pageBlock id="pageBlock">

<apex:pageBlockTable value="{!tasks}" var="o" id="table">

<apex:column >

<apex:commandLink action="{!showDetail}" value="{!o.subject}" reRender="detail">

<apex:param value="{!o.id}" name="objectId" assignTo="{!objectId}"></apex:param>

</apex:commandLink>

</apex:column>

<apex:column value="{!o.subject}"/>

<apex:column id="status" value="{!o.status}"/>

</apex:pageBlockTable>

</apex:pageBlock>

<apex:panelGrid columns="2">

<apex:commandLink action="{!previous}" rendered="{!hasPrevious}">Previous</apex:commandlink>

<apex:commandLink action="{!next}" rendered="{!hasNext}">Next</apex:commandlink>

</apex:panelGrid>

<apex:outputText id="numberOfItemsLeft" value="{!numLeft}" style="visibility:hidden"/>

<apex:actionFunction action="{!refreshNumbers}" immediate="true" name="refreshNumbers" rerender="numberOfItemsLeft, detail, table" oncomplete="checkIfNeedRefresh();"/>

</apex:form>

<apex:outputPanel layout="block" id="detail">

<!--unfortunately can't use apex:iframe since onload is not supported-->

<!--apex:iframe rendered="{!linkSelected}" id="editFrame" scrolling="no" src="{!detailPage}" height="3000" frameborder="true">apex:iframe-->

<iframe onload="addOnCompleteEvent();" id="editFrame" scrolling="no" src="{!detailPage}" height="3000" width="100%" frameborder="true"/>

<script type="text/javascript">

function addOnCompleteEvent() {

// first see if all items complete and so need to refresh the page

refreshToDoNumbers();

 

// find the save button(s) on the edit form and add the oncomplete event to them

var editFrame = window.frames[0];

var inputs = editFrame.document.getElementsByTagName('input');

for (i=0; i<inputs.length; i++) {

if (inputs[i].type == 'submit' && inputs[i].name == 'save') {

inputs[i].setAttribute("onclick", "parent.taskSaved()");

}

}

}

</script>

</apex:outputPanel>

</apex:page>

Ron HessRon Hess

This may be useful to other developers, could you post it again with the Insert Code button ?

 

this button looks like a clipboard with a 'C' over it. ( looks like this : )

 

thanks!

 

lamayclamayc

All the commented stuff is ideas that I had that didn't seem to work, so you can remove them.

As you can also see, my plan was/is to put this in an abstract base class, so that sub-classes can use this same functionality by just using a variable (say customSQL) that will be the input to the SOQL expression in the getTasks method (willl have to use Database.Query (I believe that's the call)).

For anyone who is interested, I re-route the save task to a VF page (saveComplete) which just says "saving ... wait while next record loads." And then the Ajax stuff hits and automatically loads the next task. When all tasks are completed, the whole page is refreshed (code that I have not listed -- but parent.refreshPage() should give an idea -- yes I had to do it all in iframes because I couldn't figure out a better way with the current code base we have). Anyway, the idea being that it pushes tasks (events, cases, what have you) to people instead of them having to select them (prevent them from cherry-picking their tasks and giving everyone else the crappy one :)

 

 

 

public class DashboardTaskController { //extends DashboardAbstractController {

//private String dt = null;
public String recordType {get; set;}
public String teamLeaderId {get; set;}
public String objectId {get; set;}
//public Task currentObject {get; set;}
//public String updatedItemStatus{get; set;}
public Integer numLeft {get; set;}
public Integer total {get; set;}
private Integer startNdx = 0;
private static Integer PAGESIZE = 3;
private List<Task> fullTaskList = new List<Task>();
private List<Task> displayedTaskList = new List<Task>();
//private String prefetchedNextObjectId;

public DashboardTaskController() {
// dt means done today
//this.dt = ApexPages.currentPage().getParameters().get('dt');
recordType = ApexPages.currentPage().getParameters().get('recordType');
//this.teamLeaderId = ApexPages.currentPage().getParameters().get('teamid');
//customQuery = Database.getQueryLocator([select name, id, closedate, stageName from Opportunity where stageName = :qualifier]);
}

public PageReference getDetailPage() {
getTasks();
System.debug('getting detail page for object with id : ' + this.objectId);
PageReference pg;
if (this.objectId != null) {
for (Task t : fullTaskList) {
if (this.objectId.equals(t.id)) {
pg = new ApexPages.StandardController(t).edit();
}
}
//pg = new ApexPages.StandardController(currentObject).edit();
pg.getParameters().put('saveURL', '/apex/saveComplete');
//get next open item to use as page to show up after saving
/*Task nextObject = nextTask();
this.prefetchedNextObjectId = nextObject.id;
PageReference nextPg = new ApexPages.StandardController(nextObject).edit();
pg.getParameters().put('saveURL', nextPg.getUrl());*/
pg.getParameters().put('isdtp','mn');
pg.getParameters().put('nooverride', '1');
}

System.debug('returning : ' + pg);
return pg;
}

public List<Task> getTasks() {
if (fullTaskList.isEmpty()) {
fullTaskList = [select id, subject, status, isClosed from Task where RecordType.Name = :recordType and isClosed = false];
numLeft = fullTaskList.size();
total = numLeft;
this.objectId = ((Task) fullTaskList[0]).id;
//this.currentObject = (Task) fullTaskList[0];
}

displayedTaskList.clear();
Integer endNdx = startNdx + PAGESIZE;
if (endNdx > total)
endNdx = total;

for (Integer i=startNdx; i<endNdx; i++)
displayedTaskList.add(fullTaskList.get(i));

return displayedTaskList;
}

private void updateTaskStatus() {
System.debug('before : ' + fullTaskList);
Integer i = 0;
for (i=0; i<fullTaskList.size(); i++) {
Task t = fullTaskList.get(i);
if (this.objectId.equals(t.id)) {
System.debug('updating status of ' + t);
Task tmp = [select id, subject, status, isClosed from Task where id = :t.id];
fullTaskList.set(i, tmp);
System.debug('updated to ' + tmp);
//this.updatedItemStatus = tmp.status;
break;
}
}

System.debug('after : ' + fullTaskList);
}

/*private Task nextTask() {
System.debug('currently using object : ' + this.objectId);
Task retVal;
for (Task t : fullTaskList) {
System.debug('looking at object with id ' + t.id);
if (!t.isClosed && !this.objectId.equals(t.id)) {
System.debug('found non-closed object with id ' + t.id);
retVal = t;
break;
}
}

return retVal;
}*/

private void nextTask() {
for (Task t : fullTaskList) {
if (!t.isClosed) {
System.debug('found non-closed object with id ' + t.id);
this.objectId = t.id;
break;
}
}
}

/*private void nextTask() {
//System.debug('currently using object : ' + this.currentObject);
//Task retVal;
for (Task t : fullTaskList) {
System.debug('looking at object ' + t);
if (!t.isClosed) {
System.debug('found non-closed object');
this.currentObject = t;
break;
}
}

//return retVal;
} */

public PageReference showDetail() {
// since I can't get the assignTo to work in the VF page/component
this.objectId = System.currentPageReference().getParameters().get('objectId');

/*
bizarre ... if I call setObjectId, the id returned from the detailPage method is null
but if I directly set the id in this method, everything works correctly
setObjectId(System.currentPageReference().getParameters().get('objectId'));
*/
return null;
}

public void previous() {
startNdx -= PAGESIZE;
}

public void next() {
startNdx += PAGESIZE;
}

public void refreshNumbers() {
// the only time this method should be called is when a submit is done
// on a record -- we assume the submit closes the record (or at least
// puts the record in a state where we are not interested in seeing
// it in our todo list anymore
updateTaskStatus();
nextTask();
this.numLeft = 0;
for (Task t : fullTaskList) {
if (!t.isClosed) {
this.numLeft++;
}
}
//this.objectId = prefetchedNextObjectId;
}

public Boolean getHasNext() {
return total > (startNdx + PAGESIZE);
}

public Boolean getHasPrevious() {
return startNdx > 0;
}

public Integer getNum() {
return total;
}
}

 

 

<apex:page controller="DashboardTaskController" standardStylesheets="true" showHeader="false">
<!-- close date will be null since we will only display open ones ...
but might want to be more flexible here so management view will look different ... see class -->
{!recordType}---{!num}
<apex:form id="taskList">
<script>
saved = 0;

function refreshToDoNumbers() {
//alert('checking : ' + saved);
if (saved == 1) {
saved = 0;
refreshNumbers();
}
}

function checkIfNeedRefresh() {
//alert('checking for full refresh');
var left = document.getElementById('{!$Component.taskList.numberOfItemsLeft}');
//alert('found items left todo : ' + left.innerHTML);
var tmp = parseInt(left.innerHTML);
if (tmp == NaN || tmp == 0) {
parent.refreshPage();
}
}

function taskSaved() {
saved = 1;
}
</script>
<apex:pageBlock id="pageBlock">
<apex:pageBlockTable value="{!tasks}" var="o" id="table">
<apex:column >
<apex:commandLink action="{!showDetail}" value="{!o.subject}" reRender="detail">
<apex:param value="{!o.id}" name="objectId" assignTo="{!objectId}"></apex:param>
</apex:commandLink>
</apex:column>
<apex:column value="{!o.subject}"/>
<apex:column id="status" value="{!o.status}"/>
</apex:pageBlockTable>
</apex:pageBlock>
<apex:panelGrid columns="2">
<apex:commandLink action="{!previous}" rendered="{!hasPrevious}">Previous</apex:commandlink>
<apex:commandLink action="{!next}" rendered="{!hasNext}">Next</apex:commandlink>
</apex:panelGrid>
<apex:outputText id="numberOfItemsLeft" value="{!numLeft}" style="visibility:hidden"/>
<apex:actionFunction action="{!refreshNumbers}" immediate="true" name="refreshNumbers" rerender="numberOfItemsLeft, detail, table" oncomplete="checkIfNeedRefresh();"/>
</apex:form>

<apex:outputPanel layout="block" id="detail">
<!--unfortunately can't use apex:iframe since onload is not supported-->
<!--apex:iframe rendered="{!linkSelected}" id="editFrame" scrolling="no" src="{!detailPage}" height="3000" frameborder="true">apex:iframe-->
<iframe onload="addOnCompleteEvent();" id="editFrame" scrolling="no" src="{!detailPage}" height="3000" width="100%" frameborder="true"/>
<script type="text/javascript">
function addOnCompleteEvent() {
// first see if all items complete and so need to refresh the page
refreshToDoNumbers();

// find the save button(s) on the edit form and add the oncomplete event to them
var editFrame = window.frames[0];
var inputs = editFrame.document.getElementsByTagName('input');
for (i=0; i<inputs.length; i++) {
if (inputs[i].type == 'submit' && inputs[i].name == 'save') {
inputs[i].setAttribute("onclick", "parent.taskSaved()");
}
}
}
</script>
</apex:outputPanel>
</apex:page>

 

This was selected as the best answer
EMHDevEMHDev

Brilliant, I'll work my way through this.  Thanks for sharing this.

 

Erica

lamayclamayc

Well, since you actually seem to be interested, I'll help you.

 

In the constructor, you can see I get the recordType (task SOQL qualifier) of the url.

 

In the getDetailPage, I iterate through my list of tasks to find one with the matching ID of the current one selected and then get the edit page for that. (I know I could have done something like pg= new PageReference(objectId + '/e'); instead to get the edit page but I believe I had a weird issue with that that I do not remember right now.)

 

The param isdtp=mn makes sure the header and sidebar do not display. You can remove this param.

The param nooverride=1 (I am not sure, but without it, it did not seem to work YMMV).

 

You can see in my commented out code, that I was planning to make the next page automatically go to the edit page of the next task instead of wasting a round trip to the server by presenting the saveComplete page and then having to go back to get the edit page for the next task. I got it working about 90%, but didn't want to spend the time to finish it when I already had a working solution.

 

The updateTaskStatus is called after you hit save. It re-gets the saved task from the database and replaces the stale version in fullTaskList with this new version. I do this so I can update on the VF page the status of the task.

 

PAGESIZE is obviously how many list objects to display per page.

 

The VF Page shows the task list at the top.

On initial load, you will see the task list on top and the first task (ready for edit) on bottom (in an iframe).

The commandlink in the task list, allows you to select a task also so that the bottom pane will update to reflect the edit page of the selected task.

 

On load of the task edit section, the addOnCompleteEvent javascript code is run. It searches the frame for input buttons named save (yes, if salesforce ever changes the name of the button from save, this will have to change to.) I only get the save buttons. Not the save and new task ... buttons. To these save buttons, I add the onclick attribute to run parent.taskSaved(). When the user click the save button, the javascript global var saved is set to 1 (see taskSaved function). That is it. The form is submitted and a page is returned. That page is rendered in the lower from. Upon render completion, the addOnCompleteEvent function is called (as above). the refreshToDoNumbers() javascript is called. If saved is one, which it is, reset it to 0 and call the ajax function (actionFunction) refreshNumbers. This goes to the server, and refreshes the numberOfItemsLeft hidden field on the page, as well as the table (task list -- hence the update to the task status), and the detail section (the next task edit page is place in the bottom frame). On completion it also calls checkIfNeedRefresh(). This function gets the numberOfItemsLeft hidden field, and if it is 0, it refreshes the parent page. For my case, this means, dumping the whole page and getting the next object the user has to work on (whether it be a task with a different record type, or a case, event ...)

 

Hope this helps.

lamayclamayc
By the way, I am using Firefox 3. I have not tested on IE yet.
EMHDevEMHDev

Hi Iamyc,

I have not been able to get this working.  I think I understand the Apex, I am struggling with the VF though.  I get the error 

"Save error: Unknown property 'DashboardTaskController.tasks'    Development/src/pages    Task_Dashboard.page    line 0"

which is my VF controller page.  I'm guessing this is referring to the line

 

<apex:pageBlockTable value="{!tasks}" var="o" id="table">

 since this is the only line referring to "tasks" but I can't work out what is wrong with it.  I've tried to use your code but have removed the record type references as we don't use record types.

 

Do I need to have set up something other than the VF page you show above and the Apex class?

Thanks,

Erica

 

 

lamayclamayc

Hopefully you read my follow-up where I tried to explain what I was doing.

Anyway, that {!tasks} would be referring to the getTasks method in the controller, so I am not exactly sure why you would get an error. Make sure you have the getTasks method (maybe something happened during cut 'n paste ?)

 

To verify your error, I temporarily renamed the getTasks method and saved the class. When I tried to save the VF page, I get the error you see. Hence what I said above.

 

No, you should not have to set anything else up to get this to work.

 

Feel free to ask any more questions.

 

PS. There seems to be an issue with the AJAX request under IE 6 (like I said, I verified using Firefox 3.1). Still looking into. If you are using IE 7 or 8, Opera, Chrome, Safari, maybe it will work, hopefully it will work as is (after all, we all know the mess IE 6 is, alas it is the version I have). Will post when/if I get I fix for IE 6.

EMHDevEMHDev

Here is a weird one.  In desperation I copied the code from the Eclipse IDE and pasted it into the native page editor in my salesforce dev edition and it worked!  I then sync'd from salesforce to Eclipse and it is happy again.  So now I can move forwards with it.

 

Did  you develop this because a task list controller isn't available?  I saw your post about a week before mine, did you write all of this code in a week?  I'm impressed.

 

Thanks so much for your help.  I've done so much searching and nothing else has come up that I could use.

lamayclamayc

Yes. I needed a task controller. No, a day or two, I don't have that kind of time ... but I've been doing this salesforce thing for quite a few months.

 

Anyway, I got it to work in IE (and firefox)

 

replace in VF page:

 

inputs[i].setAttribute("onclick", "parent.taskSaved()");

with

inputs[i].onclick = function() {taskSaved();};

 

and life seems to be beautiful (for a while at least ;)

 

 

Of course, you might not even need the javascript at all or even some of the apex controller stuff for that matter if all you are doing is a basic list controller.

 

EMHDevEMHDev

Hi,

 

Any chance of listing the code which deals with the saving and refreshing of the task list?  I simply can't get it working and have put some some debug alerts into addOnCompleteEvent which tells me that inputs is coming back from getElementByTagName with no results, i.e. length is 0. What I want is that if they save a task, it checks whether it needs to refresh the list, but it never gets the save=1 assignment.

 

Got pulled off this to do something else which is why there is a long time lag, but would really appreciate any help.

 

Thanks

dragonmagicldragonmagicl

Same lamayc ... different username.

 

I have no specific code that saves the task. That should be handled automatically by the fact you are using the default task edit form.

 

Refreshing the task list occurs automatically. The action function (ajax call) rerenders the table (task list) for you.

 

It sounds to me like your primary issue is the getElement function returning nothing. If the javascript does not properly find the submit button, then it will not add the onclick event to it. Without this, nothing works. I know the whole way it works here is kind of a nasty hack, but .... Anyway, just make sure it is looking at the correct form. Maybe change the getElement to look for the form tag and print the innerHTML of that to verify it is at least looking at the correct page. There will probably be 2 places where the onclick event is added since the default task edit has 2 submit buttons (one on the bottom of the form and one on the top). I will be happy to help you as much as I can, but it seems to me the return of no tags is your only real problem (so far). Once you get that resolved, it should (hopefully) work.

 

Good luck

EMHDevEMHDev

Hi,

 

I am definitely not finding the save button, you are correct. I've tinkered with the code to try to point it to the right place, so far with no success.  The function looks like this at the moment:

 

 

<apex:outputPanel layout="block" id="detail">
<!--unfortunately can't use apex:iframe since onload is not supported-->
<!--apex:iframe rendered="{!linkSelected}" id="editFrame" scrolling="no" src="{!detailPage}" height="3000" frameborder="true">apex:iframe-->
<iframe onload="addOnCompleteEvent();" id="editFrame" scrolling="no" src="{!detailPage}" height="3000" width="100%" frameborder="true"/>
<script type="text/javascript">
function addOnCompleteEvent() {
// first see if all items complete and so need to refresh the page
refreshToDoNumbers();

// find the save button(s) on the edit form and add the oncomplete event to them
//var editFrame = window.frames["editFrame"];
var editFrame = document.getElementById("editPage");
alert(" innerHTML: "+editFrame.innerHTML);
var inputs = editFrame.document.getElementsByTagName('input');
alert("length:"+inputs.length+" innerHTML: "+inputs.innerHTML);

for (i=0; i<inputs.length; i++) {
if (inputs[i].type == 'submit' && inputs[i].name == 'save') {
inputs[i].onclick = function() {taskSaved();};
} else {
alert(inputs[i].type +" "+ inputs[i].name);
}
}
}
</script>
</apex:outputPanel>

 

 Because windows.frames[0] wasn't returning anything, I tried putting in the iframe name but that didn't work either.  I then tried


var editFrame = document.getElementById("editPage");

 

after seeing that editPage is what salesforce calls the editPage form, but that doesn't work either and Firebug flags up that editFrame is null.

 

I'm a novice at all of this - I learnt Javascript in order to do scontrols, but now trying to move to VF I find my skills to be completely inadequate.  I'd appreciate any advice you can send my way.

 

Thanks

dragonmagicldragonmagicl

Sorry, just hit me. I haven't given you all the pages I used. This page is designed to be used from within another page as a frame. That is why you are not getting anything. You don't have any frames.

 

Let's see if I can come up with all the other supporting stuff.

 

Here is the top level page (for me called Dashboard).  Pretty simple, just creates the Ajax function to refresh the page.

 

 

<apex:page id="p" controller="DashboardController" standardStylesheets="true" showHeader="false">

<apex:form >

<apex:actionFunction name="refreshPage" immediate="true" action="{!refreshPage}" rerender="dashboard"/>

</apex:form>

<apex:outputPanel id="dashboard">

<apex:iframe width="93%" scrolling="no" height="2000" frameborder="true" src="/apex/DashTasks"/>

 </apex:outputPanel>

</apex:page>

 

The code to refresh the page is just  

 

 

public PageReference refreshPage() { return null; }

 

So now this will create a new frame for you using the DashTasks page (the page we have been working on since the beginning of this discussion -- Change the name to the name of your VF page of course).

 

I think this is all you are missing.

 

Note that this is the code I need to use (and not all of it) just because I do a lot of page switching and stuff that I assume you do not need to do.  That being said, and now that I think of it, you are probably not doing the switch statements that I am and just need the page I already gave you (no extra stuff -- and without the frames). If this is the case, you could probably just modify the addOnCompleteEvent javascript function to something like this (I did not verify working of this, but you can get the jist).

 

<script type="text/javascript"> function addOnCompleteEvent() { // first see if all items complete and so need to refresh the page refreshToDoNumbers(); // find the save button(s) on the edit form and add the oncomplete event to them var inputs = document.getElementsByTagName('input'); for (i=0; i<inputs.length; i++) { //alert(inputs[i].type + ' : ' + inputs[i].name); if (inputs[i].type == 'submit' && inputs[i].name == 'save') { inputs[i].onclick = function() {taskSaved();}; } } } </script>

Notice how all I did was take out the frames reference and changed the variable inputs to get elements on the document (instead of trying to get them from within a frame that you obviously do not have/need).  You might have to slightly adjust your  page refresh code (but maybe not, I haven't tried it this way.) Sorry for the confusion.

 

Good luck!

 

 
EMHDevEMHDev

Well, some progress.  I am now at least getting some results from the getElement calls, but when it shows a submit type, the name is always Search and never Save.  I've left the iFrame in so that I can call addOnCompleteEvent when it loads, otherwise I couldn't work out how to call it.  I guess this means that it is not being called when the task is saved, only once it refreshes the frame with the resulting page after the save, since the result is the same in edit mode and after the changed task has been saved.

 

Also in the line

 

<apex:actionFunction name="refreshPage" immediate="true" action="{!refreshPage}" rerender="taskList"/>

 

you had rerender="dashboard" but I couldn't find a dashboard tag anywhere in the code and so I put the taskList tag in since that is what I want to refresh, is that correct?

dragonmagicldragonmagicl

change js function to

<script type="text/javascript"> function addOnCompleteEvent() { // first see if all items complete and so need to refresh the page refreshToDoNumbers(); // find the save button(s) on the edit form and add the oncomplete event to them editFrame = document.getElementById('editFrame'); if (editFrame.document) { inputs = editFrame.document.getElementsByTagName('input'); } else { //if (editFrame.contentDocument) inputs = editFrame.contentDocument.getElementsByTagName('input'); } //var inputs = editFrame.document.getElementsByTagName('input'); for (i=0; i<inputs.length; i++) { //alert(inputs[i].type + ' : ' + inputs[i].name); if (inputs[i].type == 'submit' && inputs[i].name == 'save') { inputs[i].onclick = function() {taskSaved();}; } } } </script>

and try

dragonmagicldragonmagicl

I also use a cheesy little savecomplete page for user feedback if you want

 

<apex:page > Save Complete ... Please wait while loading next item. </apex:page>

 

just call it saveComplete
EMHDevEMHDev

I already created my own piece of saveComplete cheese, thanks!

 

OK, that is brilliant, it is picking up the tags now.  I then got an SObject SOQL error saying something to the effect that I was not returning WhatID in the query when I needed it, and I changed the updateTaskStatus Apex function to include the columns that I show in the Dashboard, and hey presto - it is working.

 

Thanks a million - never mind dragonmagicl, could be wizardcodr!

EMHDevEMHDev

I can't believe this.  When I try to roll it out to the production system, it throws an exception saying

 

Permission denied to get property HTMLDocument.getElementsByTagName
https://c.eu0.visual.force.com/apex/Task_Dashboard?core.apexpages.devmode.url=1
Line 207

 

which is

var inputs = editFrame.contentDocument.getElementsByTagName('input');

 

Looking on these boards, this error appears to be related to cross-domain calls.  How is this a cross-domain call, and why doesn't it flag up in the Development Edition?  Any ideas?

 

Thanks

dragonmagicldragonmagicl

To be honest, I never even considered that something like that would happen (I have only tried it in sandbox and in developer edition). I think I read somewhere that VF pages are actually served up from a different domain though (so unfortunately is does make sense but is pretty annoying). Unfortunately, this would be so much easier if they had an apex tag that could display the default edit page (instead of just the default detail page).

 

Anyway, if there is going to be a cross-domain problem then I guess there is nothing we can do except create our own task edit page (you should still be able to use the standard controller). I don't like this because if the edit page needs to be changed, now a developer has to get involved...

 

I was just looking back at your original requirement. It doesn't look like it has to be as fancy as mine. You might be trying to do more than you need. If all you need is the task list, which is what you specified originally, then you already have that. Just get rid of the whole outputPanel section of the page. It doesn't sound like you need to bother at all with the frames and the task edit page ... If that is the case, why don't you simplify it.

 

Maybe...

 

<apex:page controller="DashboardTaskController" standardStylesheets="true" showHeader="false"> <apex:form id="taskList"> <apex:pageBlock id="pageBlock"> <apex:pageBlockTable value="{!tasks}" var="o" id="table"> <apex:column> <apex:commandLink action="{!showDetail}" value="{!o.subject}" reRender="detail"> <apex:param value="{!o.id}" name="objectId" assignTo="{!objectId}"></apex:param> </apex:commandLink> </apex:column> <apex:column value="{!o.subject}" /> <apex:column id="status" value="{!o.status}" /> </apex:pageBlockTable> </apex:pageBlock> <apex:panelGrid columns="2"> <apex:commandLink action="{!previous}" rendered="{!hasPrevious}">Previous</apex:commandlink> <apex:commandLink action="{!next}" rendered="{!hasNext}">Next</apex:commandlink> </apex:panelGrid> </apex:form> </apex:page>

 

and then you can perhaps slim down the controller too.?

 

This should give you the task list (which is all you originally wanted from your post).

 

You just have to click on a link to go to the actually task instead of the task being in-lined. When you go back to this page, the status info should be automatically updated too (so you want need all the js stuff).

 

If you really need the task in-lined. The only other thing I can readily think of (which I was not really interested in doing), is to create your own task edit page.

 

Another thought ... If you do not need the task edit page in-lined, but the detail page is enough, then you could create a VF page that takes in an id and prints out the task detail (ie <apex:detail id='{!myId}'>). You would then create a controller extension for it the reads the id from the url (it would be like 2 lines of code). This way you are still using the default default detail page (so that sales could modify it easily). The only downside is that to edit the task, you would have to click the edit button to access the task edit page.

 

Going this way, you should have the task list and the task detail for the selected task.

 

Just some ideas.

 

(That cross-domain thing hurts though. And unfortunately salesforce doesn't allow me to rewrite the content of the page before sending it to the browser. They let you get the content from the pageref, but it doesn't look like you can restuff it in.)

EMHDevEMHDev

Well, now the powers-that-be have seen the app, they like the inline editing.  I've seen lots of discussion of this problem but no solutions, so I'm not sure what I'll do.  As you say, sounds like we'll need a developer.  Salesforce seem to give things with one hand and take them away with the other.  If I was a fulltime developer, I may have a chance of keeping up with all this stuff, but as a part time admin/customiser/jill of all trades, I find this VF stuff pretty difficult to get to grips with.

 

 

Thanks for all your help - at least I've learnta few things!

dragonmagicldragonmagicl

Vote for this idea, and maybe this stuff will be a bit easier to do

 

http://ideas.salesforce.com/article/show/10091122/Allow_VisualForce_Detail_Page_to_be_an_edit_form

EMHDevEMHDev
Voted and meanwhile will try to do my own edit page. Yuck.  If I get anywhere with it, I will post it here.
dragonmagicldragonmagicl

Can't get the "save and new task" and "save and new event" buttons working correctly. Will have to look into this in the future (other things await now). Anyway...

 

<apex:page controller="DashboardTaskController" standardStylesheets="true" showHeader="false"> <!-- close date will be null since we will only display open ones ...but might want to be more flexible here so management view will look different ... see class --> {!num} <apex:form id="taskList"> <script> stage = 0; resetStage = false; function refreshToDoNumbers() { //alert('refreshToDoNumbers'); if (stage == 2 && resetStage) { stage = 0; resetStage = false; } if (stage == 1) { stage = 2; resetStage = true; refreshNumbers(); } if (stage == 0) { stage = 1; } } function reinit() { //alert('resetting'); stage = 0; resetStage = false; } function checkIfNeedRefresh() { //alert('checking for full refresh'); var left = document.getElementById('{!$Component.taskList.numberOfItemsLeft}'); //alert('found items left todo : ' + left.innerHTML); var tmp = parseInt(left.innerHTML); if (tmp == NaN || tmp == 0) { parent.refreshPage(); } } </script> <apex:pageBlock id="pageBlock"> <apex:pageBlockTable value="{!tasks}" var="o" id="table"> <apex:column > <apex:commandLink action="{!showDetail}" value="{!o.subject}" reRender="detail" onclick="reinit()"> <apex:param value="{!o.id}" name="objectId" assignTo="{!objectId}"></apex:param> </apex:commandLink> </apex:column> <apex:column value="{!o.subject}" /> <apex:column id="status" value="{!o.status}" /> </apex:pageBlockTable> </apex:pageBlock> <apex:panelGrid columns="2"> <apex:commandLink action="{!previous}" rendered="{!hasPrevious}">Previous</apex:commandlink> <apex:commandLink action="{!next}" rendered="{!hasNext}">Next</apex:commandlink> </apex:panelGrid> <apex:outputText id="numberOfItemsLeft" value="{!numLeft}" style="visibility:hidden" /> <apex:actionFunction action="{!refreshNumbers}" immediate="true" name="refreshNumbers" rerender="numberOfItemsLeft, detail, table" oncomplete="checkIfNeedRefresh();" /> </apex:form> <apex:outputPanel layout="block" id="detail"> <iframe onload="refreshToDoNumbers()" id="editFrame" scrolling="no" src="{!detailPage}" height="3000" width="100%" frameborder="true" /> </apex:outputPanel> </apex:page>

 

I don't think I changed anything else.

dragonmagicldragonmagicl
One more thing you could probably do to make it even easier, is change the saveURL in the controller ... instead of going to saveComplete, just go back to the Dashboard page. Of coure, then you have a full page refresh instead of just the bottom section refresh. Not sure if this would entirely fix the "save and new task", and "save and new event" buttons.
EMHDevEMHDev

This looks good, although for some reason it seems to perform a lot slower than the other approach.

 

Meanwhile, I started a different approach as  I couldn't work out how to do the edit page, which involved inline editing of the task data.  I'm going to try a combination of the two approaches and see if it will work on the production system.  Will post the results once I've done it.  I'm not as quick as you are, not least because I'm part time on all of this!

 

Cheers -

Erica

EMHDevEMHDev

I decided to stick with the task list and inline editing - it is quicker and gives me what I need.  I implemented a save method which appears to work (!) - being a novice I hope I'm not doing anything dreadful.

 

Here is the page code:

 

<apex:page controller="DashboardTaskController" standardStylesheets="true" showHeader="true"> <!-- close date will be null since we will only display open ones ... but might want to be more flexible here so management view will look different ... see class --> <!-- {!recordType}---{!num} --> <apex:form id="taskList"> <script> function checkIfNeedRefresh() { //alert('checking for full refresh'); var left = document.getElementById('{!$Component.taskList.numberOfItemsLeft}'); //alert('found items left todo : ' + left.innerHTML); var tmp = parseInt(left.innerHTML); if (tmp == NaN || tmp == 0) { parent.refreshPage(); } } </script> <apex:pageBlock id="pageBlock" title="Task List"> <apex:pageBlockButtons > <apex:commandButton action="{!save}" value="Save" id="theButton"/> </apex:pageBlockButtons> <apex:pageBlockTable value="{!tasks}" var="o" id="table"> <apex:column > <apex:commandLink action="{!showDetail}" value="{!o.subject}" reRender="detail"> <apex:param value="{!o.id}" name="objectId" assignTo="{!objectId}"></apex:param> </apex:commandLink> </apex:column> <apex:column id="status" headerValue="Status"> <apex:inputField value="{!o.status}"/> </apex:column> <apex:column id="priority" headerValue="Priority"> <apex:inputField value="{!o.priority}"/> </apex:column> <apex:column id="ActivityDate" headerValue="Due Date" > <apex:inputField value="{!o.ActivityDate}"/> </apex:column> <apex:column id="WhatId" headerValue="Related to" value="{!o.WhatId}"/> <apex:column id="WhoId" headerValue="Contact/Lead" value="{!o.WhoId}"/> <apex:column id="IsReminderSet" headerValue="Reminder"> <apex:inputField value="{!o.IsReminderSet}"/> </apex:column> <apex:column id="ReminderDateTime" headerValue="Reminder" > <apex:inputField value="{!o.ReminderDateTime}"/> </apex:column> <apex:column id="Description" headerValue="Comments" > <apex:inputField value="{!o.Description}"/> </apex:column> </apex:pageBlockTable> </apex:pageBlock> <apex:panelGrid columns="2"> <apex:commandLink action="{!previous}" rendered="{!hasPrevious}">Previous Page</apex:commandlink> <apex:commandLink action="{!next}" rendered="{!hasNext}">Next Page</apex:commandlink> </apex:panelGrid> <apex:outputText id="numberOfItemsLeft" value="{!numLeft}" style="visibility:hidden"/> <apex:actionFunction action="{!refreshNumbers}" immediate="true" name="refreshNumbers" rerender="numberOfItemsLeft, detail, table" oncomplete="checkIfNeedRefresh();"/> </apex:form> </apex:page>

 

 My requirements are different to yours in that I don't use record types nor teams, so I've also changed the query to filter on the tasks owned by the running user.  The class looks as follows:

 

public class DashboardTaskController { //extends DashboardAbstractController { public String objectId {get; set;} //public Task currentObject {get; set;} //public String updatedItemStatus{get; set;} public Integer numLeft {get; set;} public Integer total {get; set;} private Integer startNdx = 0; private static Integer PAGESIZE = 8; private List<Task> fullTaskList = new List<Task>(); private List<Task> displayedTaskList = new List<Task>(); public DashboardTaskController() { } public PageReference refreshPage() { return null; } public List<Task> getTasks() { String ownerId = UserInfo.getUserId(); if (fullTaskList.isEmpty()) { fullTaskList = [select id, WhatId, WhoId, ActivityDate, subject, status, priority, Description, ReminderDateTime, IsReminderSet,isClosed from Task where isClosed = false and OwnerId = :ownerId]; numLeft = fullTaskList.size(); total = numLeft; this.objectId = ((Task) fullTaskList[0]).id; //this.currentObject = (Task) fullTaskList[0]; } displayedTaskList.clear(); Integer endNdx = startNdx + PAGESIZE; if (endNdx > total) endNdx = total; for (Integer i=startNdx; i<endNdx; i++) displayedTaskList.add(fullTaskList.get(i)); return displayedTaskList; } private void updateTaskStatus() { System.debug('before : ' + fullTaskList); Integer i = 0; for (i=0; i<fullTaskList.size(); i++) { Task t = fullTaskList.get(i); if (this.objectId.equals(t.id)) { System.debug('updating status of ' + t); Task tmp = [select id, WhatId, WhoId, ActivityDate, subject, status, priority, Description, ReminderDateTime, IsReminderSet, isClosed from Task where id = :t.id]; fullTaskList.set(i, tmp); System.debug('updated to ' + tmp); //this.updatedItemStatus = tmp.status; break; } } System.debug('after : ' + fullTaskList); } private void nextTask() { for (Task t : fullTaskList) { if (!t.isClosed) { System.debug('found non-closed object with id ' + t.id); this.objectId = t.id; break; } } } public PageReference showDetail() { // since I can't get the assignTo to work in the VF page/component this.objectId = System.currentPageReference().getParameters().get('objectId'); /* bizarre ... if I call setObjectId, the id returned from the detailPage method is null but if I directly set the id in this method, everything works correctly setObjectId(System.currentPageReference().getParameters().get('objectId')); */ return null; } public void previous() { startNdx -= PAGESIZE; } public void next() { startNdx += PAGESIZE; } public void refreshNumbers() { // the only time this method should be called is when a submit is done // on a record -- we assume the submit closes the record (or at least // puts the record in a state where we are not interested in seeing // it in our todo list anymore updateTaskStatus(); nextTask(); this.numLeft = 0; for (Task t : fullTaskList) { if (!t.isClosed) { this.numLeft++; } } //this.objectId = prefetchedNextObjectId; } public Boolean getHasNext() { return total > (startNdx + PAGESIZE); } public Boolean getHasPrevious() { return startNdx > 0; } public Integer getNum() { return total; } public PageReference save(){ for (Task t : fullTaskList) { update t; } return null; }

 Now I need to try it on the production system...