ChangeLog: Updated.
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / AdRotator.cs
1 //
2 // System.Web.UI.WebControls.AdRotator.cs
3 //
4 // Authors:
5 //   Gaurav Vaish (gvaish@iitk.ac.in)
6 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //   Andreas Nahr (ClassDevelopment@A-SoftTech.com)
8 //   Sanjay Gupta (gsanjay@novell.com)
9 //
10 // (c) 2002 Ximian, Inc. (http://www.ximian.com)
11 // (C) Gaurav Vaish (2002)
12 // (C) 2003 Andreas Nahr
13 // (C) 2004 Novell, Inc. (http://www.novell.com)
14 //
15
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using System;
38 using System.IO;
39 using System.Collections;
40 using System.Collections.Specialized;
41 using System.Web;
42 using System.Web.Caching;
43 using System.Web.UI;
44 using System.Xml;
45 using System.Web.Util;
46 using System.ComponentModel;
47 using System.ComponentModel.Design;
48
49 namespace System.Web.UI.WebControls
50 {
51         [DefaultEvent("AdCreated")]
52         [DefaultProperty("AdvertisementFile")]
53         [Designer ("System.Web.UI.Design.WebControls.AdRotatorDesigner, " + Consts.AssemblySystem_Design, typeof (IDesigner))]
54         [ToolboxData("<{0}:AdRotator runat=\"server\" Height=\"60px\" "
55         + "Width=\"468\"></{0}:AdRotator>")]
56         public class AdRotator: WebControl
57         {
58                 string advertisementFile;
59                 static readonly object AdCreatedEvent = new object();
60
61                 // Will be set values during (On)PreRender-ing
62                 string alternateText;
63                 string imageUrl;
64                 string navigateUrl;
65                 string fileDirectory;
66                 Random random;
67
68                 public AdRotator ()
69                 {
70                         advertisementFile = "";
71                         fileDirectory     = null;
72                 }
73
74                 AdRecord[] LoadAdFile (string file)
75                 {
76                         Stream fStream;
77                         try {
78                                 fStream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read);
79                         } catch (Exception e) {
80                                 throw new HttpException("AdRotator: Unable to open file", e);
81                         }
82
83                         ArrayList list = new ArrayList ();
84                         try {
85                                 IDictionary hybridDict = null;
86                                 XmlDocument document = new XmlDocument ();
87                                 document.Load (fStream);
88
89                                 XmlElement docElem = document.DocumentElement;
90
91                                 if (docElem == null)
92                                         throw new HttpException ("No advertisements found");
93
94                                 if (docElem.LocalName != "Advertisements")
95                                         throw new HttpException ("No advertisements found: invalid document element");
96
97                                 XmlNode node = docElem.FirstChild;
98                                 while (node != null) {
99                                         if (node.LocalName == "Ad") {
100                                                 XmlNode innerNode = node.FirstChild;
101                                                 while (innerNode != null) {
102                                                         if (node.NodeType == XmlNodeType.Element) {
103                                                                 if (hybridDict == null)
104                                                                         hybridDict = new HybridDictionary ();
105
106                                                                 hybridDict.Add (innerNode.LocalName, innerNode.InnerText);
107                                                         }
108                                                         innerNode = innerNode.NextSibling;
109                                                 }
110
111                                                 if (hybridDict != null) {
112                                                         list.Add (hybridDict);
113                                                         hybridDict = null;
114                                                 }
115                                         }
116                                         node = node.NextSibling;
117                                 }
118
119                         } catch(Exception e) {
120                                 throw new HttpException("Parse error:" + file, e);
121                         } finally {
122                                 if (fStream != null)
123                                         fStream.Close();
124                         }
125
126                         if (list.Count == 0)
127                                 throw new HttpException ("No advertisements found");
128
129                         AdRecord [] adsArray = new AdRecord [list.Count];
130                         int count = list.Count;
131                         for (int i = 0; i < count; i++)
132                                 adsArray [i] = new AdRecord ((IDictionary) list [i]);
133
134                         return adsArray;
135                 }
136
137                 AdRecord [] GetData (string file)
138                 {
139                         string physPath = MapPathSecure (file);
140                         string AdKey = "AdRotatorCache: " + physPath;
141                         fileDirectory = UrlUtils.GetDirectory (UrlUtils.Combine (TemplateSourceDirectory, file));
142                         Cache cache = HttpRuntime.Cache;
143                         AdRecord[] records = (AdRecord[]) cache [AdKey];
144                         if (records == null) {
145                                 records = LoadAdFile (physPath);
146                                 cache.Insert (AdKey, records, new CacheDependency (physPath));
147                         }
148
149                         return records;
150                 }
151
152                 IDictionary SelectAd ()
153                 {
154                         AdRecord[] records = GetData (AdvertisementFile);
155                         if (records == null || records.Length ==0)
156                                 return null;
157
158                         int impressions = 0;
159                         int rlength = records.Length;
160                         for (int i=0 ; i < rlength; i++) {
161                                 if (IsAdMatching (records [i]))
162                                         impressions += records [i].Hits;
163                         }
164
165                         if (impressions == 0)
166                                 return null;
167
168                         if (random == null)
169                                 random = new Random ();
170
171                         int rnd = random.Next (impressions) + 1;
172                         int counter = 0;
173                         int index = 0;
174                         for (int i = 0; i < rlength; i++) {
175                                 if(IsAdMatching(records[i])) {
176                                         if (rnd <= (counter + records [i].Hits)) {
177                                                 index = i;
178                                                 break;
179                                         }
180                                         counter += records [i].Hits;
181                                 }
182                         }
183
184                         return records [index].Properties;
185                 }
186
187                 private bool IsAdMatching (AdRecord currAd)
188                 {
189                         if (KeywordFilter != String.Empty)
190                                 return (0 == String.Compare (currAd.Keyword, KeywordFilter, true));
191
192                         return true;
193                 }
194
195                 private string ResolveAdUrl (string relativeUrl)
196                 {
197                         if (relativeUrl.Length==0 || !UrlUtils.IsRelativeUrl (relativeUrl))
198                                 return relativeUrl;
199
200                         string fullUrl;
201                         if (fileDirectory != null)
202                                 fullUrl = fileDirectory;
203                         else
204                                 fullUrl = TemplateSourceDirectory;
205
206                         if (fullUrl.Length == 0)
207                                 return relativeUrl;
208
209                         return UrlUtils.Combine (fullUrl, relativeUrl);
210                 }
211
212                 [WebCategory("Action")]
213                 [WebSysDescription("AdRotator_OnAdCreated")]
214                 public event AdCreatedEventHandler AdCreated {
215                         add { Events.AddHandler (AdCreatedEvent, value); }
216                         remove { Events.RemoveHandler (AdCreatedEvent, value); }
217                 }
218
219                 [Bindable(true)]
220                 [DefaultValue("")]
221                 [Editor ("System.Web.UI.Design.XmlUrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
222                 [WebCategory("Behavior")]
223                 [WebSysDescription("AdRotator_AdvertisementFile")]
224 #if NET_2_0
225                 [Localizable (true)]
226                 [UrlProperty ()]
227 #endif
228                 public string AdvertisementFile {
229                         get { return ((advertisementFile != null) ? advertisementFile : ""); }
230                         set { advertisementFile = value; }
231                 }
232
233                 [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)]
234                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
235                 public override FontInfo Font {
236                         get { return base.Font; }
237                 }
238
239                 [Bindable(true)]
240                 [DefaultValue("")]
241                 [WebCategory("Behavior")]
242                 [WebSysDescription("AdRotator_KeywordFilter")]
243                 public string KeywordFilter {
244                         get {
245                                 object o = ViewState ["KeywordFilter"];
246                                 if (o != null)
247                                         return (string) o;
248
249                                 return String.Empty;
250                         }
251                         set {
252                                 if(value != null)
253                                         ViewState ["KeywordFilter"] = value.Trim ();
254                         }
255                 }
256
257                 [Bindable(true)]
258                 [DefaultValue("")]
259                 [TypeConverter(typeof(TargetConverter))]
260                 [WebCategory("Behavior")]
261                 [WebSysDescription("AdRotator_Target")]
262                 public string Target {
263                         get {
264                                 object o = ViewState ["Target"];
265                                 if (o != null)
266                                         return (string) o;
267
268                                 return "_top";
269                         }
270                         set {
271                                 ViewState["Target"] = value;
272                         }
273                 }
274
275                 protected override ControlCollection CreateControlCollection ()
276                 {
277                         return new EmptyControlCollection (this);
278                 }
279
280                 protected virtual void OnAdCreated (AdCreatedEventArgs e)
281                 {
282                         if (Events == null)
283                                 return;
284
285                         AdCreatedEventHandler aceh = (AdCreatedEventHandler) Events [AdCreatedEvent];
286                         if (aceh != null)
287                                 aceh (this, e);
288                 }
289
290                 protected override void OnPreRender (EventArgs e)
291                 {
292                         if(AdvertisementFile == String.Empty)
293                                 return;
294
295                         AdCreatedEventArgs acea = new AdCreatedEventArgs (SelectAd ());
296                         OnAdCreated (acea);
297                         imageUrl = acea.ImageUrl;
298                         navigateUrl = acea.NavigateUrl;
299                         alternateText = acea.AlternateText;
300                 }
301
302                 [MonoTODO ("Update method with net 2.0 properties added for AdRotator class")]
303                 protected override void Render (HtmlTextWriter writer)
304                 {
305                         HyperLink hLink = new HyperLink ();
306                         Image adImage = new Image();
307                         foreach (string current in Attributes.Keys)
308                                 hLink.Attributes [current] = Attributes [current];
309
310                         if (ID != null && ID.Length > 0)
311                                 hLink.ID = ID;
312
313                         hLink.Target = Target;
314                         hLink.AccessKey = AccessKey;
315                         hLink.Enabled  = Enabled;
316                         hLink.TabIndex = TabIndex;
317                         if (navigateUrl != null && navigateUrl.Length != 0)
318                                 hLink.NavigateUrl = ResolveAdUrl (navigateUrl);
319
320                         hLink.RenderBeginTag (writer);
321                         if (ControlStyleCreated)
322                                 adImage.ApplyStyle(ControlStyle);
323
324                         if(imageUrl!=null && imageUrl.Length > 0)
325                                 adImage.ImageUrl = ResolveAdUrl (imageUrl);
326
327                         adImage.AlternateText = alternateText;
328                         adImage.ToolTip = ToolTip;
329                         adImage.RenderControl (writer);
330                         hLink.RenderEndTag (writer);
331                 }
332
333 #if NET_2_0
334                 AdType adType;
335                 
336                 [DefaultValueAttribute ("Banner")]
337                 [WebCategoryAttribute ("Behavior")]
338                 [WebSysDescriptionAttribute ("Advertisement of specific type is created by specified value")]
339                 public AdType AdType {
340                         get { return adType; }
341                         set { adType = value; }
342                 }
343
344                 string alternateTextField;
345
346                 [DefaultValueAttribute ("AlternateText")]
347                 [WebCategoryAttribute ("Behavior")]
348                 [WebSysDescriptionAttribute ("Alternate text is retrieved from the elmenet name specified.")]
349                 //[VerificationAttribute ()]
350                 public string AlternateTextField {
351                         get { return alternateTextField; }
352                         set { alternateTextField = value; }
353                 }
354
355                 bool countClicks;
356                 
357                 [DefaultValueAttribute (false)]
358                 [WebCategoryAttribute ("Site Counters")]
359                 [WebSysDescriptionAttribute ("On clicking an advertisement, click-through events should be counted.")]
360                 public bool CountClicks {
361                         get { return countClicks; }
362                         set { countClicks = value; }
363                 }
364
365                 string counterGroup;
366                 
367                 [DefaultValueAttribute ("AdRotator")]
368                 [WebCategoryAttribute ("Site Counters")]
369                 [WebSysDescriptionAttribute ("Name of the group which takes care of counting.")]
370                 public string CounterGroup {
371                         get { return counterGroup; }
372                         set { counterGroup = value; }
373                 }
374
375                 string counterName;
376
377                 [DefaultValueAttribute ("")]
378                 [WebCategoryAttribute ("Site Counters")]
379                 [WebSysDescriptionAttribute ("Name of the group which takes care of counting.")]
380                 public string CounterName {
381                         get { return counterName; }
382                         set { counterName = value; }
383                 }
384
385                 bool countViews;
386                 
387                 [DefaultValueAttribute (false)]
388                 [WebCategoryAttribute ("Site Counters")]
389                 [WebSysDescriptionAttribute ("On creation of an advertisement, view events should be counted.")]
390                 public bool CountViews {
391                         get { return countViews; }
392                         set { countViews = value; }
393                 }
394
395                 string imageUrlField;
396
397                 [DefaultValueAttribute ("ImageUrl")]
398                 [WebCategoryAttribute ("Behavior")]
399                 [WebSysDescriptionAttribute ("Image URL is retrieved from the elmenet name specified.")]
400                 public string ImageUrlField {
401                         get { return imageUrlField; }
402                         set { imageUrlField = value; }
403                 }
404
405                 string navigateUrlField;
406
407                 [DefaultValueAttribute ("NavigateUrl")]
408                 [WebCategoryAttribute ("Behavior")]
409                 [WebSysDescriptionAttribute ("Advertisement Web page URL is retrieved from the elmenet name specified.")]
410                 public string NavigateUrlField {
411                         get { return navigateUrlField; }
412                         set { navigateUrlField = value; }
413                 }
414
415                 int popFrequency;
416
417                 [DefaultValueAttribute ("100")]
418                 [WebCategoryAttribute ("Behavior")]
419                 [WebSysDescriptionAttribute ("Frequency in percentage for creation of Popup or PopUnder advertisement.")]
420                 public int PopFrequency {
421                         get { return popFrequency; }
422                         set { popFrequency = value; }
423                 }
424
425                 int popPositionLeft;
426
427                 [DefaultValueAttribute ("-1")]
428                 [WebCategoryAttribute ("Appearance")]
429                 [WebSysDescriptionAttribute ("Specifies X-coordinate in pixels of Popunder or Popup advertisements top-left corner.")]
430                 public int PopPositionLeft {
431                         get { return popPositionLeft; }
432                         set { popPositionLeft = value; }
433                 }
434
435                 int popPositionTop;
436
437                 [DefaultValueAttribute ("-1")]
438                 [WebCategoryAttribute ("Appearance")]
439                 [WebSysDescriptionAttribute ("Specifies Y-coordinate in pixels of Popunder or Popup advertisements top-left corner.")]
440                 public int PopPositionTop {
441                         get { return popPositionTop; }
442                         set { popPositionTop = value; }
443                 }
444
445                 int rowsPerDay;
446
447                 [DefaultValueAttribute ("-1")]
448                 [WebCategoryAttribute ("Site Counters")]
449                 [WebSysDescriptionAttribute ("On a given day this many number of rows of data needs to be collected.")]
450                 public int RowsPerDay {
451                         get { return rowsPerDay; }
452                         set { rowsPerDay = value; }
453                 }
454  
455                 string siteCountersProvider;
456
457                 [DefaultValueAttribute ("")]
458                 [WebCategoryAttribute ("Site Counters")]
459                 [WebSysDescriptionAttribute ("Control uses the specified provider.")]
460                 public string SiteCountersProvider {
461                         get { return siteCountersProvider; }
462                         set { siteCountersProvider = value; }
463                 }
464
465                 bool trackApplicationName;
466
467                 [DefaultValueAttribute (true)]
468                 [WebCategoryAttribute ("Site Counters")]
469                 [WebSysDescriptionAttribute ("SiteCounters service tracks and stores the specified application name in a database.")]
470                 public bool TrackApplicationName {
471                         get { return trackApplicationName; }
472                         set { trackApplicationName = value; }
473                 }
474
475                 bool trackNavigateUrl;
476
477                 [DefaultValueAttribute (true)]
478                 [WebCategoryAttribute ("Site Counters")]
479                 [WebSysDescriptionAttribute ("SiteCounters service tracks and stores the destination URL of click through event in a database.")]
480                 public bool TrackNavigateUrl {
481                         get { return trackNavigateUrl; }
482                         set { trackNavigateUrl = value; }
483                 }
484
485                 bool trackPageUrl;
486
487                 [DefaultValueAttribute (true)]
488                 [WebCategoryAttribute ("Site Counters")]
489                 [WebSysDescriptionAttribute ("SiteCounters service tracks and stores the originating page URL in a database.")]
490                 public bool TrackPageUrl {
491                         get { return trackPageUrl; }
492                         set { trackPageUrl = value; }
493                 }
494 #endif
495
496                 class AdRecord
497                 {
498                         public IDictionary Properties;
499                         public int Hits; // or impressions or clicks
500                         public string Keyword;
501
502                         public AdRecord (IDictionary adProps)
503                         {
504                                 this.Properties = adProps;
505                                 Keyword = Properties ["Keyword"] as string;
506                                 if (Keyword == null)
507                                         Keyword = "";
508
509                                 string imp = Properties ["Impressions"] as string;
510                                 Hits = 1;
511                                 if (imp != null) {
512                                         try {
513                                                 Hits = Int32.Parse (imp);
514                                         } catch {
515                                         }
516                                 }
517                         }
518                 }
519         }
520 }
521