Last active
August 29, 2015 14:10
-
-
Save greenstork/4fd5420dad2df374ff24 to your computer and use it in GitHub Desktop.
Visualforce/JS Feedback widget
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="feedback"> | |
<div id="feedback-widget" style="z-index:300;display:none;"> | |
<a class="handle" style="border-left:1px solid #ddd;border-top:1px solid #ddd;border-bottom:1px solid #ddd;" href="#">Feedback</a> | |
<div id="feedback-frame" style="display:none;"> | |
</div> | |
</div> | |
</div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<CustomObject xmlns="http://soap.sforce.com/2006/04/metadata"> | |
<actionOverrides> | |
<actionName>Accept</actionName> | |
<type>Default</type> | |
</actionOverrides> | |
<actionOverrides> | |
<actionName>Clone</actionName> | |
<type>Default</type> | |
</actionOverrides> | |
<actionOverrides> | |
<actionName>Delete</actionName> | |
<type>Default</type> | |
</actionOverrides> | |
<actionOverrides> | |
<actionName>Edit</actionName> | |
<type>Default</type> | |
</actionOverrides> | |
<actionOverrides> | |
<actionName>List</actionName> | |
<type>Default</type> | |
</actionOverrides> | |
<actionOverrides> | |
<actionName>New</actionName> | |
<type>Default</type> | |
</actionOverrides> | |
<actionOverrides> | |
<actionName>Tab</actionName> | |
<type>Default</type> | |
</actionOverrides> | |
<actionOverrides> | |
<actionName>View</actionName> | |
<type>Default</type> | |
</actionOverrides> | |
<deploymentStatus>Deployed</deploymentStatus> | |
<enableActivities>false</enableActivities> | |
<enableEnhancedLookup>false</enableEnhancedLookup> | |
<enableFeeds>true</enableFeeds> | |
<enableHistory>false</enableHistory> | |
<enableReports>true</enableReports> | |
<fields> | |
<fullName>Message__c</fullName> | |
<externalId>false</externalId> | |
<label>Message</label> | |
<length>32768</length> | |
<trackFeedHistory>false</trackFeedHistory> | |
<type>LongTextArea</type> | |
<visibleLines>5</visibleLines> | |
</fields> | |
<fields> | |
<fullName>Page_Name__c</fullName> | |
<externalId>false</externalId> | |
<formula>LOWER(SUBSTITUTE(IF(CONTAINS(URL__c, "?"), LEFT((RIGHT(URL__c, LEN(URL__c) - FIND(".com/", URL__c) - 4)), FIND("?", RIGHT(URL__c, LEN(URL__c) - FIND(".com/", URL__c) - 4)) - 1), RIGHT(URL__c, LEN(URL__c) - FIND(".com/", URL__c) - 4)), "apex/", ""))</formula> | |
<label>Page Name</label> | |
<required>false</required> | |
<type>Text</type> | |
<unique>false</unique> | |
</fields> | |
<fields> | |
<fullName>Rating__c</fullName> | |
<externalId>false</externalId> | |
<label>Rating</label> | |
<precision>1</precision> | |
<required>false</required> | |
<scale>0</scale> | |
<trackFeedHistory>false</trackFeedHistory> | |
<type>Number</type> | |
<unique>false</unique> | |
</fields> | |
<fields> | |
<fullName>Status__c</fullName> | |
<externalId>false</externalId> | |
<inlineHelpText>Status is this feedback. Has it been processed and has the person submitting received a response?</inlineHelpText> | |
<label>Status</label> | |
<picklist> | |
<picklistValues> | |
<fullName>New</fullName> | |
<default>true</default> | |
</picklistValues> | |
<picklistValues> | |
<fullName>In Progress</fullName> | |
<default>false</default> | |
</picklistValues> | |
<picklistValues> | |
<fullName>Completed</fullName> | |
<default>false</default> | |
</picklistValues> | |
<sorted>false</sorted> | |
</picklist> | |
<trackFeedHistory>false</trackFeedHistory> | |
<type>Picklist</type> | |
</fields> | |
<fields> | |
<fullName>URL__c</fullName> | |
<externalId>false</externalId> | |
<label>URL</label> | |
<required>false</required> | |
<trackFeedHistory>false</trackFeedHistory> | |
<type>Url</type> | |
</fields> | |
<label>Feedback</label> | |
<listViews> | |
<fullName>All</fullName> | |
<columns>NAME</columns> | |
<columns>URL__c</columns> | |
<columns>Page_Name__c</columns> | |
<columns>Rating__c</columns> | |
<columns>Message__c</columns> | |
<columns>CREATED_DATE</columns> | |
<columns>OWNER.FIRST_NAME</columns> | |
<columns>OWNER.LAST_NAME</columns> | |
<filterScope>Everything</filterScope> | |
<label>All</label> | |
<language>en_US</language> | |
</listViews> | |
<listViews> | |
<fullName>Unresolved_Feedback</fullName> | |
<columns>CREATEDBY_USER</columns> | |
<columns>NAME</columns> | |
<columns>Message__c</columns> | |
<columns>Status__c</columns> | |
<filterScope>Everything</filterScope> | |
<filters> | |
<field>Status__c</field> | |
<operation>equals</operation> | |
<value>New,In Progress</value> | |
</filters> | |
<label>Unresolved Feedback</label> | |
<language>en_US</language> | |
</listViews> | |
<nameField> | |
<displayFormat>F-{0000000}</displayFormat> | |
<label>Feedback Name</label> | |
<trackFeedHistory>false</trackFeedHistory> | |
<type>AutoNumber</type> | |
</nameField> | |
<pluralLabel>Feedback</pluralLabel> | |
<searchLayouts> | |
<customTabListAdditionalFields>URL__c</customTabListAdditionalFields> | |
<customTabListAdditionalFields>Rating__c</customTabListAdditionalFields> | |
<customTabListAdditionalFields>Message__c</customTabListAdditionalFields> | |
<customTabListAdditionalFields>OWNER.FIRST_NAME</customTabListAdditionalFields> | |
<customTabListAdditionalFields>OWNER.LAST_NAME</customTabListAdditionalFields> | |
<searchResultsAdditionalFields>URL__c</searchResultsAdditionalFields> | |
<searchResultsAdditionalFields>Rating__c</searchResultsAdditionalFields> | |
<searchResultsAdditionalFields>Message__c</searchResultsAdditionalFields> | |
</searchLayouts> | |
<sharingModel>Private</sharingModel> | |
</CustomObject> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="/resource/Bootstrap3/js/jquery-1.10.2.min.js" ></script> | |
<script src="/resource/pubstyles/js/jquery.tabSlideOut.v1.3.js" ></script> | |
<script type="text/javascript"> | |
var j$ = jQuery.noConflict(); | |
j$(document).ready(function(){ | |
j$('#feedback-frame').html('<iframe width="300" height="260" src="/apex/PUBfeedback?url='+ window.location.href + '" frameBorder="0"></iframe>'); | |
j$('.handle').click( function() { | |
j$('#feedback-frame').toggle('fast'); | |
}); | |
j$(function(){ | |
j$('#feedback-widget').tabSlideOut({ | |
tabHandle: '.handle', //class of the element that will be your tab | |
pathToTabImage: '/resource/pubstyles/img/feedback.gif', //path to the image for the tab (optionaly can be set using css) | |
imageHeight: '90px', //height of tab image | |
imageWidth: '30px', //width of tab image | |
tabLocation: 'right', //side of screen where tab lives, top, right, bottom, or left | |
speed: 300, //speed of animation | |
action: 'click', //options: 'click' or 'hover', action to trigger animation | |
topPos: '200px', //position from the top | |
fixedPosition: false //options: true makes it stick(fixed position) on scroll | |
}); | |
j$('#feedback-widget').show(); | |
}); | |
</script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<apex:page controller="PUBfeedbackController" showHeader="false" sidebar="false" standardstylesheets="false"> | |
<apex:includeScript value="{!URLFOR($Resource.jQuery, 'jQuery/jquery-1.8.3.min.js')}" /> | |
<apex:styleSheet value="{!URLFOR($Resource.bootstrap, 'css/bootstrap.min.css')}" /> | |
<apex:includeScript value="{!URLFOR($Resource.JQuery_Raty, 'js/jquery.raty.js')}" /> | |
<apex:styleSheet value="{!URLFOR($Resource.JQuery_Raty, 'css/application.css')}" /> | |
<script> | |
var j$ = jQuery.noConflict(); | |
j$(document).ready(function(){ | |
j$('#star').raty({ | |
path: "{!URLFOR($Resource.JQuery_Raty, 'img/')}", | |
click: function(score, evt) { | |
j$('[id$="feedbackRating"]').val(score); | |
} | |
}); | |
}); | |
</script> | |
<body style="background-color:rgb(247, 247, 249);margin:0px;padding:0px;"> | |
<apex:form style="margin:0px;"> | |
<fieldset style="height:247px;margin:0;"> | |
<apex:outputPanel rendered="{!NOT(submitted)}"> | |
<label>Rate this page</label> | |
<div style="margin-bottom:10px;" id="star"></div> | |
<label>Message</label> | |
<apex:inputTextArea value="{!feedback.Message__c}" rows="5" style="width:95%"></apex:inputTextArea> | |
<div style="margin-top:10px;"> | |
<apex:commandLink styleClass="btn" action="{!Save}">Submit</apex:commandLink> | |
</div> | |
</apex:outputPanel> | |
<apex:outputPanel rendered="{!submitted}"> | |
<div class="alert alert-success"> | |
<span style="font-size:14px;">Thank you for your feedback!</span> | |
</div> | |
</apex:outputPanel> | |
</fieldset> | |
<apex:inputHidden id="feedbackRating" value="{!feedback.Rating__c}" /> | |
</apex:form> | |
</body> | |
</apex:page> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public with sharing class PUBfeedbackController { | |
public Feedback__c feedback { get; set; } | |
public boolean submitted { get; private set; } | |
public PUBfeedbackController() { | |
feedback = new Feedback__c(); | |
map<string,string> params = apexPages.currentPage().getParameters(); | |
feedback.URL__c = params.get('url'); | |
} | |
public pageReference Save() { | |
//only try to save a record if some feedback was given, otherwise just do nothing | |
//we are not opting to incorporate validation since this is such a simple form | |
if (feedback.Rating__c != null || feedback.Message__c != null) { | |
try { | |
insert feedback; | |
submitted = true; | |
} catch (Exception e) { | |
WLMA_Error_Logger.log(e, 'PUBfeedbackController', 'Save', null); | |
} | |
} | |
return null; | |
} | |
/**************************************** | |
* TESTS * | |
****************************************/ | |
static testMethod void submitFeedback() { | |
Test.setCurrentPageReference(new PageReference('PUBfeedback.myPage')); | |
System.currentPageReference().getParameters().put('url', 'http://www.google.com/'); | |
PUBfeedbackController controller = new PUBfeedbackController(); | |
controller.feedback.Message__c = 'The PUB rocks, man, far out'; | |
controller.feedback.Rating__c = 5; | |
controller.Save(); | |
Feedback__c[] feedback = [SELECT Message__c, Rating__c, URL__c FROM Feedback__c LIMIT 1]; | |
system.assertEquals(feedback[0].Message__c, 'The PUB rocks, man, far out'); | |
system.assertEquals(feedback[0].Rating__c, 5); | |
system.assertEquals(feedback[0].URL__c, 'http://www.google.com/'); | |
} | |
static testMethod void throwFeedbackInsertError() { | |
Test.setCurrentPageReference(new PageReference('PUBfeedback.myPage')); | |
System.currentPageReference().getParameters().put('url', 'http://www.google.com/'); | |
PUBfeedbackController controller = new PUBfeedbackController(); | |
controller.feedback.Message__c = 'The PUB rocks, man, far out'; | |
//this should throw an error, since this is a one digit number field | |
controller.feedback.Rating__c = 56; | |
try { | |
controller.Save(); | |
} catch (Exception e) { | |
system.assert(e != null); | |
} | |
} | |
static testMethod void noInsert() { | |
Test.setCurrentPageReference(new PageReference('PUBfeedback.myPage')); | |
System.currentPageReference().getParameters().put('url', 'http://www.google.com/'); | |
PUBfeedbackController controller = new PUBfeedbackController(); | |
controller.Save(); | |
//there was no Rating or Message, so no record should have been inserted | |
Feedback__c[] feedback = [SELECT Message__c, Rating__c, URL__c FROM Feedback__c LIMIT 1]; | |
system.assert(feedback.isEmpty()); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
tabSlideOUt v1.3 (altered by katowulf) | |
Originally by William Paoli: http://wpaoli.building58.com | |
To use you must have an image ready to go as your tab | |
Make sure to pass in at minimum the path to the image and its dimensions: | |
example: | |
$('.slide-out-div').tabSlideOut({ | |
tabHandle: '.handle', //class of the element that will be your tab -doesnt have to be an anchor | |
pathToTabImage: 'images/contact_tab.gif', //relative path to the image for the tab | |
imageHeight: '133px', //height of tab image | |
imageWidth: '44px', //width of tab image | |
}); | |
or you can leave out these options | |
and set the image properties using css | |
*/ | |
(function($){ | |
$.fn.tabSlideOut = function(callerSettings) { | |
var settings = $.extend({ | |
tabHandle: '.handle', | |
speed: 300, | |
action: 'click', | |
tabLocation: 'left', | |
topPos: '200px', | |
leftPos: '20px', | |
fixedPosition: false, | |
positioning: 'absolute', | |
pathToTabImage: null, | |
imageHeight: null, | |
imageWidth: null, | |
onLoadSlideOut: false, | |
tabHandleOffset: 0 | |
}, callerSettings||{}); | |
settings.tabHandle = $(settings.tabHandle); | |
var obj = this; | |
if (settings.fixedPosition === true) { | |
settings.positioning = 'fixed'; | |
} else { | |
settings.positioning = 'absolute'; | |
} | |
//ie6 doesn't do well with the fixed option | |
if (document.all && !window.opera && !window.XMLHttpRequest) { | |
settings.positioning = 'absolute'; | |
} | |
//set initial tabHandle css | |
if (settings.pathToTabImage != null) { | |
settings.tabHandle.css({ | |
'background' : 'url('+settings.pathToTabImage+') no-repeat', | |
'width' : settings.imageWidth, | |
'height': settings.imageHeight | |
}); | |
} | |
settings.tabHandle.css({ | |
'display': 'block', | |
'textIndent' : '-99999px', | |
'outline' : 'none', | |
'position' : 'absolute' | |
}); | |
obj.css({ | |
'line-height' : '1', | |
'position' : settings.positioning | |
}); | |
var properties = { | |
containerWidth: parseInt(obj.outerWidth(), 10) + 'px', | |
containerHeight: parseInt(obj.outerHeight(), 10) + 'px', | |
tabWidth: parseInt(settings.tabHandle.outerWidth(), 10) + 'px', | |
tabHeight: parseInt(settings.tabHandle.outerHeight(), 10) + 'px' | |
}; | |
//set calculated css | |
if(settings.tabLocation === 'top' || settings.tabLocation === 'bottom') { | |
obj.css({'left' : settings.leftPos}); | |
var tabRightOffset = settings.tabHandleOffset==='center'? Math.floor(obj.outerWidth()/2)+'px' : settings.tabHandleOffset; | |
settings.tabHandle.css({'right' : tabRightOffset}); | |
} | |
if(settings.tabLocation === 'top') { | |
obj.css({'top' : '-' + properties.containerHeight}); | |
settings.tabHandle.css({'bottom' : '-' + properties.tabHeight}); | |
} | |
if(settings.tabLocation === 'bottom') { | |
obj.css({'bottom' : '-' + properties.containerHeight, 'position' : 'fixed'}); | |
settings.tabHandle.css({'top' : '-' + properties.tabHeight}); | |
} | |
if(settings.tabLocation === 'left' || settings.tabLocation === 'right') { | |
obj.css({ | |
'height' : properties.containerHeight, | |
'top' : settings.topPos | |
}); | |
var tabTopOffset = settings.tabHandleOffset==='center'? Math.floor(obj.outerHeight()/2)+'px' : settings.tabHandleOffset; | |
settings.tabHandle.css({'top' : tabTopOffset}); | |
} | |
if(settings.tabLocation === 'left') { | |
obj.css({ 'left': '-' + properties.containerWidth}); | |
settings.tabHandle.css({'right' : '-' + properties.tabWidth}); | |
} | |
if(settings.tabLocation === 'right') { | |
obj.css({ 'right': '-' + properties.containerWidth}); | |
settings.tabHandle.css({'left' : '-' + properties.tabWidth}); | |
$('html').css('overflow-x', 'hidden'); | |
} | |
//functions for animation events | |
settings.tabHandle.click(function(event){ | |
event.preventDefault(); | |
}); | |
var slideIn = function() { | |
if (settings.tabLocation === 'top') { | |
obj.animate({top:'-' + properties.containerHeight}, settings.speed).removeClass('open'); | |
} else if (settings.tabLocation === 'left') { | |
obj.animate({left: '-' + properties.containerWidth}, settings.speed).removeClass('open'); | |
} else if (settings.tabLocation === 'right') { | |
obj.animate({right: '-' + properties.containerWidth}, settings.speed).removeClass('open'); | |
} else if (settings.tabLocation === 'bottom') { | |
obj.animate({bottom: '-' + properties.containerHeight}, settings.speed).removeClass('open'); | |
} | |
}; | |
var slideOut = function() { | |
if (settings.tabLocation == 'top') { | |
obj.animate({top:'-3px'}, settings.speed).addClass('open'); | |
} else if (settings.tabLocation == 'left') { | |
obj.animate({left:'-3px'}, settings.speed).addClass('open'); | |
} else if (settings.tabLocation == 'right') { | |
obj.animate({right:'-3px'}, settings.speed).addClass('open'); | |
} else if (settings.tabLocation == 'bottom') { | |
obj.animate({bottom:'-3px'}, settings.speed).addClass('open'); | |
} | |
}; | |
var clickScreenToClose = function() { | |
obj.click(function(event){ | |
event.stopPropagation(); | |
}); | |
$(document).click(function(){ | |
slideIn(); | |
}); | |
}; | |
var clickAction = function(){ | |
settings.tabHandle.click(function(event){ | |
if (obj.hasClass('open')) { | |
slideIn(); | |
} else { | |
slideOut(); | |
} | |
}); | |
clickScreenToClose(); | |
}; | |
var hoverAction = function(){ | |
obj.hover( | |
function(){ | |
slideOut(); | |
}, | |
function(){ | |
slideIn(); | |
}); | |
settings.tabHandle.click(function(event){ | |
if (obj.hasClass('open')) { | |
slideIn(); | |
} | |
}); | |
clickScreenToClose(); | |
}; | |
var slideOutOnLoad = function(){ | |
slideIn(); | |
setTimeout(slideOut, 500); | |
}; | |
//choose which type of action to bind | |
if (settings.action === 'click') { | |
clickAction(); | |
} | |
if (settings.action === 'hover') { | |
hoverAction(); | |
} | |
if (settings.onLoadSlideOut) { | |
slideOutOnLoad(); | |
} | |
}; | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment