NopCommerce: Image size constraints - nopcommerce

Using NopComm 3.90.
Is there a way in NopComm to set an image size constraint?
In Config -> Settings -> Media Settings, I can set the size of the images, but from what I gather this is setting default sizes of images? I need to set a hard constraint on the image sizes that our publishers will have to abide by. Thanks.

If you are using default picture upload in admin panel this method gets triggered during picture insert
/// <summary>
/// Validates input picture dimensions
/// </summary>
/// <param name="pictureBinary">Picture binary</param>
/// <param name="mimeType">MIME type</param>
/// <returns>Picture binary or throws an exception</returns>
public virtual byte[] ValidatePicture(byte[] pictureBinary, string mimeType)
{
using (var destStream = new MemoryStream())
{
ImageBuilder.Current.Build(pictureBinary, destStream, new ResizeSettings
{
MaxWidth = _mediaSettings.MaximumImageSize,
MaxHeight = _mediaSettings.MaximumImageSize,
Quality = _mediaSettings.DefaultImageQuality
});
return destStream.ToArray();
}
}
If you set "MaximumImageSize" in media settings nopCommerce will auto resize large images.
Setting a constraint to enforce a specific minimum picture size does not exists.
But you could easily create a plugin for that.

Related

2sxc dnn sql database and DNN search

If got this code in my razor template:
#using DotNetNuke.Entities.Modules
#using ToSic.Eav.DataSources
#using ToSic.SexyContent.Search
#functions
{
// Customize data not used - but it could often be used in search-demos, so I left it in
public override void CustomizeData()
{
// Don't customize anything, nothing to customize in this case
}
/// <summary>
/// Populate the search - ensure that each entity has an own url/page
/// </summary>
/// <param name="searchInfos"></param>
/// <param name="moduleInfo"></param>
/// <param name="startDate"></param>
public override void CustomizeSearch(Dictionary<string, List<ISearchInfo>> searchInfos, ModuleInfo moduleInfo, DateTime startDate)
{
// all we'll do is tell the search engine that the URL is a bit different for each found entity
foreach (var si in searchInfos["SearchIndex"])
{
si.QueryString = "?id=" + si.Entity.EntityId;
}
}
}
With this data queries:
But the dnn search is not picking up the items? I'm not seeing errors in the dnn logs when the search indexer runs. Do i have to use the Customize data function?
the query i run is this one:
SELECT [Hotel_ID] as EntityId
,[Hotel_ID]
,[Hotel_Name] + ' in ' + [City_Name] as EntityTitle
,[Hotel_Name]
,[Hotel_Address1]
,[Hotel_Address2]
,[Hotel_Address3]
,[Hotel_LAT]
,[Hotel_LNG]
,[Hotel_Website]
,[Hotel_Telephone1]
,[Hotel_Telephone2]
,[Hotel_Email1]
,[Hotel_Email2]
,[Hotel_Fax1]
,[Hotel_Logo]
,[Hotel_ZoomLevel]
,[City_Name]
,[Neighborhood_Name]
,[Departments_Name]
FROM [CI-HotelList]
Can anyone point me in the right direction?

Image resizing for image gallery on Tridion 2011

I'm currently working on a web site that will show kind a image gallery on some detail pages. It must show a navigation at the bottom with small thumbnail images and it must show per each element some basic information and the big image.
The big image must be resized too, because there is a maximun size allowed for them.
The point is to use just a source image per multimedia component and being able to resize the images on publishing time so, from the source image would be sent to the client browser a thumbnail and a big image. It's possible to show small and big images using just styles or HTML, but this is quite uneficient because the source (some of them really heavy) image is always sent to the customer.
My first thought was a custom code fragment, something written in C# but I find complicated to resize only some images to a certain size and then resize them again to another size too. I don't find the way to replace the SRC on the final HTML with the appropiate paths neither.
Another idea was to create an old-style PublishBinary method but I find this really complex because looks like the current Tridion architecture is not meant to do this at all...
And the most important point, even in case we can do the resizing succesfully (somehow) it's currently a Tridion 2011 issue to publish twice the same image. Both the big and the small version would came actually from the same multimedia component so shouldn't be possible to publish both of them or playing with the names, the first one would be allways gone, because the path would be updated with the second one :-S.
Any ideas?
I have built an image re-sizing TBB in the past which reads the output of a Dreamweaver or XSLT template. The idea is to produce a tag like the following with the first template.
<img src="tcm:1-123" maxWidth="250" maxHeight="400"
cropPosition="middle" variantId="250x400"
action="PostProcess" enlargeIfTooSmall="true"
/>
The "Re-Sizing" TBB then post processes the Output item in the package, looking for nodes with the PostProcess action.
It then creates a variant of the Multimedia Component using the System.Drawing library according to the maxHieght and maxWidth dimention attributes, and publishes it using the AddBinary() method #frank mentioned and using the variantId attribute for a filename prefix, and variant id (and replaces the SRC attribute with the URL of the new binary).
To make this 100% flexible, if either of the maxHeight or maxWidth attributes are set to 0, the TBB re-sizes based on just the "non-zero" dimension, or if both are set it crops the image based on the cropPosition attribute. This enables us to make sqare thumbnails for both landscape and portrait images without distorting them. The enlargeIfTooSmall attribute is use to prevent small images from being stretched to much.
You can see samples of the final galleries here: http://medicine.yale.edu/web/help/examples/specific/photo/index.aspx
and other image re-sizeing examples here:
http://medicine.yale.edu/web/help/examples/general/images.aspx
All of the images are just uploaded to the CMS once, and then re-sized and cropped on the fly at publish time.
Tridion can perfectly well publish multiple variants on a single MMC. When you call AddBinary you can specify that this binary is a variant of the MMC, with each variant being identified by a simple string that you specify.
public Binary AddBinary(
Stream content,
string filename,
StructureGroup location,
string variantId,
Component relatedComponent,
string mimeType
)
As you can see you can also specify the filename for the binary. If you do, it is your responsibility that variants have unique filenames and filenames between different MMCs remain unique. So typically, it is easiest to simply prefix or suffix the filename with some indication of the variantId: <MmcImageFileName>_thumbnail.jpg.
For a recent demo project, I took a completely different approach. The binaries are all published to a broker database. They are extracted from the broker with an HttpModule, which writes the binary data to the file system.
I made it possible to encode the desired width or height in the URL of the image (of course, for binaries that are not images this part of the logic will not work). The module then resizes the image on the fly (truly on the fly, not during publishing!) and writes the resized version to the disk.
For example: if I request /Images/photo.jpg, I will get the original image. If I request /Images/photo_h100.jpg, I get a version of 100 pixels high. The url /Images/photo_w150.jpg leads to a width of 150 pixels.
No variants needed, no republishing because of different size requirements either: resizing is completely done on demand! The performance penalty on the server is negligible: each size is generated only once, until the image is republished.
I used .NET, but of course it can work in Java as well.
Following the Frank's and Quirijn's answer you may be interested on resize the image in a Cartridge Claims processor using the Ambient Data Framework. This solution would be technology agnostic and can be re-used in both Java and .Net. You just need to put the resized image bytes in a Claim and then use it in Java or .Net.
Java Claims Processor:
public void onRequestStart(ClaimStore claims) throws AmbientDataException {
int publicationId = getPublicationId();
int binaryId = getBinaryId();
BinaryContentDAO bcDAO = (BinaryContentDAO)StorageManagerFactory.getDAO(publicationId, StorageTypeMapping.BINARY_CONTENT);
BinaryContent binaryContent = bcDAO.findByPrimaryKey(publicationId, binaryId, null);
byte[] binaryBuff = binaryContent.getContent();
pixelRatio = getPixelRatio();
int resizeWidth = getResizeWidth();
BufferedImage original = ImageIO.read(new ByteArrayInputStream(binaryBuff));
if (original.getWidth() < MAX_IMAGE_WIDTH) {
float ratio = (resizeWidth * 1.0f) / (float)MAX_IMAGE_WIDTH;
float width = original.getWidth() * ratio;
float height = original.getHeight() * ratio;
BufferedImage resized = new BufferedImage(Math.round(width), Math.round(height), original.getType());
Graphics2D g = resized.createGraphics();
g.setComposite(AlphaComposite.Src);
g.drawImage(original, 0, 0, resized.getWidth(), resized.getHeight(), null);
g.dispose();
ByteArrayOutputStream output = new ByteArrayOutputStream();
BinaryMeta meta = new BinaryMetaFactory().getMeta(String.format("tcm:%s-%s", publicationId, binaryId));
String suffix = meta.getPath().substring(meta.getPath().lastIndexOf('.') + 1);
ImageIO.write(resized, suffix, output);
binaryBuff = output.toByteArray();
}
claims.put(new URI("taf:extensions:claim:resizedimage"), binaryBuff);
}
.Net HTTP Handler:
public void ProcessRequest(HttpContext context) {
if (context != null) {
HttpResponse httpResponse = HttpContext.Current.Response;
ClaimStore claims = AmbientDataContext.CurrentClaimStore;
if (claims != null) {
Codemesh.JuggerNET.byteArray javaArray = claims.Get<Codemesh.JuggerNET.byteArray>("taf:extensions:claim:resizedimage");
byte[] resizedImage = javaArray.ToNative(javaArray);
if (resizedImage != null && resizedImage.Length > 0) {
httpResponse.Expires = -1;
httpResponse.Flush();
httpResponse.BinaryWrite(resizedImage);
}
}
}
}
Java Filter:
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ClaimStore claims = AmbientDataContext.getCurrentClaimStore();
if (claims != null) {
Object resizedImage = claims.get(new URI("taf:extensions:claim:resizedimage"));
if (resizedImage != null) {
byte[] binaryBuff = (byte[])resizedImage;
response.getOutputStream().write(binaryBuff);
}
}
}

An image from byte to optimized web page presentation

I get the data of the stored image on database as byte[] array;
then I convert it to System.Drawing.Image like the code shown below;
public System.Drawing.Image CreateImage(byte[] bytes)
{
System.IO.MemoryStream memoryStream = new System.IO.MemoryStream(bytes);
System.Drawing.Image image = System.Drawing.Image.FromStream(memoryStream);
return image;
}
(*) On the other hand I am planning to show a list of images on asp.net pages as the client scrolls downs the page. The more user gets down and down on the page he/she does see the more photos. So it means fast page loads and rich user experience. (you may see what I mean on www.mashable.com, just take care the new loads of the photos as you scroll down.)
Moreover, the returned imgae object from the method above, how can i show it in a loop dynamically using the (*) conditions above.
Regards
bk
Well, I think the main bottleneck is actually hitting the database each time you need an image. (Especially considering many users accessing the site.)
I would go with the following solution:
Database will store images with the original quality;
.ashx handler will cache images on the file system in various needed resolutions (like 32x32 pixels for icons, 48x48 for thumbnails, etc.) returning them on request and accessing database only once; (in this example is shown how to return an image via ashx handler)
The actual pages will point to .ashx page to get an image. (like <img scr="GetImage.ashx?ID=324453&Size=48" />)
UPDATE:
So the actual workflow in the handler will be like:
public void ProcessRequest (HttpContext context)
{
// Create path of cached file based on the context passed
int size = Int32.Parse(context.Request["Size"]);
// For ID Guids are possibly better
// but it can be anything, even parameter you need to pass
// to the web service in order to get those bytes
int id = Int32.Parse(context.Request["Id"]);
string imagePath = String.Format(#"images/cache/{0}/{1}.png", size, id);
// Check whether cache image exists and created less than an hour ago
// (create it if necessary)
if (!File.Exists(imagePath)
|| File.GetLastWriteTime(imagePath) < DateTime.Now.AddHours(-1))
{
// Get the file from the web service here
byte[] imageBytes = ...
// Save as a file
using (var memoryStream = new MemoryStream(imageBytes))
using (var outputStream = File.OpenWrite(imagePath))
Image.FromStream(memoryStream).Save(outputStream);
}
context.Response.ContentType = "image/png";
context.Response.WriteFile(imagePath);
}

DevExpress LookUpEdit Behavior

I'm tearing my hair off for this amazing problem.
I'm binding 2 LookUpEdit from code:
MyBinding.DataSource = typeof(MyObject);
MyBinding.DataSource = _dataObject.GetMyList();
firstLookUp.DataBindings.Add("EditValue", MyBinding, "Code");
firstLookUp.Properties.DataSource = MyBinding;
firstLookUp.Properties.ValueMember = "Code";
firstLookUp.Properties.DisplayMember = "Code";
secondLookUp.DataBindings.Add("EditValue", MyBinding, "Info");
secondLookUp.Properties.DataSource = MyBinding;
secondLookUp.Properties.ValueMember = "Info";
secondLookUp.Properties.DisplayMember = "Info";
First problem is: Changing the value on one of the two LookUps not reflecting changing the other one! But im using the same BindingSource, isn't the position the same?
Another one is: They both populate automatically the columns, i dont want to show all columns, tried to remove, exception column not found, if i add, i get duplicate columns!
I don't get it!!!
Changing LookupEdit's EditValue is not directly bound to the BindingSource.Current position.
You have to use something like
lookUpEdit1.Properties.GetDataSourceRowByKeyValue(lookUpEdit1.EditValue)
If you want both LookupEdits linked you are probably better off setting the edit value of the one when the other is changed.
Secondly You should be able to clear the list of Columns like so:
lookUpEdit1.Properties.Columns.Clear();
lookUpEdit1.Properties.Columns.Add(new LookUpColumnInfo("FirstName"));
As said here
http://www.devexpress.com/Support/Center/p/B138420.aspx
http://www.devexpress.com/Support/Center/p/A2275.aspx
LookupEdit does update the Current Property of the BindingSource.
We use the following Code as a workaround:
/// <summary>
/// Wrapper around DevExpress.XtraEditors.LookUpEdit to fix bug with adjusting the BindingSources Current Position
/// </summary>
public sealed class LookUpEditWithDataSource : LookUpEdit
{
private bool firstCall = true;
/// <summary>
/// Called when the edit value changes.
/// </summary>
protected override void OnEditValueChanged()
{
base.OnEditValueChanged();
if (this.Properties.DataSource == null)
{
return;
}
if (this.BindingContext == null)
{
return;
}
if (this.firstCall)
{
this.firstCall = false;
// HACK
// starting and selecting the first item
// doesn't work so we change the position to the first item
this.BindingContext[this.Properties.DataSource].Position = 1;
}
this.BindingContext[this.Properties.DataSource].Position = this.Properties.GetDataSourceRowIndex(this.Properties.ValueMember, this.EditValue);
}
}

Asp.NET - Multiple Instances of AdRotator

I've created a master page that uses 3 instances of System.Web.UI.WebControls.AdRotator each backed by the same XML file of ad elements. For example,
<asp:adRotator AdvertisementFile="/ads.xml" Runat="server"/>
*** ads.xml ***
<Advertisements>
<Ad>
<ImageUrl>/Path/Banner.gif</ImageUrl>
<NavigateUrl>http://www.google.com</NavigateUrl>
<AlternateText>
The best search engine in the world!
</AlternateText>
<Impressions>3</Impressions>
</Ad>
</Advertisements>
The problem is on occasion the same ad will appear in 2 or more of the AdRotators at a given instant.
What's the best way to make the ads presented at any given time unique? One possibility is to separate the ads into 3 distinct XML files and assign each AdRotator a different file. However, that leads to a given ad always being in the same location that may or my not be the "premium" location on the page.
AdRotator wasn't designed to display a series of banners, so you can't prevent duplicates if you place multiple AdRotator controls on a Web Form and point them to the same AdvertisementFile.
The AdRotator is only designed to serve the most basic ad functionality. It's definitely not intended to compete with or replace a "real" ad-serving system. If you need anything more elaborate, you'll need to look into a third-party system or roll your own.
Although I second Rex M's recommendation of rolling out your own AdRotator because of its inherent limitations, there is a way to do this within the AdCreatedEvent of the rotator. For 3 or more rotators try the concept outlined at http://tinyurl.com/7rymect (must scroll to bottom of page to see answer). Otherwise, you could try something along these lines with 2 AdRotators on the page:
/// <summary>
/// First ad rotator control
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void AdRotator_AdCreated(object sender, AdCreatedEventArgs e)
{
//Get displayed ad's id
int currentAd = (int)e.AdProperties["AdId"];
//Remove that ad from the list
TList<Banner> ads = GetBannerThatIsNotThisAdId(currentAd, AdRotator.KeywordFilter);
//Do we have an ad to display?
if (ads.Count > 0)
{
AdRotator1.DataSource = ads;
AdRotator1.DataBind();
}
else //nope, so hide 2nd control
{
AdRotator1.Visible = false;
}
}
public static TList<Banner> GetBannerThatIsNotThisAdId(int adId, string pCategory)
{
BannerService bannerService = new BannerService();
TList<Banner> banners = bannerService.GetAll();
Banner banner = bannerService.GetByAdId(adId);
banners.ApplyFilter(delegate(Banner b) { return b.Keyword.Equals(pCategory) && (b.IsActive.Equals(true)); });
banners.Remove(banner);
return banners;
}

Resources