1+ // --------------------------------------------------------------------------------------------------------------------
2+ // <summary>
3+ // Themeable class for displaying WidgetBase derived controls.
4+ // </summary>
5+ // --------------------------------------------------------------------------------------------------------------------
6+
7+ namespace App_Code . Controls
8+ {
9+ using System ;
10+ using System . IO ;
11+ using System . Text ;
12+ using System . Threading ;
13+ using System . Web . UI ;
14+ using System . Web . Hosting ;
15+ using BlogEngine . Core ;
16+
17+ using Resources ;
18+
19+ /// <summary>
20+ /// Themeable class for displaying WidgetBase derived controls.
21+ /// </summary>
22+ /// <remarks>
23+ /// WidgetContainer is meant to be the themeable parent class of any control that derives from WidgetBase. This way a theme can automatically
24+ /// apply some basic styling to the way widgets are displayed without having to edit each one or edit the WidgetBase class to change the
25+ /// rendered output.
26+ /// Inherited WidgetContainers must contain a control named phWidgetBody. This is the control that the WidgetContainer's child Widget is
27+ /// injected inside of. phWidgetBody just needs to derive from Control to work, leaving flexibility for anyone creating a theme.
28+ /// If phWidgetBody isn't found, an exception isn't thrown, but a warning label is applied to the page.
29+ /// </remarks>
30+ public abstract class WidgetContainer : UserControl
31+ {
32+ #region "Properties"
33+
34+ /// <summary>
35+ /// Gets or sets the Widget this WidgetContainer holds.
36+ /// </summary>
37+ public WidgetBase Widget
38+ {
39+ get ;
40+ set ;
41+ }
42+
43+
44+ /// <summary>
45+ /// Gets a string representing the rendered html for administrative control of this WidgetContainer's child Widget.
46+ /// </summary>
47+ public string AdminLinks
48+ {
49+ get
50+ {
51+ if ( Security . IsAuthorizedTo ( Rights . ManageWidgets ) )
52+ {
53+ if ( this . Widget != null )
54+ {
55+ var sb = new StringBuilder ( ) ;
56+
57+ var widgetId = this . Widget . WidgetId ;
58+
59+ sb . AppendFormat ( "<a class=\" delete\" href=\" #\" onclick=\" BlogEngine.widgetAdmin.removeWidget('{0}');return false\" title=\" {1} widget\" ><span class=\" widgetImg imgDelete\" > </span></a>" , widgetId , labels . delete ) ;
60+ sb . AppendFormat ( "<a class=\" edit\" href=\" #\" onclick=\" BlogEngine.widgetAdmin.editWidget('{0}', '{1}');return false\" title=\" {2} widget\" ><span class=\" widgetImg imgEdit\" > </span></a>" , this . Widget . Name , widgetId , labels . edit ) ;
61+ sb . AppendFormat ( "<a class=\" move\" href=\" #\" onclick=\" BlogEngine.widgetAdmin.initiateMoveWidget('{0}');return false\" title=\" {1} widget\" ><span class=\" widgetImg imgMove\" > </span></a>" , widgetId , labels . move ) ;
62+
63+ return sb . ToString ( ) ;
64+ }
65+ }
66+
67+
68+ return String . Empty ;
69+ }
70+ }
71+
72+ #endregion
73+
74+ /// <summary>
75+ /// Raises the <see cref="E:System.Web.UI.Control.Load"/> event.
76+ /// </summary>
77+ /// <param name="e">The <see cref="T:System.EventArgs"/> object that contains the event data.</param>
78+ protected override void OnLoad ( EventArgs e )
79+ {
80+ base . OnLoad ( e ) ;
81+ ProcessLoad ( ) ;
82+ }
83+
84+ private bool _processedLoad ;
85+ /// <summary>
86+ /// Manually run the Initialization process.
87+ /// </summary>
88+ public void ProcessLoad ( )
89+ {
90+ if ( _processedLoad ) { return ; }
91+
92+ // phWidgetBody is the control that the Widget control
93+ // gets added to.
94+ var widgetBody = this . FindControl ( "phWidgetBody" ) ;
95+
96+ if ( widgetBody != null )
97+ {
98+ widgetBody . Controls . Add ( this . Widget ) ;
99+ }
100+ else
101+ {
102+ var warn = new LiteralControl
103+ {
104+ Text = "Unable to find control with id \" phWidgetBody\" in theme's WidgetContainer."
105+ } ;
106+ this . Controls . Add ( warn ) ;
107+ }
108+
109+ _processedLoad = true ;
110+ }
111+
112+ /// <summary>
113+ /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
114+ /// </summary>
115+ /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains the event data.</param>
116+ protected override void OnPreRender ( EventArgs e )
117+ {
118+ base . OnPreRender ( e ) ;
119+
120+ // Hide the container if the Widget is null or also not visible.
121+ this . Visible = ( this . Widget != null ) && this . Widget . Visible ;
122+ }
123+
124+ /// <summary>
125+ /// The container will be processed when invoked, rather than waiting
126+ /// for the Load event to occur.
127+ /// </summary>
128+ public virtual void RenderContainer ( ) { }
129+
130+ /// <summary>
131+ /// Returns the virtual path of where a theme's widget container would expect to be located.
132+ /// </summary>
133+ /// <param name="existenceCheck">
134+ /// When true, the path to the theme folder to check for the WidgetContainer existence
135+ /// is returned. When false, the path to the control that will be loaded is returned.
136+ /// If it's a Razor theme, the path will be RazorHost instead of the actual theme folder
137+ /// name.
138+ /// </param>
139+ /// <returns></returns>
140+ public static string GetThemeWidgetContainerVirtualPath ( bool existenceCheck )
141+ {
142+ if ( existenceCheck )
143+ {
144+ // if it's a Razor theme, check if WidgetContainer.cshtml exists.
145+ string filename = BlogSettings . Instance . IsRazorTheme ? "WidgetContainer.cshtml" : "WidgetContainer.ascx" ;
146+ return string . Format ( "~/Custom/Themes/{0}/{1}" , BlogSettings . Instance . Theme , filename ) ;
147+ }
148+ else
149+ {
150+ // when existenceCheck == false, the actual file that will be loaded needs to be
151+ // returned. if it's a Razor theme, we will load WidgetContainer.ascx in the
152+ // RazorHost folder. we assume that the RazorHost folder contains WidgetContainer.ascx.
153+ return string . Format ( "~/Custom/Themes/{0}/WidgetContainer.ascx" , BlogSettings . Instance . GetThemeWithAdjustments ( null ) ) ;
154+ }
155+ }
156+
157+ /// <summary>
158+ /// Returns the file path of where a theme's widget container would expect to be located.
159+ /// </summary>
160+ public static string GetThemeWidgetContainerFilePath ( bool existenceCheck )
161+ {
162+ return HostingEnvironment . MapPath ( GetThemeWidgetContainerVirtualPath ( existenceCheck ) ) ;
163+ }
164+
165+ /// <summary>
166+ /// Returns whether the theme contains a widget container file.
167+ /// </summary>
168+ public static bool DoesThemeWidgetContainerExist ( bool existenceCheck )
169+ {
170+ // This is for compatibility with older themes that do not have a WidgetContainer control.
171+ return File . Exists ( GetThemeWidgetContainerFilePath ( existenceCheck ) ) ;
172+ }
173+
174+ /// <summary>
175+ /// Loads the widget container (either the one located in the theme folder, or the default one if
176+ /// a theme widget container is missing), and adds the Widget to the widget container.
177+ /// </summary>
178+ public static WidgetContainer GetWidgetContainer (
179+ WidgetBase widgetControl , bool widgetContainerExists ,
180+ string widgetContainerVirtualPath )
181+ {
182+ // If a custom WidgetContainer can't be found, create a new DefaultWidgetContainer instance as it
183+ // provides backwards compatibility with existing themes that may have depended on WidgetBase's
184+ // old rendering method.
185+ var widgetContainer = widgetContainerExists ? ( WidgetContainer ) widgetControl . Page . LoadControl ( widgetContainerVirtualPath ) : new DefaultWidgetContainer ( ) ;
186+
187+ widgetContainer . ID = "widgetContainer" + widgetControl . ID ;
188+ widgetContainer . Widget = widgetControl ;
189+
190+ return widgetContainer ;
191+ }
192+
193+ /// <summary>
194+ /// Loads the widget container (either the one located in the theme folder, or the default one if
195+ /// a theme widget container is missing), and adds the Widget to the widget container.
196+ /// </summary>
197+ public static WidgetContainer GetWidgetContainer (
198+ WidgetBase widgetControl )
199+ {
200+ return GetWidgetContainer ( widgetControl , DoesThemeWidgetContainerExist ( true ) , GetThemeWidgetContainerVirtualPath ( false ) ) ;
201+ }
202+ }
203+
204+ /// <summary>
205+ /// Default implementation of WidgetContainer that provides backwards compatibility with themes that do not have
206+ /// their own WidgetContainer user control.
207+ /// </summary>
208+ internal sealed class DefaultWidgetContainer : WidgetContainer
209+ {
210+ /// <summary>
211+ /// The widgetBody instance needed by all WidgetContainers.
212+ /// </summary>
213+ private readonly System . Web . UI . WebControls . PlaceHolder widgetBody = new System . Web . UI . WebControls . PlaceHolder
214+ {
215+ ID = "phWidgetBody"
216+ } ;
217+
218+ /// <summary>
219+ /// Initializes a new instance of the <see cref="DefaultWidgetContainer"/> class.
220+ /// </summary>
221+ internal DefaultWidgetContainer ( )
222+ {
223+ this . Controls . Add ( this . widgetBody ) ;
224+ }
225+
226+ /// <summary>
227+ /// Sends server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object, which writes the content to be rendered on the client.
228+ /// </summary>
229+ /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the server control content.</param>
230+ protected override void Render ( HtmlTextWriter writer )
231+ {
232+ if ( this . Widget == null )
233+ {
234+ throw new NullReferenceException ( "WidgetContainer requires its Widget property be set to a valid WidgetBase derived control" ) ;
235+ }
236+
237+ var widgetName = this . Widget . Name ;
238+ var widgetId = this . Widget . WidgetId ;
239+
240+ if ( string . IsNullOrEmpty ( this . Widget . Name ) )
241+ {
242+ throw new NullReferenceException ( "Name must be set on a widget" ) ;
243+ }
244+
245+ var sb = new StringBuilder ( ) ;
246+
247+ sb . AppendFormat ( "<div class=\" widget {0}\" id=\" widget{1}\" >" , widgetName . Replace ( " " , string . Empty ) . ToLowerInvariant ( ) , widgetId ) ;
248+ sb . Append ( this . AdminLinks ) ;
249+ if ( this . Widget . ShowTitle )
250+ {
251+ sb . AppendFormat ( "<h4>{0}</h4>" , this . Widget . Title ) ;
252+ }
253+
254+ sb . Append ( "<div class=\" content\" >" ) ;
255+
256+ writer . Write ( sb . ToString ( ) ) ;
257+ base . Render ( writer ) ;
258+ writer . Write ( "</div>" ) ;
259+ writer . Write ( "</div>" ) ;
260+ }
261+ }
262+ }
0 commit comments