How to add forreign key fields like it happens in django admin site? - django

Actually am trying to add a foreign key field in my form like how it happens in django admin site . When you click on the green " + " button it opens up a new pop up window where you add the respective field .
My models are like :
class DealType(models.Model):
label = models.CharField(max_length = 100)
def __unicode__(self):
return self.label
class Deal(models.Model):
label = models.ForeignKey(DealType, blank = True, null = True)
.
.
.
And i want to add DealType while i fill up my DealForm .

I think you have to create a seperate view to create a DealType.
In your DealForm you add a link to open that view.
...
I took a look at an admin page from a project of mine.
html
<img src="/static/admin/img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/>
Javascript
taken from
<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"> </script>
function showAddAnotherPopup(triggeringLink) {
var name = triggeringLink.id.replace(/^add_/, '');
name = id_to_windowname(name);
href = triggeringLink.href
if (href.indexOf('?') == -1) {
href += '?_popup=1';
} else {
href += '&_popup=1';
}
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
win.focus();
return false;
}
This opens a new window with the view with the add form.
This view should add the DealType and then close the window using the following function also found in the same javascript file
function dismissAddAnotherPopup(win, newId, newRepr) {
// newId and newRepr are expected to have previously been escaped by
// django.utils.html.escape.
newId = html_unescape(newId);
newRepr = html_unescape(newRepr);
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
if (elem) {
if (elem.nodeName == 'SELECT') {
var o = new Option(newRepr, newId);
elem.options[elem.options.length] = o;
o.selected = true;
} else if (elem.nodeName == 'INPUT') {
if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + newId;
} else {
elem.value = newId;
}
}
} else {
var toId = name + "_to";
elem = document.getElementById(toId);
var o = new Option(newRepr, newId);
SelectBox.add_to_cache(toId, o);
SelectBox.redisplay(toId);
}
win.close();
}
This is just backtracked from the admin panel but it should get you started.
...
Found a guide that walks you through the process here which probably explains alot better. (havn't read it)

Related

SPAlert.Filter not working

Can anybody help with SPAlert filters on Sharepoint 2013?
If I set Filter property on SPAlert instance the alert has not been sent
SPAlert newAlert = user.Alerts.Add();
SPAlertTemplateCollection alertTemplates = new SPAlertTemplateCollection(
(SPWebService)(SPContext.Current.Site.WebApplication.Parent));
newAlert.AlertType = SPAlertType.List;
newAlert.List = list;
newAlert.Title = alertTitle;
newAlert.DeliveryChannels = SPAlertDeliveryChannels.Email;
newAlert.EventType = eventType;
newAlert.AlertFrequency = SPAlertFrequency.Immediate;
newAlert.AlertTemplate = alertTemplates[Constants.AlertTemplates.GenericListCustom];
var wsm = new WorkflowServicesManager(web);
var wss = wsm.GetWorkflowSubscriptionService();
var subscriptions = wss.EnumerateSubscriptionsByList(list.ID);
bool assotiationExist = false;
var guid = Constants.Workflows.ApprovalWF.Guid;
foreach (var subs in subscriptions)
{
assotiationExist = subs.DefinitionId == guid;
if (assotiationExist)
{
newAlert.Filter = "<Query><Eq><FieldRef Name=\"ApprovalStatus\"/><Value type=\"string\">Approved</Value></Eq></Query>";
}
}
newAlert.Update(false);
If I set Filter property on SPAlert instance the alert has not been sent
What do you need exactly ?
If you just want to change the filter (alert condition), did you simply try :
newAlert.AlertType = SPAlertType.List;
newAlert.List = list;
newAlert.Title = alertTitle;
newAlert.DeliveryChannels = SPAlertDeliveryChannels.Email;
newAlert.EventType = eventType;
newAlert.AlertFrequency = SPAlertFrequency.Immediate;
newAlert.AlertTemplate = alertTemplates[Constants.AlertTemplates.GenericListCustom];
newAlert.Filter = "<Query><Eq><FieldRef Name=\"ApprovalStatus/New\"/><Value type=\"string\">Approved</Value></Eq></Query>";
newAlert.Update(false);
I have just added a /New in your filter query. Query filter in alert need to get a /New or a /Old in your field.
If your alert still doesn't work, it might be something else than the filter.
The problem was in line newAlert.EventType = eventType. eventType was SPEventType.Add. That was the reason of not sending alert after Workflow set the ApprovalStatus field to «Approved».
I’ve modified algourithm. Now eventType is SPEventType.Modify and I added new field "IsNewAlertSent" to list. When event fires the first time then I send email and set the "IsNewAlertSent" field
Final code is shown below.
class UserAlertManager:
..
newAlert.EventType = (eventType == SPEventType.Add? SPEventType.Modify: eventType);
newAlert.AlertFrequency = SPAlertFrequency.Immediate;
newAlert.AlertTemplate = alertTemplates[Constants.AlertTemplates.GenericListCustom];
..
if (assotiationExist)
{
newAlert.Filter = "<Query><Eq><FieldRef name=\"ApprovalStatus\"/><Value type=\"Text\">Approved</Value></Eq></Query>";
newAlert.Properties.Add("grcustomalert", "1");
}
..
newAlert.Update(false);
class GRCustomAlertHandler:
...
string subject = string.Empty;
string body = string.Empty;
bool grCustomAlert = Utils.IsSPAlertCustom(ahp.a);
if (ahp.eventData[0].eventType == (int)SPEventType.Modify && grCustomAlert)
{
SPListItem item = list.GetItemById(ahp.eventData[0].itemId);
var isNewAlertSentField = item.Fields.GetFieldByInternalName(Constants.Fields.IsNewAlertSent);
if (isNewAlertSentField != null && (item[Constants.Fields.IsNewAlertSent] == null || !(bool)item[Constants.Fields.IsNewAlertSent]))
{
...
Utils.SendMail(web, new List<string> { ahp.headers["to"].ToString() }, subject, body);
item[Constants.Fields.IsNewAlertSent] = true;
using (new DisabledItemEventScope())
{
item.SystemUpdate(false);
}
}
}
...

Sitecore media item url

I am on Sitecore 7.2
I am experiencing issues trying to retrieve media URL.
I have a template (PageBanner) with just one field called BannerImage. Field type is Image.
Another template named Homepage inherits this template PageBanner.
A content item Home uses template Homepage. I can see the BannerImage field as a part of the Home content item. An image has been assigned to this field as well.
Now, the back-end bit where the issue is encountered.
homeItem.Field["BannerImage"] returns image item.
homeItem["BannerImage"] returns empty string.
If I try to cast it to ImageField -(ImageField)homeItem.Field["BannerImage"], the resultant ImageField item doesn't have MediaItem or any other field set.
I can do :
var imageFieldItem = Sitecore.Context.Database.GetItem(homeItem.Fields["BannerImage"].ID);
var mediaUrl = MediaManager.GetMediaUrl(imageFieldItem);
But that gives me a dynamic media url in the form of -~/media/a2c15f35836746f398e772c81d040607.ashx
I am looking to get the media URL by path.
Any idea what am I missing here?
You are making the correct call to get the URL using the MediaManager but you need to pass the inner MediaItem to the GetMediaUrl() method:
string imageURL = string.Empty;
Sitecore.Data.Fields.ImageField imageField = homeItem.Field["BannerImage"];
if (imageField != null && imageField.MediaItem != null)
{
Sitecore.Data.Items.MediaItem image = new Sitecore.Data.Items.MediaItem(imageField.MediaItem);
imageURL = Sitecore.StringUtil.EnsurePrefix('/', Sitecore.Resources.Media.MediaManager.GetMediaUrl(image));
}
As for the dynamic URL being generated, if it is in Edit mode then this is normal, Check in Normal mode that the media URL is fully rendered.
Try this code out in some utility class.
var imageUrl = GetImageUrl(homeItem, "BannerImage" false);
public static string GetImageUrl(Item item, string fieldname, bool includeServerUrl)
{
// do the checks
if (item == null) { return ""; }
if (fieldname.Length == 0) { return ""; }
// create media options
Sitecore.Resources.Media.MediaUrlOptions mediaUrlOptions = new Sitecore.Resources.Media.MediaUrlOptions { AlwaysIncludeServerUrl = true };
mediaUrlOptions.AbsolutePath = true;
// do we want to include the FQDN?
if (includeServerUrl)
mediaUrlOptions.AlwaysIncludeServerUrl = true;
// convert to image field
Sitecore.Data.Fields.ImageField imagefield = item.Fields[fieldname];
if (imagefield == null) { return ""; }
// get the item so we can process it
Item mediaitem = Sitecore.Context.Database.GetItem(imagefield.MediaID);
if (mediaitem == null) { return ""; }
// pass in the item with the options to get the URL
string mediaurl = Sitecore.Resources.Media.MediaManager.GetMediaUrl(mediaitem, mediaUrlOptions);
if (mediaurl == null) { return ""; }
return mediaurl;
}

SharePoint field required on task form

I have a task form (OOB) where user needs to enter comments only when clicks on rejected button.
I wrote following script but this is firing even when clicked on approved button.
$('input[value=Rejected]').click(function() {
PreSaveAction();
});
});
function PreSaveAction()
{
var commentsBox = getFieldControl('ApprovalComments','Text');
var comments=$("textarea[title='ApprovalComments']").val();
if(comments.length==0)
{
var errorHtml='';
errorHtml = '<span class="ms-formvalidation"><span role="alert">Please enter Comments<br></span> </span>';
commentsBox.after(errorHtml);
return false;
}
return false;
}
function getFieldControl(fieldName,fieldType)
{
var control = $('[id^=' + fieldName + '_][id$=' + fieldType + 'Field]');
return control;
}
Thanks in advance
You can actually achieve it easily. Below is the jquery code sample:
function PreSaveAction()
{
var currentPage = decodeURIComponent(window.location.href.replace(/\+/g, " "));
if (currentPage.contains("Lists/WorkflowTasks/EditForm.aspx"))
{
var commentsTextArea=$("textarea[title='ApprovalComments']");
if(commentsTextArea.val().trim()=="")
{
var errorHtml='';
errorHtml = '<span class="ms-formvalidation"><span role="alert">Please enter Comments<br></span></span>';
commentsTextArea.after(errorHtml);
return false;
}
else
{
return true;
}
}else
{
return true;
}
}
String.prototype.contains = function (it) {
return this.indexOf(it) != -1;
};
Explanation:
PreSaveAction method will be triggered in all the New and Edit Forms in SP2010/SP2013.
Hence there is no need of Button onClick methods for client side validation on these forms.
In the above code, when you click of Save/Approved/Rejected buttons in your page, PreSaveAction will be fired.
We will be checking if current page is of Task Forms (WorkflowTasks is the name of the task List , please update this accordingly), else return true.
If the ApprovalComments is empty then error message will be shown and return false.
Else, return true.
Also notice the helper method String.prototype.contains.

How to pass 2 lists to 1 view

I want to show recent and incoming appointments in my view(one page).Here is my controller where can i add the second list and how can i pass it to same view?I know I cant return two list but there must be way for it?
public ActionResult Index()
{
if (Session["UserEmail"] != null)
{
string Email = (string)Session["UserEmail"];
using (var db = new MaindbModelDataContext())
{
var patient = db.Patients.FirstOrDefault(u => u.Email == (String)Session["UserEmail"]);
ViewBag.FirstName = patient.Name;
ViewBag.LastName = patient.Surname;
ViewBag.BirthDate = patient.Birthday;
ViewBag.Email = patient.Email;
}
using (var db = new MaindbModelDataContext())
{
var patient = db.Patients.FirstOrDefault(u => u.Email == (String)Session["UserEmail"]);
var listrecent = (from y in db.Appointments
where y.PatientNo == patient.PatientNo
where y.Date < DateTime.Today
orderby y.Date descending
select y).Take(5);
var TempRecent = new List<Models.AppModel>();
foreach (var item in listrecent)
{
var Temp = new Models.AppModel();
Temp.AppNo = item.AppNo;
Temp.PatientNo = (Int32)item.PatientNo;
Temp.Date = (DateTime)item.Date;
Temp.Status = item.Status;
Temp.Description = item.Description;
TempRecent.Add(Temp);
}
return View(TempRecent);
}
}
else
{
return RedirectToAction("RegAndLogin", "User");
}
}
}
}
and here is my view part
#model IEnumerable<DentAppSys.Models.AppModel>
#using System.Web.Helpers
#{
ViewBag.Title = "Index";
}
<section class="Patient-Dashboard">
<div id="dashboard_left">
<h1> Recent Appointments</h1>
#{
var Mygrid = new WebGrid(Model, selectionFieldName: "SelectedRow");
}
#Mygrid.GetHtml(
displayHeader: true,
mode: WebGridPagerModes.FirstLast,
columns: Mygrid.Columns
(
Mygrid.Column("Appointment No", "Appointment No",format: #<text>#item.AppNo</text>),
Mygrid.Column("Patient No", "Patient No", format: #<text>#item.PatientNo</text>) ,
Mygrid.Column("Description", "Description", format: #<text>#item.Description</text>),
Mygrid.Column("Date", "Date", format: #<text>#item.Date.ToString("yyyy/MM/dd")</text>),
Mygrid.Column("Status", "Status", format: #<text>#item.Status</text>)
))
</div>
<div id="dashboard_right">
<br/>
<h1>Incoming Appointments</h1>
/* HERE I WANT TO ADD MY SECOND LIST*/
</div>
</section>
Edit:
and after using two instances of the AppModel I get error when I try to equal Temp.RecentIncoming.AppNo=item.AppNo.
using (var db = new MaindbModelDataContext())
{
var patient = db.Patients.FirstOrDefault(u => u.Email == (String)Session["UserEmail"]);
var listincoming = (from y in db.Appointments
where y.PatientNo == patient.PatientNo
where y.Date > DateTime.Today
orderby y.Date descending
select y).Take(5);
var TempIncoming = new List<Models.RecentIncoming>();
foreach (var item in listincoming)
{
var Temp = new Models.RecentIncoming.;
Temp.RecentIncoming.AppNo?????= item.AppNo;
Temp.PatientNo = (Int32)item.PatientNo;
Temp.Date = (DateTime)item.Date;
Temp.Status = item.Status;
Temp.Description = item.Description;
TempIncoming.Add(Temp);
}
return View(TempIncoming);
}
Instead of having IEnumerable as your model, create a new model class that has two instances of the AppModel as well as any other additional data you need to pass to the view...
public class MyAppointments
{
public IEnumerable<DentAppSys.Models.AppModel> RecentAppts;
public IEnumerable<DentAppSys.Models.AppModel> IncomingAppts;
public MyAppointments() { }
}
...
return View( new MyAppointments() { RecentAppts=TempRecent, IncomingAppts=TempIncoming } );
Change the view to...
#model MyAppointments
...
#{
var MyRecentgrid = new WebGrid(Model.RecentAppts, selectionFieldName: "SelectedRow");
var MyIncomingGrid = new WebGrid(Model.IncomingAppts, selectionFieldName: "SelectedRow");
}

Zend Regex route reverse with paginator

I have following regex route, which allows me to use pagination - to parse URL and also build it:
; route "product-{brand}"
product.type = "Zend_Controller_Router_Route_Regex"
product.route = "product-([a-z\-]+)(?:/page/(\d+)/?)?"
product.defaults.module = "default"
product.defaults.controller = "products"
product.defaults.action = "product"
product.defaults.page = "1"
product.map.1 = "brand"
product.map.2 = "page"
product.reverse = "product-%s/page/%d"
Everything is working fine, however, I need to get the rid of default page. Currently we are migrating old web to the Zend and we need to preserve old links because of current google positions, etc.
With default "page", I'm getting always /page/1, without it Zend "cannot assemble" URL.
How to not display page 1 in URL ?
Finally, I've managed to achieve this, but only by rewriting Regex assemble method.
I'm simply overriding two methods.
Class extension:
class Utils_Router_Regex extends Zend_Controller_Router_Route_Regex
{
// instantiate
public static function getInstance(Zend_Config $config)
{
$defs = ($config->defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
$map = ($config->map instanceof Zend_Config) ? $config->map->toArray() : array();
$reverse = (isset($config->reverse)) ? $config->reverse : null;
return new self($config->route, $defs, $map, $reverse);
}
// in this case, we are using $this->_reverse as array from config
public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
{
//// EXTENSION PART !!!
if( !isset( $data[(string) $this->_reverse->pagemap]) ) { // if not set url helper
$reverse = $this->_reverse->base;
} else { // if set in url helper
$reverse = $this->_reverse->paginator;
}
/// end of extension part
if ($reverse === null) {
require_once 'Zend/Controller/Router/Exception.php';
throw new Zend_Controller_Router_Exception('Cannot assemble. Reversed route is not specified.');
}
$defaultValuesMapped = $this->_getMappedValues($this->_defaults, true, false);
$matchedValuesMapped = $this->_getMappedValues($this->_values, true, false);
$dataValuesMapped = $this->_getMappedValues($data, true, false);
// handle resets, if so requested (By null value) to do so
if (($resetKeys = array_search(null, $dataValuesMapped, true)) !== false) {
foreach ((array) $resetKeys as $resetKey) {
if (isset($matchedValuesMapped[$resetKey])) {
unset($matchedValuesMapped[$resetKey]);
unset($dataValuesMapped[$resetKey]);
}
}
}
// merge all the data together, first defaults, then values matched, then supplied
$mergedData = $defaultValuesMapped;
$mergedData = $this->_arrayMergeNumericKeys($mergedData, $matchedValuesMapped);
$mergedData = $this->_arrayMergeNumericKeys($mergedData, $dataValuesMapped);
if ($encode) {
foreach ($mergedData as $key => &$value) {
$value = urlencode($value);
}
}
ksort($mergedData);
$return = #vsprintf($reverse, $mergedData);
if ($return === false) {
require_once 'Zend/Controller/Router/Exception.php';
throw new Zend_Controller_Router_Exception('Cannot assemble. Too few arguments?');
}
return $return;
}
}
Then you can define which sprintf string to use:
hodinky.type = "Utils_Router_Regex"
hodinky.route = "hodinky-([a-z\-]+)(?:/page/(\d+)/?)?"
hodinky.defaults.module = "default"
hodinky.defaults.controller = "products"
hodinky.defaults.action = "hodinky"
hodinky.map.1 = "znacka"
hodinky.map.2 = "page"
hodinky.reverse.paginator = "hodinky-%s/page/%d" ; assebmly with paginator
hodinky.reverse.base = "hodinky-%s/" ; assebmly without paginator
hodinky.reverse.pagemap = "page" ; "page" map key
If you have better solution, please let me know.