Related
I'm working on a simple portfolio project. I would like to show images on a webpage that logged in users can edit. My problem is in the [HttpPost] Edit, more specifically this part:
if (ModelState.IsValid)
{
//updating current info
inDb = ModelFactory<ArtSCEn>.GetModel(db, artSCEn.ArtSCEnID);
inDb.LastModified = DateTime.Now;
inDb.TechUsed = artSCEn.TechUsed;
inDb.DateOfCreation = artSCEn.DateOfCreation;
inDb.Description = artSCEn.Description;
inDb.ArtSC.LastModified = DateTime.Now;
//validating img
if (Validator.ValidateImage(img))
{
inDb.ImageString = Image.JsonSerialzeImage(img);
}
else
{
//return to the UI becuase we NEED a valid pic
return View(artSCEn);
}
db.Entry(inDb).State = System.Data.Entity.EntityState.Modified;
db.SaveChanges();
//[PROBLEMATIC PART STARTS HERE]
//updating the pic on the server
//getting the string info
string userArtImgFolder = Server.MapPath($"~/Content/Images/Artistic/{inDb.ArtSC.PersonID}");
string imgNameOnServer = Path.Combine(
userArtImgFolder,
$"{inDb.ArtSC.PersonID}_{inDb.ArtSC.ArtSCID}_{inDb.ArtSCEnID}{Path.GetExtension(img.FileName)}");
//deleting previous pic
System.IO.File.Delete(imgNameOnServer);
//creating a new pic
Image.ResizePropotionatelyAndSave(img, Path.Combine(
userArtImgFolder,
$"{inDb.ArtSC.PersonID}_{inDb.ArtSC.ArtSCID}_{inDb.ArtSCEnID}{Path.GetExtension(img.FileName)}"));
return RedirectToAction("Edit", "Art", new { id = inDb.ArtSCID });
}
When I get back the new picture and I want to delete the previous, System.IO.File.Delete() always triggers an exception that it cannot access the resource, because someone else is holding onto it. Any idea what that might be?
Maybe it's something simple, I'm new to ASP, but just can't figure it out.
UPDATE
Following on the suggestions in the comments section, I checked the processes with a tool called Process Monitor and it seems that indeed IIS is locking the resource:
This one appears 2 more times in the logs, by the way.
Judging by the fact that the operation is CreateFileMapping, I guess it has to do with either Server.MapPath() or Path.Combine(), however, the Server is an IDisposable (being derived from Controller), so can that be the one I should deal with?
Also, the resource I'm trying to delete is an image used on the website, which might be a problem, but that section of the website is not shown during this process.
I found the solution building on the comment of #Diablo.
The IIS was indeed holding on to the resource, but Server.MapPath() or any of that code had nothing to do with it: it was the Edit view my page returning the data to. With the help of this SO answer, it turns out I was careless with a BitMap that I used without a using statement in the view to get some image stats. I updated the helper function with the following code:
public static float GetImageWidthFromPath(string imgAbsolutPath, int offset)
{
float width = 0;
using (Bitmap b = new Bitmap(imgAbsolutPath))
{
width = b.Width - offset;
}
return width;
}
Now IIS does not hold on to the resource and I can delete the file.
I'm getting an error within the kendo.all.min.js when I try and click on one of the nodes in a TreeView - anyone know if I have to further configure it to avoid this?
The error I'm getting is:
JavaScript runtime error: Unable to get property 'set' of undefined or null reference
and the rough area that is highlighted on error by VS is:
return arguments.length?(n=e(n,r).closest(q),r.find(".k-state-selected").each(function(){var e=i.dataItem(this);e.set("selected",!1),delete e.selected}),n.length&&i.dataItem(n).set("selected",!0)...
What i'm after is basically a list of files within a parent folder, and if the file is active and has child files, then I'd like it to give it a URL so they can open the link, but if not, I'd like nothing to happen, but I don't want to completely disable the node as I'd like the user to be able to expand it or shrink it if required.
Here is the code I'm using for the treeview in MVC:
#(Html.Kendo().TreeView()
.Name("treeview")
.Items(level1 =>
{
level1.Add().Text(rootFolderName)
.SpriteCssClasses("folder")
.Expanded(true)
.Items(level2 =>
{
int count = 0;
foreach (var node in list)
{
string title = node.Title;
string url = "";
if (node.HasChildren)
{
title = node.Title + "(" + node.ChildrenCount + ")";
url = "/Secure/Areas/Compliance.aspx?id=" + node.ItemId;
}
level2.Add().Text(title).Url(url)
.Expanded(true);
}
});
})
)
Anyone used these before and know what else I'm needing to do to achieve my aim?
Ok, learning more as I go. Ended up using an event like so:
#(Html.Kendo().TreeView()
.Name("treeview")
.Events(events => events
.Select("onSelect")
)
.Items(level1 =>
and then added a preventDefault if the url is empty
function onSelect(e) {
var dataItem = $('#treeview').data('kendoTreeView').dataItem(e.node);
if (dataItem.href == 'undefined')
e.preventDefault();
else
location.href = dataItem.href;
}
There is maybe a better way, but this seems to work so I'm happy for now.
I have a document library in sharepoint storing a word document.
If I click on the link to the document I get a dialog box with "you want to open this file in readonly or editmode etc" and can open it in edit mode, change it, save it directly in word an the changes are saved in the document library.
The link to the file in the document library looks like this:
<a onfocus="OnLink(this)"
href="/test/DocLib2/wordtest.docx"
onmousedown="return VerifyHref(this,event,'1','SharePoint.OpenDocuments','')"
onclick="return DispEx(this,event,'TRUE','FALSE','FALSE',
'SharePoint.OpenDocuments.3','1', 'SharePoint.OpenDocuments',
'','','','1','0','0','0x7fffffffffffffff','','')"
>wordtest</a>
How do I create this link in my own web part where I have the name of the file and document library? Without just copying the above code, that wouldn't be a good idea...
Is there some "official" method to achieve this?
Unfortunately it doesn't seem like there is a better option. But at least you can sort of figure out what the function definition is. The DispEx function is defined in the core.js file (but it's easier to read in the core.debug.js). Both are in 14\Templates\Layouts\1033 directory.
Here is the function definition:
function DispEx(ele, objEvent, fTransformServiceOn, fShouldTransformExtension,
fTransformHandleUrl, strHtmlTrProgId, iDefaultItemOpen, strProgId, strHtmlType,
strServerFileRedirect, strCheckoutUser, strCurrentUser, strRequireCheckout,
strCheckedoutTolocal, strPermmask)
Here is my guess on what they mean. Please feel free to add comments to correct any mistakes or omissions:
ele - [obj] the element
objEvent - [obj] the event object
fTransformServiceOn - [bool] (unknown functionality) defaults to True
fShouldTransformExtension - [bool] (unknown functionality) defaults to False
fTransformHandleUrl - [bool] (unknown functionality) defaults to False
strHtmlTrProgId - [string] name of the ActiveXControl to try to load defaults to SharePoint.OpenDocuments.3
iDefaultItemOpen - [int] indicator of default to Edit or Read defaults to 1
strProgId - [string] name of the ActiveX Control
strHtmlType [string] (unknown functionality) defaults to empty
strServerFileRedirect - [string] (unknown functionality)
strCheckoutUser [string] the ID of the user who has checked out the document
strCurrentUser - [string] the ID of the current user
strRequireCheckout - [string] indicator whether to force a checkout
strCheckedoutTolocal - [string] indicator of whether to use the Local Drafts folder
strPermmask - [string] permissions mask for the current user defaults to 0x7fffffffffffffff
There are clearly some inconsistencies in terms of using strings and integers to represent boolean values. It's also strange that your code has 17 parameters but I can only find a function definition with 15 parameters, so I'm not sure what those last two empty strings are for. Some of that is the nature of JavaScript, but it also just looks sloppy on the part of Microsoft.
This doesn't really answer the question, hopefully it helps you or someone else.
Chad Schroeder made a blog post on how to construct the javascript function call in C#. Taking into account a couple of settings, like force checkout and open in browser or client for instance.
private string GetFileViewScript(SPFile file)
{
string text = SPUtility.MapToControl(SPContext.Current.Web, file.Name, string.Empty);
string text2 = (file.Item.ParentList.DefaultItemOpen == DefaultItemOpen.Browser) ? "1" : "0";
SPFieldLookupValue sPFieldLookupValue = file.Item["CheckedOutUserId"] as SPFieldLookupValue;
string scriptLiteralToEncode = (sPFieldLookupValue == null) ? string.Empty : sPFieldLookupValue.LookupValue;
string text3 = (SPContext.Current.Web.CurrentUser != null) ? SPContext.Current.Web.CurrentUser.ID.ToString(CultureInfo.InvariantCulture) : string.Empty;
string text4 = file.Item.ParentList.ForceCheckout ? "1" : "0";
return string.Format(CultureInfo.InvariantCulture, "return DispEx(this,event,'{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}')", new object[]
{
"TRUE",
"FALSE",
"FALSE",
text,
text2,
text,
string.Empty,
string.Empty,
SPHttpUtility.EcmaScriptStringLiteralEncode(scriptLiteralToEncode),
text3,
text4,
(string)file.Item["IsCheckedoutToLocal"],
(string)file.Item["PermMask"]
});
}
Using DispEx in a link to a SharePoint document
I end up with adding this code
return DispEx(this,event,'TRUE','FALSE','FALSE',
'SharePoint.OpenDocuments.3','1', 'SharePoint.OpenDocuments','','','',
'1','0','0','0x7fffffffffffffff','','')
to my link tag because I wasn't able to find a better solution.
If there is any, please let me know.
DispEx does not work in Chrome unless the link is within a div that contains the document type in an attribute called app:
<div class="ms-vb itx" ctxname="ctx19" id="2" app="ms-word">
<a onfocus="OnLink(this)"
href="/test/DocLib2/wordtest.docx"
onmousedown="return VerifyHref(this,event,'1','SharePoint.OpenDocuments','')"
onclick="return DispEx(this,event,'TRUE','FALSE','FALSE',
'SharePoint.OpenDocuments.3','1', 'SharePoint.OpenDocuments',
'','','','1','0','0','0x7fffffffffffffff','','')">wordtest</a>
<span class="ms-newdocument-iconouter">
<img class="ms-newdocument-icon" src="/_layouts/15/images/spcommon.png?rev=23" alt="new" title="new">
</span>
</div>
Either you need to wrap it in such a div, and be sure to insert the correct application that will open the file, or make your own list by looking at the file extension:
$('.test_links').click(function(e) {
e.preventDefault();
if (!!window.chrome) {
var extenstion = this.href.substr(this.href.lastIndexOf('.') + 1);
var prefix = '';
switch (extenstion) {
case 'doc':
case 'docx':
prefix = 'ms-word:ofv|u|';
break;
case 'xls':
case 'xlsx':
prefix = 'ms-excel:ofv|u|';
break;
}
window.location.href = prefix + this.href;
} else {
DispEx(this, e, 'TRUE', 'FALSE', 'FALSE', 'SharePoint.OpenDocuments.3', '0', 'SharePoint.OpenDocuments', '', '', '', _spPageContextInfo.userId + '', '0', '0', '0x7fffffffffffffff');
}
});
I don't remember if there is an official ability to do it with JavaScript COM, but you can use the ASP.NET HyperLink control to generate the similar link. For instance put in layout
<asp:HyperLink ID="EditHl" runat="server" Text="Edit document"/>
and in code-behind something like
EditHl.Attributes["attribute name"] = "attribute value";
just use the same values from OOTB link, but change
/test/DocLib2/wordtest.docx
to URL of your document.
Here's the deal. Have a functioning web app using ASP.NET WebForms with a C# backend. The thing works fine, but I'm always looking to improve, as a beginner at this stuff. Right now, to deal with a user's search coming back with no results, I utilize the following, and was wondering if there was any cleaner way to do it, for future reference:
DataClass data = new DataClass();
var searchresults = data.GetData(searchBox.Text);
int datanumber = searchresults.Count();
if (datanumber == 0)
{
ClientScript.RegisterStartupScript(this.GetType(), "alert", "javascript:alert('There were no records found to match your search');", true);
}
else
{
DropDownList1.Visible = true;
DropDownList1.Items.Clear();
DropDownList1.DataSource = searchresults;
DropDownList1.DataBind();
}
I agree with the not using popups, so you could always do something as simple as having a Label object on your page:
<asp:Label runat="server" id="lblResultMsg" ForeColor="Red" Visible="False" />
And then set the text dynamically (or add it as a property to the code) and set the label to be visible on postback if no results are found:
if (datanumber == 0)
{
lblResultMsg.Text = "There were no records found to match your search.";
lblResultMsg.Visible = true;
}
else
{
lblResultMsg.Text = "";
lblResultMsg.Visible = false;
// do your data binding
}
But there are quite a vast number of ways you could achieve something like this. Regarding your question about using the .Count from the Enumerable collection - there's nothing stopping you doing this as it's perfectly valid. The question is which method do you find more readable?
if you include the jquery ui dialog (http://jqueryui.com/demos/dialog/), you can simply call this to create a nice dialog box:
$('<div>message</div>').dialog({autoOpen:true,title:'Error'});
Personally I prefer to create a helper function for inserting the relevant javascript into the page, and only pass parameters to the function so that I don't need to worry about the messy details every time.
Something like :
public static void GrowlMessage(System.Web.UI.Control pageControl, string header = "", string message = "", bool sticky = false, string position = "top-right", string theme = "", bool closer = true, int life = 8)
{
string _js = "$.jGrowl('" + HttpContext.Current.Server.HtmlEncode(message) + "', { header:'" + header + "', sticky:" + sticky.ToString().ToLower() + ", position: '" + position + "', theme: '" + theme + "', closer: " + closer.ToString().ToLower() + ", life:" + life * 1000 + "});";
ScriptManager.RegisterStartupScript(pageControl, pageControl.GetType(),"Growl",_js, true);
}
The sample I have used also requires jQuery and the jGrowl library available here. And IMHO the messages are pretty. They are unobtrusive, the user does not need to click a button to make them go away, and they fade away after your specified amount of time.
But I agree with Mike, that if you don't have any records, you should just use the built in properties of a GridView (EmptyDataRowStyle and EmptyDataRowText) to display a 'no data matching your query' style message. Assuming that you're using a GridView at all, that is..
When it comes to user feedback, Impromptu is my friend. There is a nice ASP.NET implementation of Impromptu on Aaron Goldenthal's website: http://www.aarongoldenthal.com/post/2009/11/11/Using-jQuery-Impromptu-With-ASPNET.aspx
If you have decided to alert user via alert then please go ahead with light box effect..
http://www.designyourway.net/blog/resources/30-efficient-jquery-lightbox-plugins/
if you are still would like to go ahead with traditional alert then obviously its easy for you to fire it up on page load rather than attaching script to it..
')" ....>
Because if you require any change then you just need to alter the javascript alone and you dont need to build project again to test it...
Hope its useful for you..
Note: I'm using my own DLLs to render content so above coding may requires alteration because i did forget traditional asp codings.. :)
Gents,
I need help to create an imagemap to my web app's charts. After reading many resources, here and on the web, I end up with the following problem:
The Problem:
On my view I' am calling #Html.Action(RenderMyMap), that renders an <map> tag, with an atribute coords=0,0,0,0.
What Am I doing:
My project is organized this way:
-Core.cs: Class responsable to create the System.Web.UI.DataVisualization.Charting.Chart objects;
-ClientController: With an existing Chart object, I'm saving it with Chat.SaveImage(memoryStream,format), storing it in a session variable as a byte array, and returning a ContentResult object, like in return Content(chart.GetHtmlImageMap());
-ClientController: After that, I'am using the session variable to actually render the chart, like in return File(chartByteArray,"image/png");
-Index.cshtml: Finally in my view I'am calling method on the ClientController to render the map and the chart.
My setup:
.NET 4.0, MVC3, SQL Server 2008.
Bellw goes the code of one of my methods, on the ClientController, to generate and return n ImageMap.
public ActionResult ChartMapGetClientsByType()
{
System.Web.UI.DataVisualization.Charting.Chart chart = null;
if (Session["GraficoClientePorTipo"] != null)
{
chart = (System.Web.UI.DataVisualization.Charting.Chart)Session["GraficoClientePorTipo"];
Session.Remove("GraficoClientePorTipo");
}
else
{
chart = MeuMRP.Core.Chart.CreateChartImageMap("GraficoClientePorTipo", SeriesChartType.Pie);
chart.IsMapEnabled = true;
System.IO.MemoryStream ms = new System.IO.MemoryStream();
chart.SaveImage(ms, ChartImageFormat.Png);
Session["GraficoClientePorTipo"] = ms.ToArray();
}
return Content(chart.GetHtmlImageMap("GraficoClientePorTipo"));
}
What Am I doing wrong?
You should set the Url property to the Chart.Series object. For example:
chart.Series[0].Url = "#";
This error resulted for me on a complex chart with a set of BoxPlots.
For a simple Points chart, there was no error, the <map> was fine with coordinates and attributes.
In order for the map area coordinates to be generated, you need to add a ToolTip or a URL or MapAreaAttributes: e.g.
series.Points[idx].ToolTip = tooltip;
or
series.Points[idx].MapAreaAttributes = "onmouseover=\"DisplayTooltip('" + tooltip +"');\" onmouseout=\"DisplayTooltip('');\"";
But I found the ToolTip to be useless because it added nothing to the <area> in the <map> except for the coordinates (what am I doing wrong?). I did not test assigning a Url, assuming that works as asserted in another answer.
Adding the mouseover and mouseout MapAreaAttributes works just fine resulting in this:
<area onmouseover="DisplayTooltip('99');" onmouseout="DisplayTooltip('');" shape="rect" coords="98,44,103,49" alt="">
FYI You can find more details on setting up Web.UI Charting in MVC, at various URI, including
http://www.codeproject.com/Articles/297677/Using-ASP-Net-Charting-with-Image-Map-in-MVC
http://geekswithblogs.net/DougLampe/archive/2011/01/23/charts-in-asp.net-mvc-2-with-drill-down.aspx.