Chatter Controller
public with sharing class ChatterController {
public class Response {
public Boolean success;
public String errorMessage;
public List<SObject> records;
public Integer total;
Response() {
records = new List<SObject>();
success = true;
}
}
//One of the parameters supplied by the DirectProxy read method.
public class QueryRequest {
Integer start;
Integer recordCount;
List<Map<String, String>> sortParams;
Public QueryRequest() {
start = 1;
recordCount = 1;
}
Public QueryRequest(Integer pStart, Integer pRecordCount) {
start = pStart;
recordCount = pRecordCount;
}
}
@RemoteAction
public static Response Add(List<FeedItem> chatterData){
return insertLeadList(chatterData);
}
@RemoteAction
public static Response Destroy(List<FeedItem> chatterData){
return deleteChatterList(chatterData);
}
@RemoteAction
public static Response Edit(List<FeedItem> chatterData){
return editChatterList(chatterData);
}
@RemoteAction
public static Response EditComment(List<String> chatterCommentData){
return editCommentChatterList(chatterCommentData);
}
@RemoteAction
public static Response Query(QueryRequest qr){
Response resp = new Response();
//Enforce a limit on the number of rows requested.
final integer QUERY_LIMIT = 500;
if( qr.start >= QUERY_LIMIT ){
resp.success = false;
resp.errorMessage = 'Maximum number of records (' + String.valueOf(QUERY_LIMIT) + ') exceeded!';
return resp;
}
try {
getAllChatter(qr, resp);
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Query failed: ' + e.getMessage();
}
return resp;
}
private static Response insertCommentOrLike(List<FeedComment> chatterComment, List<FeedLike> chatterLike)
{
Response resp = new Response();
resp.success = true;
try {
insert chatterComment;
insert chatterLike;
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Insert failed: ' + e.getMessage();
}
return resp;
}
private static void getAllChatter(QueryRequest qr, Response resp){
//Page size is set in the Sencha store as recordCount.
Integer pageSize = qr.recordCount;
//Page number will be calculated.
Integer pageNumber = 0;
//Start is the record number indicating the start of the page.
if( qr.start > 0 ){
pageNumber = qr.start / pageSize;
}
//Calculate the offset for SOQL.
Integer offset = pageNumber * pageSize;
//Build the query in pieces.
String fieldList = 'Id, Title, Body, LikeCount,CommentCount';
//Construct a base query to which the page offsets will be added.
String baseQuery = 'SELECT ' + fieldList + ' FROM FeedItem Order By CreatedDate DESC';
//Construct a count query to pass back the total records matching a search criteria.
String baseCountQuery = 'SELECT COUNT() FROM FeedItem';
//Construct the fetch query with the offset.
String fetchQuery = baseQuery + ' LIMIT ' + pageSize + ' OFFSET ' + offset;
try {
//Set the count.
resp.total = Database.countQuery(baseCountQuery);
//Set the fetched recordset.
resp.records = Database.query(fetchQuery);
//Set the status flag.
resp.success = true;
} catch (Exception e) {
//Set the total count of records matching the query.
resp.total = 0;
//Set the recordset to return.
resp.records = new List<Lead>();
//Set the status flag.
resp.success = false;
}
}
private static Response insertLeadList(List<FeedItem> chatterData){
Response resp = new Response();
resp.success = true;
try {
chatterData[0].ParentId = UserInfo.getUserId();
insert chatterData;
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Insert failed: ' + e.getMessage();
}
return resp;
}
private static Response deleteChatterList(List<FeedItem> ChatterData){
Response resp = new Response();
resp.success = true;
try {
delete ChatterData;
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Deletion failed: ' + e.getMessage();
}
return resp;
}
private static Response editChatterList(List<FeedItem> chatterData){
Response resp = new Response();
resp.success = true;
try {
System.debug('Hier: ' + chatterData);
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Deletion failed: ' + e.getMessage();
}
return resp;
}
private static Response editCommentChatterList(List<String> chatterCommentData)
{
Response resp = new Response();
resp.success = true;
try {
System.debug('Hier: ' + chatterCommentData);
} catch (Exception e) {
resp.success = false;
resp.errorMessage = 'Deletion failed: ' + e.getMessage();
}
return resp;
}
}
Chatter Component
<apex:component controller="ChatterController">
<script type="text/javascript">
Ext.application({
name: "ChatterPlus",
models: ["Chatter"],
stores: ["Chatter"],
controllers: ["Chatter"],
views: ["ChatterList", "ChatterEditor", "ChatterLikeCommentEditor"],
launch: function () {
var chatterListView = {
xtype: "chatterlistview"
};
var chatterEditorView = {
xtype: "chattereditorview"
};
var chatterLikeCommentEditor = {
xtype: "chatterlikecommentview"
};
Ext.Viewport.add([chatterListView, chatterEditorView, chatterLikeCommentEditor]);
}
});
Ext.define("ChatterPlus.view.ChatterList", {
extend: "Ext.Container",
requires: "Ext.dataview.List",
alias: "widget.chatterlistview",
config: {
//Take up the full space available in the parent container.
layout: {
type: 'fit'
},
//Add the components to include within the list view.
items: [
{
xtype: "toolbar",
title: "Chatter",
docked: "top",
items: [
{
xtype: 'spacer'
},
{
xtype: "button",
text: 'New',
ui: 'action',
itemId: "newButton"
}
]
},
{
xtype: "toolbar",
docked: "bottom",
itemId: "bottomToolBar",
items: [
]
},
{
//The main list and its properties.
xtype: "list",
store: "Chatter",
itemId:"chatterList",
onItemDisclosure: true,
indexBar: true,
grouped: false,
disableSelection: false,
plugins: [
{
xclass: 'Ext.plugin.ListPaging',
autoPaging: true
}
],
loadingText: "Loading Feed...",
emptyText: '<div class="chatter-list-empty-text">No feeds found.</div>',
itemTpl: '<div class="list-item-line-main">{Title}</div>' +
'<div class="list-item-line-main">{Body}</div>',
}],
listeners: [{
delegate: "#newButton",
event: "tap",
fn: "onNewButtonTap"
}, {
delegate: "#syncButton",
event: "tap",
fn: "onSyncButtonTap"
}, {
delegate: "#chatterList",
event: "disclose",
fn: "onChatterListDisclose"
},
{
delegate: "#chatterList",
event: "refresh",
fn: "onChatterListRefresh",
},
{
//Listener on the view's Activate event fires when redisplayed by transition.
event: "activate",
fn: "onChatterListViewActivate",
}]
},
onSyncButtonTap: function () {
console.log("syncChatterCommand");
this.fireEvent("syncChatterCommand", this);
},
onNewButtonTap: function () {
console.log("newChatterCommand");
this.fireEvent("newChatterCommand", this);
},
onChatterListDisclose: function (list, record, target, index, evt, options) {
console.log("editChatterCommand");
this.fireEvent('editChatterCommand', this, record);
},
onChatterListRefresh: function () {
console.log("onChatterListRefresh");
this.updateListCounter();
},
onChatterListViewActivate: function () {
console.log("onChatterListViewActivate");
this.updateListCounter();
},
//Function to get count of records in the list and show on the search button's badge.
updateListCounter: function () {
var listCount = Ext.getStore("Chatter").getCount();
//this.getComponent("bottomToolBar").getComponent("syncButton").setBadgeText(listCount);
}
});
Ext.define("ChatterPlus.view.ChatterEditor", {
extend: "Ext.form.Panel",
requires: "Ext.form.FieldSet",
alias: "widget.chattereditorview",
config: {
scrollable: 'vertical',
items: [
{
xtype: "toolbar",
docked: "top",
title: "Edit Chatter",
items: [
{
xtype: "button",
ui: "back",
text: "Home",
itemId: "backButton"
},
{ xtype: "spacer" },
{
xtype: "button",
ui: "action",
text: "Save",
itemId: "saveButton"
}
]
},
{
xtype: "toolbar",
docked: "bottom",
items: [
{
xtype: "button",
iconCls: "trash",
iconMask: true,
itemId: "deleteButton"
}
]
},
{ xtype: "fieldset",
title: 'Chatter Info',
items: [
{
xtype: 'textfield',
name: 'Title',
label: 'Title',
required: true
},
{
xtype: 'textareafield',
name: 'Body',
label: 'Body',
maxRows: 1,
required: true
},
{
xtype: 'textfield',
name: 'CommentCount',
label: 'Comment Count',
disabled: true
},
{
xtype: 'textfield',
name: 'LikeCount',
label: 'Like Count',
disabled: true
},
]
},
{ xtype: "fieldset",
title: 'Chatter Comment & Like',
items: [
{
xtype : 'textareafield',
name : 'Comment',
label : 'Comment',
maxRows: 1
},
{
xtype : 'checkboxfield',
name : 'Like',
label : 'Like'
},
]
},
],
listeners: [
{
delegate: "#backButton",
event: "tap",
fn: "onBackButtonTap"
},
{
delegate: "#saveButton",
event: "tap",
fn: "onSaveButtonTap"
},
{
delegate: "#deleteButton",
event: "tap",
fn: "onDeleteButtonTap"
}
]
},
onSaveButtonTap: function () {
console.log("saveChatterCommand");
this.fireEvent("saveChatterCommand", this);
},
onDeleteButtonTap: function () {
console.log("deleteChatterCommand");
Ext.Msg.confirm("Delete Feed", "Are you sure?", function(button){
if (button == 'yes') {
this.fireEvent("deleteChatterCommand", this);
} else {
return false;
}
}, this);
},
onBackButtonTap: function () {
console.log("backToHomeCommand");
this.fireEvent("backToHomeCommand", this);
}
});
Ext.define("ChatterPlus.view.ChatterLikeCommentEditor", {
extend: "Ext.form.Panel",
requires: "Ext.form.FieldSet",
alias: "widget.chatterlikecommentview",
config: {
}
});
Ext.define("ChatterPlus.controller.Chatter", {
extend: "Ext.app.Controller",
config: {
refs: {
chatterListView: "chatterlistview",
chatterEditorView: "chattereditorview",
chatterList: "#chatterList",
},
control: {
chatterListView: {
// The commands fired by the list container.
syncChatterCommand: "onSyncChatterCommand",
newChatterCommand: "onNewChatterCommand",
editChatterCommand: "onEditChatterCommand",
},
chatterEditorView: {
// The commands fired by the note editor.
saveChatterCommand: "onSaveChatterCommand",
deleteChatterCommand: "onDeleteChatterCommand",
backToHomeCommand: "onBackToHomeCommand"
}
}
},
//View Transitions
slideLeftTransition: { type: 'slide', direction: 'left' },
slideRightTransition: { type: 'slide', direction: 'right' },
activateChatterEditor: function (record) {
var chatterEditorView = this.getChatterEditorView();
chatterEditorView.setRecord(record);
Ext.Viewport.animateActiveItem(chatterEditorView, this.slideLeftTransition);
},
activateChatterList: function () {
Ext.Viewport.animateActiveItem(this.getChatterListView(), this.slideRightTransition);
},
onSyncChatterCommand: function () {
console.log("onSyncChatterCommand");
this.loadList();
},
onNewChatterCommand: function () {
console.log("onNewChatterCommand");
var newChatter = Ext.create("ChatterPlus.model.Chatter");
this.activateChatterEditor(newChatter);
},
onEditChatterCommand: function (list, record) {
console.log("onEditChatterCommand");
this.activateChatterEditor(record);
},
loadList: function () {
//Get a ref to the store and remove it.
var chatterStore = Ext.getStore("Chatter");
var model = Ext.ModelMgr.getModel('ChatterPlus.model.Chatter');
model.getProxy();
chatterStore.getData().clear();
chatterStore.loadPage(1);
//Reshow the list.
this.activateChatterList();
},
// Base Class functions.
launch: function () {
console.log("launch");
this.callParent(arguments);
//Load up the Store associated with the controller and its views.
console.log("load Chatter");
this.loadList();
},
onSaveChatterCommand: function () {
console.log("onSaveChatterCommand");
var chatterEditorView = this.getChatterEditorView();
var currentChatter = chatterEditorView.getRecord();
var newValues = chatterEditorView.getValues();
this.getChatterEditorView().updateRecord(currentChatter);
var errors = currentChatter.validate();
if (!errors.isValid()) {
var msg = '';
errors.each(function(error) {
msg += error.getMessage() + '<br/>';
});
console.log('Errors: ' + msg);
Ext.Msg.alert('Please correct errors!', msg, Ext.emptyFn);
currentChatter.reject();
return;
}
//Get a ref to the store.
var chatterStore = Ext.getStore("Chatter");
var chatterCommentStore = Ext.getStore("ChatterComment");
//var newChatterComment = Ext.create("ChatterPlus.model.ChatterComment");
//Add new record to the store.
if (null == chatterStore.findRecord('id', currentChatter.data.id)) {
chatterStore.add(currentChatter);
//chatterCommentStore.add(newChatterComment);
}
chatterCommentStore.sync();
//Resync the proxy and activate the list.
chatterStore.sync();
this.activateChatterList();
},
onDeleteChatterCommand: function () {
console.log("onDeleteChatterCommand");
//Get a ref to the form and its record.
var chatterEditorView = this.getChatterEditorView();
var currentChatter = chatterEditorView.getRecord();
var chatterStore = Ext.getStore("Chatter");
chatterStore.remove(currentChatter);
chatterStore.sync();
this.activateChatterList();
},
onBackToHomeCommand: function () {
console.log("onBackToHomeCommand");
this.activateChatterList();
},
init: function() {
this.callParent(arguments);
console.log("init");
//Listen for exceptions observed by the proxy so we can report them and clean up.
Ext.getStore('Chatter').getProxy().addListener('exception', function (proxy, response, operation, options) {
// only certain kinds of errors seem to have useful information returned from the server
if (response) {
if (response.errorMessage) {
Ext.Msg.alert('Error', response.errorMessage);
} else {
Ext.Msg.alert('Error', operation.config.action + ' failed: ' + response.errorMessage);
}
} else {
Ext.Msg.alert('Error', operation.config.action + ' failed for an unknown reason: proxy = ' + proxy);
}
});
},
});
Ext.define("ChatterPlus.model.Chatter", {
extend: "Ext.data.Model",
config: {
idProperty: 'Id',
fields: [
{ name: 'Id', type: 'string', persist: false},
{ name: 'Title', type: 'string'},
{ name: 'Body', type: 'string'},
{ name: 'LikeCount', type: 'string', persist: false},
{ name: 'CommentCount', type: 'string', persist: false},
{ name: 'Comment', type: 'string', persist: false},
{ name: 'Like', type: 'string', persist: false},
{ name: 'ParentId', type: 'string'}
],
validations: [
{ type: 'presence', field: 'Title', message: 'Enter a Title' },
{ type: 'presence', field: 'Body', message: 'Enter a Body' }
],
//Bind each CRUD functions to a @RemoteAction method in the Apex controller
proxy: {
type: 'direct',
api: {
read: ChatterController.Query,
create: ChatterController.Add,
update: ChatterController.Edit,
destroy: ChatterController.Destroy
},
limitParam: 'recordCount', // because "limit" is an Apex keyword
sortParam: 'sortParams', // because "sort" is a keyword too
pageParam: false, // we don't use this in the controller, so don't send it
reader: {
type: 'json',
rootProperty: 'records',
messageProperty: 'errorMessage'
},
writer: {
type: 'json',
root: 'records',
writeAllFields: false, // otherwise empty fields will transmit as empty strings, instead of "null"/not present
allowSingle: false, // need to always be an array for code simplification
encode: false // docs say "set this to false when using DirectProxy"
}
}
},
});
Ext.define("ChatterPlus.model.ChatterComment", {
extend: "Ext.data.Model",
config: {
idProperty: 'Id',
fields: [
{ name: 'Comment', type: 'string'}
],
validations: [
],
//Bind each CRUD functions to a @RemoteAction method in the Apex controller
proxy: {
type: 'direct',
api: {
update: ChatterController.EditComment
},
writer: {
type: 'json',
root: 'records',
writeAllFields: false, // otherwise empty fields will transmit as empty strings, instead of "null"/not present
allowSingle: false, // need to always be an array for code simplification
encode: false // docs say "set this to false when using DirectProxy"
}
}
},
});
ChatterController.Query.directCfg.method.getArgs =
function (params, paramOrder, paramsAsHash) {
console.log('getArgs: ' + params.data);
return [params];
}
Ext.data.proxy.Direct.prototype.createRequestCallback =
function(request, operation, callback, scope){
var me = this;
return function(data, event){
console.log('createRequestCallback: ' + operation);
me.processResponse(event.status, operation, request, data, callback, scope);
};
};
Ext.define("ChatterPlus.store.Chatter", {
extend: "Ext.data.Store",
requires: "Ext.data.proxy.LocalStorage",
config: {
model: "ChatterPlus.model.Chatter",
autoLoad: true,
pageSize: 25,
},
listeners: {
load: function(){
console.log('store.load(): loaded!');
},
}
});
</script>
</apex:component>
0.000000
0.000000
Like this:
Like Loading...