2005-02-02 Lluis Sanchez Gual <lluis@novell.com>
[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: 
57 #if NET_2_0
58                 DataBoundControl
59 #else
60                 WebControl
61 #endif
62         {
63                 string advertisementFile;
64                 static readonly object AdCreatedEvent = new object();
65
66                 // Will be set values during (On)PreRender-ing
67                 string alternateText;
68                 string imageUrl;
69                 string navigateUrl;
70                 string fileDirectory;
71                 Random random;
72
73                 public AdRotator ()
74                 {
75                         advertisementFile = "";
76                         fileDirectory     = null;
77                 }
78
79                 AdRecord[] LoadAdFile (string file)
80                 {
81                         Stream fStream;
82                         try {
83                                 fStream = new FileStream (file, FileMode.Open, FileAccess.Read, FileShare.Read);
84                         } catch (Exception e) {
85                                 throw new HttpException("AdRotator: Unable to open file", e);
86                         }
87
88                         ArrayList list = new ArrayList ();
89                         try {
90                                 IDictionary hybridDict = null;
91                                 XmlDocument document = new XmlDocument ();
92                                 document.Load (fStream);
93
94                                 XmlElement docElem = document.DocumentElement;
95
96                                 if (docElem == null)
97                                         throw new HttpException ("No advertisements found");
98
99                                 if (docElem.LocalName != "Advertisements")
100                                         throw new HttpException ("No advertisements found: invalid document element");
101
102                                 XmlNode node = docElem.FirstChild;
103                                 while (node != null) {
104                                         if (node.LocalName == "Ad") {
105                                                 XmlNode innerNode = node.FirstChild;
106                                                 while (innerNode != null) {
107                                                         if (node.NodeType == XmlNodeType.Element) {
108                                                                 if (hybridDict == null)
109                                                                         hybridDict = new HybridDictionary ();
110
111                                                                 hybridDict.Add (innerNode.LocalName, innerNode.InnerText);
112                                                         }
113                                                         innerNode = innerNode.NextSibling;
114                                                 }
115
116                                                 if (hybridDict != null) {
117                                                         list.Add (hybridDict);
118                                                         hybridDict = null;
119                                                 }
120                                         }
121                                         node = node.NextSibling;
122                                 }
123
124                         } catch(Exception e) {
125                                 throw new HttpException("Parse error:" + file, e);
126                         } finally {
127                                 if (fStream != null)
128                                         fStream.Close();
129                         }
130
131                         if (list.Count == 0)
132                                 throw new HttpException ("No advertisements found");
133
134                         AdRecord [] adsArray = new AdRecord [list.Count];
135                         int count = list.Count;
136                         for (int i = 0; i < count; i++)
137                                 adsArray [i] = new AdRecord ((IDictionary) list [i]);
138
139                         return adsArray;
140                 }
141
142                 AdRecord [] GetData (string file)
143                 {
144                         string physPath = MapPathSecure (file);
145                         string AdKey = "AdRotatorCache: " + physPath;
146                         fileDirectory = UrlUtils.GetDirectory (UrlUtils.Combine (TemplateSourceDirectory, file));
147                         Cache cache = HttpRuntime.Cache;
148                         AdRecord[] records = (AdRecord[]) cache [AdKey];
149                         if (records == null) {
150                                 records = LoadAdFile (physPath);
151                                 cache.Insert (AdKey, records, new CacheDependency (physPath));
152                         }
153
154                         return records;
155                 }
156
157                 IDictionary SelectAd ()
158                 {
159                         AdRecord[] records = GetData (AdvertisementFile);
160                         if (records == null || records.Length ==0)
161                                 return null;
162
163                         int impressions = 0;
164                         int rlength = records.Length;
165                         for (int i=0 ; i < rlength; i++) {
166                                 if (IsAdMatching (records [i]))
167                                         impressions += records [i].Hits;
168                         }
169
170                         if (impressions == 0)
171                                 return null;
172
173                         if (random == null)
174                                 random = new Random ();
175
176                         int rnd = random.Next (impressions) + 1;
177                         int counter = 0;
178                         int index = 0;
179                         for (int i = 0; i < rlength; i++) {
180                                 if(IsAdMatching(records[i])) {
181                                         if (rnd <= (counter + records [i].Hits)) {
182                                                 index = i;
183                                                 break;
184                                         }
185                                         counter += records [i].Hits;
186                                 }
187                         }
188
189                         return records [index].Properties;
190                 }
191
192                 private bool IsAdMatching (AdRecord currAd)
193                 {
194                         if (KeywordFilter != String.Empty)
195                                 return (0 == String.Compare (currAd.Keyword, KeywordFilter, true));
196
197                         return true;
198                 }
199
200                 private string ResolveAdUrl (string relativeUrl)
201                 {
202                         if (relativeUrl.Length==0 || !UrlUtils.IsRelativeUrl (relativeUrl))
203                                 return relativeUrl;
204
205                         string fullUrl;
206                         if (fileDirectory != null)
207                                 fullUrl = fileDirectory;
208                         else
209                                 fullUrl = TemplateSourceDirectory;
210
211                         if (fullUrl.Length == 0)
212                                 return relativeUrl;
213
214                         return UrlUtils.Combine (fullUrl, relativeUrl);
215                 }
216
217                 [WebCategory("Action")]
218                 [WebSysDescription("AdRotator_OnAdCreated")]
219                 public event AdCreatedEventHandler AdCreated {
220                         add { Events.AddHandler (AdCreatedEvent, value); }
221                         remove { Events.RemoveHandler (AdCreatedEvent, value); }
222                 }
223
224                 [Bindable(true)]
225                 [DefaultValue("")]
226                 [Editor ("System.Web.UI.Design.XmlUrlEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
227                 [WebCategory("Behavior")]
228                 [WebSysDescription("AdRotator_AdvertisementFile")]
229 #if NET_2_0
230                 [Localizable (true)]
231                 [UrlProperty ()]
232 #endif
233                 public string AdvertisementFile {
234                         get { return ((advertisementFile != null) ? advertisementFile : ""); }
235                         set { advertisementFile = value; }
236                 }
237
238                 [Browsable (false), EditorBrowsable (EditorBrowsableState.Never)]
239                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
240                 public override FontInfo Font {
241                         get { return base.Font; }
242                 }
243
244                 [Bindable(true)]
245                 [DefaultValue("")]
246                 [WebCategory("Behavior")]
247                 [WebSysDescription("AdRotator_KeywordFilter")]
248                 public string KeywordFilter {
249                         get {
250                                 object o = ViewState ["KeywordFilter"];
251                                 if (o != null)
252                                         return (string) o;
253
254                                 return String.Empty;
255                         }
256                         set {
257                                 if(value != null)
258                                         ViewState ["KeywordFilter"] = value.Trim ();
259                         }
260                 }
261
262                 [Bindable(true)]
263                 [DefaultValue("")]
264                 [TypeConverter(typeof(TargetConverter))]
265                 [WebCategory("Behavior")]
266                 [WebSysDescription("AdRotator_Target")]
267                 public string Target {
268                         get {
269                                 object o = ViewState ["Target"];
270                                 if (o != null)
271                                         return (string) o;
272
273                                 return "_top";
274                         }
275                         set {
276                                 ViewState["Target"] = value;
277                         }
278                 }
279
280                 protected override ControlCollection CreateControlCollection ()
281                 {
282                         return new EmptyControlCollection (this);
283                 }
284
285                 protected virtual void OnAdCreated (AdCreatedEventArgs e)
286                 {
287                         if (Events == null)
288                                 return;
289
290                         AdCreatedEventHandler aceh = (AdCreatedEventHandler) Events [AdCreatedEvent];
291                         if (aceh != null)
292                                 aceh (this, e);
293                 }
294
295                 protected override void OnPreRender (EventArgs e)
296                 {
297                         if(AdvertisementFile == String.Empty)
298                                 return;
299
300                         AdCreatedEventArgs acea = new AdCreatedEventArgs (SelectAd ());
301                         OnAdCreated (acea);
302                         imageUrl = acea.ImageUrl;
303                         navigateUrl = acea.NavigateUrl;
304                         alternateText = acea.AlternateText;
305                 }
306
307                 [MonoTODO ("Update method with net 2.0 properties added for AdRotator class")]
308                 protected override void Render (HtmlTextWriter writer)
309                 {
310                         HyperLink hLink = new HyperLink ();
311                         Image adImage = new Image();
312                         foreach (string current in Attributes.Keys)
313                                 hLink.Attributes [current] = Attributes [current];
314
315                         if (ID != null && ID.Length > 0)
316                                 hLink.ID = ID;
317
318                         hLink.Target = Target;
319                         hLink.AccessKey = AccessKey;
320                         hLink.Enabled  = Enabled;
321                         hLink.TabIndex = TabIndex;
322                         if (navigateUrl != null && navigateUrl.Length != 0)
323                                 hLink.NavigateUrl = ResolveAdUrl (navigateUrl);
324
325                         hLink.RenderBeginTag (writer);
326                         if (ControlStyleCreated)
327                                 adImage.ApplyStyle(ControlStyle);
328
329                         if(imageUrl!=null && imageUrl.Length > 0)
330                                 adImage.ImageUrl = ResolveAdUrl (imageUrl);
331
332                         adImage.AlternateText = alternateText;
333                         adImage.ToolTip = ToolTip;
334                         adImage.RenderControl (writer);
335                         hLink.RenderEndTag (writer);
336                 }
337
338 #if NET_2_0
339                 AdType adType;
340                 
341                 [DefaultValueAttribute ("Banner")]
342                 [WebCategoryAttribute ("Behavior")]
343                 [WebSysDescriptionAttribute ("Advertisement of specific type is created by specified value")]
344                 public AdType AdType {
345                         get { return adType; }
346                         set { adType = value; }
347                 }
348
349                 string alternateTextField;
350
351                 [DefaultValueAttribute ("AlternateText")]
352                 [WebCategoryAttribute ("Behavior")]
353                 [WebSysDescriptionAttribute ("Alternate text is retrieved from the elmenet name specified.")]
354                 //[VerificationAttribute ()]
355                 public string AlternateTextField {
356                         get { return alternateTextField; }
357                         set { alternateTextField = value; }
358                 }
359
360                 bool countClicks;
361                 
362                 [DefaultValueAttribute (false)]
363                 [WebCategoryAttribute ("Site Counters")]
364                 [WebSysDescriptionAttribute ("On clicking an advertisement, click-through events should be counted.")]
365                 public bool CountClicks {
366                         get { return countClicks; }
367                         set { countClicks = value; }
368                 }
369
370                 string counterGroup;
371                 
372                 [DefaultValueAttribute ("AdRotator")]
373                 [WebCategoryAttribute ("Site Counters")]
374                 [WebSysDescriptionAttribute ("Name of the group which takes care of counting.")]
375                 public string CounterGroup {
376                         get { return counterGroup; }
377                         set { counterGroup = value; }
378                 }
379
380                 string counterName;
381
382                 [DefaultValueAttribute ("")]
383                 [WebCategoryAttribute ("Site Counters")]
384                 [WebSysDescriptionAttribute ("Name of the group which takes care of counting.")]
385                 public string CounterName {
386                         get { return counterName; }
387                         set { counterName = value; }
388                 }
389
390                 bool countViews;
391                 
392                 [DefaultValueAttribute (false)]
393                 [WebCategoryAttribute ("Site Counters")]
394                 [WebSysDescriptionAttribute ("On creation of an advertisement, view events should be counted.")]
395                 public bool CountViews {
396                         get { return countViews; }
397                         set { countViews = value; }
398                 }
399
400                 string imageUrlField;
401
402                 [DefaultValueAttribute ("ImageUrl")]
403                 [WebCategoryAttribute ("Behavior")]
404                 [WebSysDescriptionAttribute ("Image URL is retrieved from the elmenet name specified.")]
405                 public string ImageUrlField {
406                         get { return imageUrlField; }
407                         set { imageUrlField = value; }
408                 }
409
410                 string navigateUrlField;
411
412                 [DefaultValueAttribute ("NavigateUrl")]
413                 [WebCategoryAttribute ("Behavior")]
414                 [WebSysDescriptionAttribute ("Advertisement Web page URL is retrieved from the elmenet name specified.")]
415                 public string NavigateUrlField {
416                         get { return navigateUrlField; }
417                         set { navigateUrlField = value; }
418                 }
419
420                 int popFrequency;
421
422                 [DefaultValueAttribute ("100")]
423                 [WebCategoryAttribute ("Behavior")]
424                 [WebSysDescriptionAttribute ("Frequency in percentage for creation of Popup or PopUnder advertisement.")]
425                 public int PopFrequency {
426                         get { return popFrequency; }
427                         set { popFrequency = value; }
428                 }
429
430                 int popPositionLeft;
431
432                 [DefaultValueAttribute ("-1")]
433                 [WebCategoryAttribute ("Appearance")]
434                 [WebSysDescriptionAttribute ("Specifies X-coordinate in pixels of Popunder or Popup advertisements top-left corner.")]
435                 public int PopPositionLeft {
436                         get { return popPositionLeft; }
437                         set { popPositionLeft = value; }
438                 }
439
440                 int popPositionTop;
441
442                 [DefaultValueAttribute ("-1")]
443                 [WebCategoryAttribute ("Appearance")]
444                 [WebSysDescriptionAttribute ("Specifies Y-coordinate in pixels of Popunder or Popup advertisements top-left corner.")]
445                 public int PopPositionTop {
446                         get { return popPositionTop; }
447                         set { popPositionTop = value; }
448                 }
449
450                 int rowsPerDay;
451
452                 [DefaultValueAttribute ("-1")]
453                 [WebCategoryAttribute ("Site Counters")]
454                 [WebSysDescriptionAttribute ("On a given day this many number of rows of data needs to be collected.")]
455                 public int RowsPerDay {
456                         get { return rowsPerDay; }
457                         set { rowsPerDay = value; }
458                 }
459  
460                 string siteCountersProvider;
461
462                 [DefaultValueAttribute ("")]
463                 [WebCategoryAttribute ("Site Counters")]
464                 [WebSysDescriptionAttribute ("Control uses the specified provider.")]
465                 public string SiteCountersProvider {
466                         get { return siteCountersProvider; }
467                         set { siteCountersProvider = value; }
468                 }
469
470                 bool trackApplicationName;
471
472                 [DefaultValueAttribute (true)]
473                 [WebCategoryAttribute ("Site Counters")]
474                 [WebSysDescriptionAttribute ("SiteCounters service tracks and stores the specified application name in a database.")]
475                 public bool TrackApplicationName {
476                         get { return trackApplicationName; }
477                         set { trackApplicationName = value; }
478                 }
479
480                 bool trackNavigateUrl;
481
482                 [DefaultValueAttribute (true)]
483                 [WebCategoryAttribute ("Site Counters")]
484                 [WebSysDescriptionAttribute ("SiteCounters service tracks and stores the destination URL of click through event in a database.")]
485                 public bool TrackNavigateUrl {
486                         get { return trackNavigateUrl; }
487                         set { trackNavigateUrl = value; }
488                 }
489
490                 bool trackPageUrl;
491
492                 [DefaultValueAttribute (true)]
493                 [WebCategoryAttribute ("Site Counters")]
494                 [WebSysDescriptionAttribute ("SiteCounters service tracks and stores the originating page URL in a database.")]
495                 public bool TrackPageUrl {
496                         get { return trackPageUrl; }
497                         set { trackPageUrl = value; }
498                 }
499 #endif
500
501                 class AdRecord
502                 {
503                         public IDictionary Properties;
504                         public int Hits; // or impressions or clicks
505                         public string Keyword;
506
507                         public AdRecord (IDictionary adProps)
508                         {
509                                 this.Properties = adProps;
510                                 Keyword = Properties ["Keyword"] as string;
511                                 if (Keyword == null)
512                                         Keyword = "";
513
514                                 string imp = Properties ["Impressions"] as string;
515                                 Hits = 1;
516                                 if (imp != null) {
517                                         try {
518                                                 Hits = Int32.Parse (imp);
519                                         } catch {
520                                         }
521                                 }
522                         }
523                 }
524         }
525 }
526