Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

How-To Tutorials - Web Development

1802 Articles
article-image-tracking-sql-queries-request-using-django
Packt
22 Apr 2010
13 min read
Save for later

Tracking SQL Queries for a Request using Django

Packt
22 Apr 2010
13 min read
For a typical Django application, database interactions are of key importance. Ensuring that the database queries being made are correct helps to ensure that the application results are correct. Further, ensuring that the database queries produced for the application are efficient helps to make sure that the application will be able to support the desired number of concurrent users. Django provides support in this area by making the database query history available for examination. This type of access is useful to see the SQL that is issued as a result of calling a particular model method. However, it is not helpful in learning about the bigger picture of what SQL queries are made during the processing of a particular request. This section will show how to include information about the SQL queries needed for production of a page in the page itself. We will alter our existing survey application templates to include query information, and examine the query history for some of the existing survey application views. Though we are not aware of any problems with the existing views, we may learn something in the process of verifying that they issue the queries we expect. Settings for accessing query history in templates Before the query history can be accessed from a template, we need to ensure some required settings are configured properly. Three settings are needed in order for the SQL query information to be available in a template. First, the debug context processor, django.core.context_processors.debug, must be included in the TEMPLATE_CONTEXT_PROCESSORS setting. This context processor is included in the default value for TEMPLATE_CONTEXT_PROCESSORS. We have not changed that setting; therefore we do not need to do anything to enable this context processor in our project. Second, the IP address of the machine sending the request must be listed in the INTERNAL_IPS setting. This is not a setting we have used before, and it is empty by default, so we will need to add it to the settings file. When testing using the same machine as where the development server runs, setting INTERNAL_IPS to include the loopback address is sufficient: # Addresses for internal machines that can see potentially sensitive # information such as the query history for a request. INTERNAL_IPS = ('127.0.0.1', ) If you also test from other machines, you will need to include their IP addresses in this setting as well. Third and finally, DEBUG must be True in order for the SQL query history to be available in templates. When those three settings conditions are met, the SQL query history may be available in templates via a template variable named sql_queries. This variable contains a list of dictionaries. Each dictionary contains two keys: sql and time. The value for sql is the SQL query itself, and the value for time is the number of seconds the query took to execute. Note that the sql_queries context variable is set by the debug context processor. Context processors are only called during template rendering when a RequestContext is used to render the template. Up until now, we have not used RequestContexts in our survey application views, since they were not necessary for the code so far. But in order to access the query history from the template, we will need to start using RequestContexts. Therefore, in addition to modifying the templates, we will need to change the view code slightly in order to include query history in the generated pages for the survey application. SQL queries for the home page Let's start by seeing what queries are issued in order to generate the survey application home page. Recall that the home page view code is: def home(request): today = datetime.date.today() active = Survey.objects.active() completed = Survey.objects.completed().filter(closes__gte=today- datetime.timedelta(14)) upcoming = Survey.objects.upcoming().filter( opens__lte=today+datetime.timedelta(7)) return render_to_response('survey/home.html', {'active_surveys': active, 'completed_surveys': completed, 'upcoming_surveys': upcoming, }) There are three QuerySets rendered in the template, so we would expect to see that this view generates three SQL queries. In order to check that, we must first change the view to use a RequestContext: from django.template import RequestContext def home(request): today = datetime.date.today() active = Survey.objects.active() completed = Survey.objects.completed().filter(closes__gte=today- datetime.timedelta(14)) upcoming = Survey.objects.upcoming().filter( opens__lte=today+datetime.timedelta(7)) return render_to_response('survey/home.html', {'active_surveys': active, 'completed_surveys': completed, 'upcoming_surveys': upcoming,}, RequestContext(request)) The only change here is to add the RequestContext(request) as a third parameter to render_to_response, after adding an import for it earlier in the file. When we make this change, we may as well also change the render_to_response lines for the other views to use RequestContexts as well. That way when we get to the point of examining the SQL queries for each, we will not get tripped up by having forgotten to make this small change. Second, we'll need to display the information from sql_queries somewhere in our survey/home.html template. But where? We don't necessarily want this information displayed in the browser along with the genuine application data, since that could get confusing. One way to include it in the response but not have it be automatically visible on the browser page is to put it in an HTML comment. Then the browser will not display it on the page, but it can be seen by viewing the HTML source for the displayed page. As a first attempt at implementing this, we might change the top of survey/home.html to look like this: {% extends "survey/base.html" %} {% block content %} <!-- {{ sql_queries|length }} queries {% for qdict in sql_queries %} {{ qdict.sql }} ({{ qdict.time }} seconds) {% endfor %} --> This template code prints out the contents of sql_queries within an HTML comment at the very beginning of the content block supplied by survey/home. html. First, the number of queries is noted by filtering the list through the length filter. Then the code iterates through each dictionary in the sql_queries list and displays sql, followed by a note in parentheses about the time taken for each query. How well does that work? If we try it out by retrieving the survey home page (after ensuring the development server is running), and use the browser's menu item for viewing the HTML source for the page, we might see that the comment block contains something like: <!-- 1 queries SELECT `django_session`.`session_key`, `django_session`.`session_data`, `django_session`.`expire_date` FROM `django_session` WHERE (`django_ session`.`session_key` = d538f13c423c2fe1e7f8d8147b0f6887 AND `django_ session`.`expire_date` &gt; 2009-10-24 17:24:49 ) (0.001 seconds) --> Note that the exact number of queries displayed here will depend on the version of Django you are running. This result is from Django 1.1.1; later versions of Django may not show any queries displayed here. Furthermore, the history of the browser's interaction with the site will affect the queries issued. This result is from a browser that had been used to access the admin application, and the last interaction with the admin application was to log out. You may see additional queries if the browser had been used to access the admin application but the user had not logged out. Finally, the database in use can also affect the specific queries issued and their exact formatting. This result is from a MySQL database. That's not exactly what we expected. First, a minor annoyance, but 1 queries is wrong, it should be 1 query. Perhaps that wouldn't annoy you, particularly just in internal or debug information, but it would annoy me. I would change the template code that displays the query count to use correct pluralization: {% with sql_queries|length as qcount %} {{ qcount }} quer{{ qcount|pluralize:"y,ies" }} {% endwith %} Here, since the template needs to use the length result multiple times, it is first cached in the qcount variable by using a {% with %} block. Then it is displayed, and it is used as the variable input to the pluralize filter that will put the correct letters on the end of quer depending on the qcount value. Now the comment block will show 0 queries, 1 query, 2 queries, and so on. With that minor annoyance out of the way, we can concentrate on the next, larger, issue, which is that the displayed query is not a query we were expecting. Furthermore, the three queries we were expecting, to retrieve the lists of completed, active, and upcoming surveys, are nowhere to be seen. What's going on? We'll take each of these in turn. The query that is shown is accessing the django_session table. This table is used by the django.contrib.sessions application. Even though the survey application does not use this application, it is listed in our INSTALLED_APPS, since it is included in the settings.py file that startproject generates. Also, the middleware that the sessions application uses is listed in MIDDLEWARE_CLASSES. The sessions application stores the session identifier in a cookie, named sessionid by default, that is sent to the browser as soon as any application uses a session. The browser will return the cookie in all requests to the same server. If the cookie is present in a request, the session middleware will use it to retrieve the session data. This is the query we see previously listed: the session middleware is retrieving the data for the session identified by the session cookie sent by the browser. But the survey application does not use sessions, so how did the browser get a session cookie in the first place? The answer is that the admin application uses sessions, and this browser had previously been used to access the admin application. At that time, the sessionid cookie was set in a response, and the browser faithfully returns it on all subsequent requests. Thus, it seems likely that this django_session table query is due to a sessionid cookie set as a side-effect of using the admin application. Can we confirm that? If we find and delete the cookie from the browser and reload the page, we should see that this SQL query is no longer listed. Without the cookie in the request, whatever code was triggering access to the session data won't have anything to look up. And since the survey application does not use sessions, none of its responses should include a new session cookie, which would cause subsequent requests to include a session lookup. Is this reasoning correct? If we try it, we will see that the comment block changes to: <!-- 0 queries --> Thus, we seem to have confirmed, to some extent, what happened to cause a django_session table query during processing of a survey application response. We did not track down what exact code accessed the session identified by the cookie—it could have been middleware or a context processor, but we probably don't need to know the details. It's enough to keep in mind that there are other applications running in our project besides the one we are working on, and they may cause database interactions independent of our own code. If we observe behavior which looks like it might cause a problem for our code, we can investigate further, but for this particular case we will just avoid using the admin application for now, as we would like to focus attention on the queries our own code is generating. Now that we understand the query that was listed, what about the expected ones that were not listed? The missing queries are due to a combination of the lazy evaluation property of QuerySets and the exact placement of the comment block that lists the contents of sql_queries. We put the comment block at the top of the content block in the home page, to make it easy to find the SQL query information when looking at the page source. The template is rendered after the three QuerySets are created by the view, so it might seem that the comment placed at the top should show the SQL queries for the three QuerySets. However, QuerySets are lazy; simply creating a QuerySet does not immediately cause interaction with the database. Rather, sending the SQL to the database is delayed until the QuerySet results are actually accessed. For the survey home page, that does not happen until the parts of the template that loop through each QuerySet are rendered. Those parts are all below where we placed the sql_queries information, so the corresponding SQL queries had not yet been issued. The fix for this is to move the placement of the comment block to the very bottom of the content block. When we do that we should also fix two other issues with the query display. First, notice that the query displayed above has &gt; shown instead of the > symbol that would actually have been in the query sent to the database. Furthermore, if the database in use is one (such as PostgreSQL) that uses straight quotes instead of back quotes for quoting, all of the back quotes in the query would be shown as &quot;. This is due to Django's automatic escaping of HTML markup characters. This is unnecessary and hard to read in our HTML comment, so we can suppress it by sending the sql query value through the safe filter. Second, the query is very long. In order to avoid needing to scroll to the right in order to see the entire query, we can also filter the sql value through wordwrap to introduce some line breaks and make the output more readable. To make these changes, remove the added comment block from the top of the content block in the survey/home.html template and instead change the bottom of this template to be: {% endif %} <!-- {% with sql_queries|length as qcount %} {{ qcount }} quer{{ qcount|pluralize:"y,ies" }} {% endwith %} {% for qdict in sql_queries %} {{ qdict.sql|safe|wordwrap:60 }} ({{ qdict.time }} seconds) {% endfor %} --> {% endblock content %} Now, if we again reload the survey home page and view the source for the returned page, we will see the queries listed in a comment at the bottom: <!-- 3 queries SELECT `survey_survey`.`id`, `survey_survey`.`title`, `survey_survey`.`opens`, `survey_survey`.`closes` FROM `survey_survey` WHERE (`survey_survey`.`opens` <= 2009-10-25 AND `survey_survey`.`closes` >= 2009-10-25 ) (0.000 seconds) SELECT `survey_survey`.`id`, `survey_survey`.`title`, `survey_survey`.`opens`, `survey_survey`.`closes` FROM `survey_survey` WHERE (`survey_survey`.`closes` < 2009-10-25 AND `survey_survey`.`closes` >= 2009-10-11 ) (0.000 seconds) SELECT `survey_survey`.`id`, `survey_survey`.`title`, `survey_survey`.`opens`, `survey_survey`.`closes` FROM `survey_survey` WHERE (`survey_survey`.`opens` > 2009-10-25 AND `survey_survey`.`opens` <= 2009-11-01 ) (0.000 seconds) --> That is good, those look like exactly what we expect to see for queries for the home page. Now that we seem to have some working template code to show queries, we will consider packaging up this snippet so that it can easily be reused elsewhere.
Read more
  • 0
  • 0
  • 11020

article-image-django-debug-toolbar
Packt
20 Apr 2010
9 min read
Save for later

The Django Debug Toolbar

Packt
20 Apr 2010
9 min read
The debug toolbar has a far more advanced way of displaying the information than simply embedding it in HTML comments. The capabilities are best shown by example, so we will immediately proceed with installing the toolbar. Installing the Django Debug Toolbar The toolbar can be found on the Python package index site: https://pypi.python.org/pypi/django-debug-toolbar. Once installed, activating the debug toolbar in a Django project is accomplished with the addition of just a couple of settings. First, the debug toolbar middleware, debug_toolbar.middleware.DebugToolbarMiddleware, must be added to the MIDDLEWARE_CLASSES setting. The documentation for the toolbar notes that it should be placed after any other middleware that encodes the response content, so it is best to place it last in the middleware sequence. Second, the debug_toolbar application needs to be added to INSTALLED_APPS. The debug_toolbar application uses Django templates to render its information, thus it needs to be listed in INSTALLED_APPS so that its templates will be found by the application template loader. Third, the debug toolbar requires that the requesting IP address be listed in INTERNAL_IPS. Finally, the debug toolbar is displayed only when DEBUG is True. We've been running with debug turned on, so again we don't have to make any changes here. Note also that the debug toolbar allows you to customize under what conditions the debug toolbar is displayed. It's possible, then, to set things up so that the toolbar will be displayed for requesting IP addresses not in INTERNAL_IPS or when debug is not turned on, but for our purposes the default configuration is fine so we will not change anything. One thing that is not required is for the application itself to use a RequestContext in order for things such as the SQL query information to be available in the toolbar. The debug toolbar runs as middleware, and thus is not dependent on the application using a RequestContext in order for it to generate its information. Thus, the changes made to the survey views to specify RequestContexts on render_to_response calls would not have been needed if we started off first with the Django Debug Toolbar. Debug toolbar appearance Once the debug toolbar is added to the middleware and installed applications settings, we can see what it looks like by simply visiting any page in the survey application. Let's start with the home page. The returned page should now look something like this: Note this screenshot shows the appearance of the 0.8.0 version of the debug toolbar. Earlier versions looked considerably different, so if your results do not look like this you may be using a different version than 0.8.0. The version that you have will most likely be newer than what was available when this was written, and there may be additional toolbar panels or functions that are not covered here. As you can see, the debug toolbar appears on the right-hand side of the browser window. It consists of a series of panels that can be individually enabled or disabled by changing the toolbar configuration. The ones shown here are the ones that are enabled by default. Before taking a closer look at some of the individual panels, notice that the toolbar contains an option to hide it at the top. If Hide is selected, the toolbar reduces itself to a small tab-like indication to show that it is present: This can be very useful for cases where the expanded version of the toolbar obscures application content on the page. All of the information provided by the toolbar is still accessible, after clicking again on the DjDT tab; it is just out of the way for the moment. Most of the panels will provide detailed information when they are clicked. A few also provide summary information in the main toolbar display. As of debug toolbar version 0.8.0, the first panel listed, Django Version, only provides summary information. There is no more detailed information available by clicking on it. As you can see in the screenshot, Django 1.1.1 is the version in use here. Note that the current latest source version of the debug toolbar already provides more information for this panel than the 0.8.0 release. Since 0.8.0, this panel has been renamed to Versions, and can be clicked to provide more details. These additional details include version information for the toolbar itself and for any other installed Django applications that provide version information. The other three panels that show summary information are the Time, SQL, and Logging panels. Thus, we can see at a glance from the first appearance of the page that 60 milliseconds of CPU time were used to produce this page (111 milliseconds total elapsed time), that the page required four queries, which took 1.95 milliseconds, and that zero messages were logged during the request. In the following sections, we will dig into exactly what information is provided by each of the panels when clicked. We'll start first with the SQL panel, since it is one of the most interesting and provides the same information (in addition to a lot more). The SQL panel If we click on the SQL section of the debug toolbar, the page will change to: At a glance, this is a much nicer display of the SQL queries for the page than what we came up with earlier. The queries themselves are highlighted so that SQL keywords stand out, making them easier to read. Also, since they are not embedded inside an HTML comment, their content does not need to be altered in any way—there was no need to change the content of the query containing the double dash in order to avoid it causing display problems. (Now would probably be a good time to remove that added query, before we forget why we added it.) Notice also that the times listed for each query are more specific than what was available in Django's default query history. The debug toolbar replaces Django's query recording with its own, and provides timings in units of milliseconds instead of seconds. The display also includes a graphical representation of how long each query took, in the form of horizontal bars that appear above each query. This representation makes it easy to see when there are one or more queries that are much more expensive than the others. In fact, if a query takes an excessive amount of time, its bar will be colored red. In this case, there is not a great deal of difference in the query times, and none took particularly long, so all the bars are of similar length, and are colored gray. Digging deeper, some of the information we had to manually figure out earlier in this article is just a click away on this SQL query display. Specifically, the answer to the question of what line of our code triggered a particular SQL query to be issued. Each of the displayed queries has a Toggle Stacktrace option, which when clicked will show the stack trace associated with the query: Here we can see that all queries are made by the home method in the survey views. py file. Note that the toolbar filters out levels in the stack trace that are within Django itself, which explains why each of these has only one level shown. The first query is triggered by Line 61, which contains the filter call added to test what will happen if a query containing two dashes in a row was logged. The remaining queries are all attributed to Line 66, which is the last line of the render_to_response call in the home view. These queries, as we figured out earlier, are all made during the rendering of the template. (Your line numbers may vary from those shown here, depending on where in the file various functions were placed.) Finally, this SQL query display makes available information that we had not even gotten around to wanting yet. Under the Action column are links to SELECT, EXPLAIN, and PROFILE each query. Clicking on the SELECT link shows what the database returns when the query is actually executed. For example: Similarly, clicking on EXPLAIN and PROFILE displays what the database reports when asked to explain or profile the selected query, respectively. The exact display, and how to interpret the results, will differ from database to database. (In fact, the PROFILE option is not available with all databases—it happens to be supported by the database in use here, MySQL.) Interpreting the results from EXPLAIN and PROFILE is beyond the scope of what's covered here, but it is useful to know that if you ever need to dig deep into the performance characteristics of a query, the debug toolbar makes it easy to do so. We've now gotten a couple of pages deep into the SQL query display. How do we get back to the actual application page? Clicking on the circled >> at the upper-right of the main page display will return to the previous SQL query page, and the circled >> will turn into a circled X. Clicking the circled X on any panel detail page closes the details and returns to displaying the application data. Alternatively, clicking again on the panel area on the toolbar for the currently displayed panel will have the same effect as clicking on the circled symbol in the display area. Finally, if you prefer using the keyboard to the mouse, pressing Esc has the same effect as clicking the circled symbol. Now that we have completely explored the SQL panel, let's take a brief look at each of the other panels provided by the debug toolbar. The Time panel Clicking on the Time panel brings up more detailed information on where time was spent during production of the page: The total CPU time is split between user and system time, the total elapsed (wall clock) time is listed, and the number of voluntary and involuntary context switches are displayed. For a page that is taking too long to generate, these additional details about where the time is being spent can help point towards a cause. Note that the detailed information provided by this panel comes from the Python resource module. This is a Unix-specific Python module that is not available on non-Unix-type systems. Thus on Windows, for example, the debug toolbar time panel will only show summary information, and no further details will be available.
Read more
  • 0
  • 0
  • 5833

article-image-handling-invalid-survey-submissions-django
Packt
20 Apr 2010
5 min read
Save for later

Handling Invalid Survey Submissions with Django

Packt
20 Apr 2010
5 min read
What would make a survey submission invalid? The only likely error case for our QuestionVoteForm is if no answer is chosen. What happens, then, if we attempt to submit a survey with missing answers? If we try it, we see that the result is not ideal: There are at least two problems here. First, the placement of the error messages, above the survey questions, is confusing. It is hard to know what the first error message on the page is referring to, and the second error looks like it is associated with the first question. It would be better to move the error messages closer to where the selection is actually made, such as between the question and answer choice list. Second, the text of the error message is not very good for this particular form. Technically the list of answer choices is a single form field, but to a general user the word field in reference to a list of choices sounds odd. We will correct both of these errors next. Coding custom error message and placement Changing the error message is easy, since Django provides a hook for this. To override the value of the error message issued when a required field is not supplied, we can specify the message we would like as the value for the required key in an error_messages dictionary we pass as an argument in the field declaration. Thus, this new definition for the answer field in QuestionVoteForm will change the error message to Please select an answer below: class QuestionVoteForm(forms.Form): answer = forms.ModelChoiceField(widget=forms.RadioSelect, queryset=None, empty_label=None, error_messages={'required': 'Please select an answer below:'}) Changing the placement of the error message requires changing the template. Instead of using the as_p convenience method, we will try displaying the label for the answer field, errors for the answer field, and then the answer field itself, which displays the choices. The {% for %} block that displays the survey forms in the survey/active_survey.html template then becomes: {% for qform in qforms %} {{ qform.answer.label }} {{ qform.answer.errors }} {{ qform.answer }}{% endfor %} How does that work? Better than before. If we try submitting invalid forms now, we see: While the error message itself is improved, and the placement is better, the exact form of the display is not ideal. By default, the errors are shown as an HTML unordered list. We could use CSS styling to remove the bullet that is appearing (as we will eventually do for the list of choices), but Django also provides an easy way to implement custom error display, so we could try that instead. To override the error message display, we can specify an alternate error_class attribute for QuestionVoteForm, and in that class, implement a __unicode__ method that returns the error messages with our desired formatting. An initial implementation of this change to QuestionVoteForm and the new class might be: class QuestionVoteForm(forms.Form): answer = forms.ModelChoiceField(widget=forms.RadioSelect, queryset=None, empty_label=None, error_messages={'required': 'Please select an answer below:'}) def __init__(self, question, *args, **kwargs): super(QuestionVoteForm, self).__init__(*args, **kwargs) self.fields['answer'].queryset = question.answer_set.all() self.fields['answer'].label = question.question self.error_class = PlainErrorListfrom django.forms.util import ErrorListclass PlainErrorList(ErrorList): def __unicode__(self): return u'%s' % ' '.join([e for e in sefl]) The only change to QuestionVoteForm is the addition of setting its error_class attribute to PlainErrorList in its __init__ method. The PlainErrorList class is based on the django.form.util.ErrorList class and simply overrides the __unicode__ method to return the errors as a string with no special HTML formatting. The implementation here makes use of the fact that the base ErrorList class inherits from list, so iterating over the instance itself returns the individual errors in turn. These are then joined together with spaces in between, and the whole string is returned. Note that we're only expecting there to ever be one error here, but just in case we are wrong in that assumption, it is safest to code for multiple errors existing. Although our assumption may never be wrong in this case, it's possible we might decide to re-use this custom error class in other situations where the single possible error expectation doesn't hold. If we code to our assumption and simply return the first error in the list, this may result in confusing error displays in some situations where there are multiple errors, since we will have prevented reporting all but the first error. If and when we get to that point, we may also find that formatting a list of errors with just spaces intervening is not a good presentation, but we can deal with that later. First, we'd like to simply verify that our customization of the error list display is used.
Read more
  • 0
  • 0
  • 3241

article-image-customizing-kubrik-wordpress
Packt
19 Apr 2010
5 min read
Save for later

Customizing Kubrik with Wordpress

Packt
19 Apr 2010
5 min read
Getting ready The first step is to make a list of the changes that you want to make. Here's what we've come up with: Increase width to 980px Increase font sizes in sidebar Use a graphic header We've picked 980px as our target width because this size is optimized for a 1024 screen resolution and works well in a grid layout. Several CSS adjustments will be necessary to realize this modification, as well as using an image editing program (we will be using Photoshop). How to do it... To increase the page width, the first step is to determine which entries in the CSS stylesheet are controlling the width. Using Firebug to inspect the page (as seen below), we find that the selector #page has a value of 760px for the width property. And #header has a width of 758px (less because there is a 1px left margin). The .narrowcolumn selector gives the main content column a width of 450px. And #sidebar has a width of 190px. Finally, #footer has a width of 760px. So, we will increase #page and #footer to 980px. #header we will increase to 978px. Let's apply all of the additional 220px width to .narrowcolumn. Taking note of the existing 45px left margin, our new value for the width property will be 700px. That means #sidebar width will remain at 190px, but the margin-left will need to be increased from 545px to 765px. Click on Appearance | Editor. In the right-hand column, below the Templates heading, click on style.css. Scroll past the section that says /* Begin Typography & Colors */, until you get to the section that says /* Begin Structure */. Make the following changes to the stylesheet (style.css), commenting as appropriate to document your changes. #page { background-color: white; margin: 20px auto; padding: 0; width: 980px; /* increased from 760px */ border: 1px solid #959596; }#header { background-color: #73a0c5; margin: 0 0 0 1px; padding: 0; height: 200px; width: 978px; /* increased from 758px */ }.narrowcolumn { float: left; padding: 0 0 20px 45px; margin: 0px 0 0; width: 700px; /* increased from 450px */ }#sidebar {margin-left:765px; /* increaseed from 545px */padding:20px 0 10px;width:190px;}#footer { padding: 0; margin: 0 auto; width: 980px; /* increased from 760px */ clear: both; } Adjustments via Photoshop We'll also need to use an image editing program to modify the three background images that create the rounded corners: kubrikbg-ltr.jpg, kubrickheader.jpg, and kubrickfooter.jpg. In this example, we modify kubrik-ltr.jpg (the background image for #page), a 760px image. Open up the image in Photoshop, select all, copy, create a new document (with a white or transparent background), and paste (Ctrl-A, Ctrl-C, Ctrl-N, Ctrl-V). Increase the canvas size (Image | Canvas Size) to 980px, keeping the image centered on the left-hand side by clicking on the left-pointing arrow. Select one half of the image with the Rectangular Marquee Tool, cut and paste. Use the Move Tool to drag the new layer to the right-hand side of the canvas. In this case, it does not matter if you can see the transparent background or if your selection was exactly one half the image. Since the middle of the image is simply a white background, we are really only concerned with the borders on the left and right. The following screenshot shows the background image cut in half and moved over: Save for Web and Devices, exporting as a jpg. Then, replace the existing kubrikbgltr.jpg with your modified version via FTP. The steps are similar for both kubrickheader.jpg and kubrickfooter.jpg. Increase the canvas size and copy/paste from the existing image to increase the image size without stretching or distortion. The only difference is that you need to copy and paste different parts of the image in order to preserve the background gradient and/or top and bottom borders. In order to complete our theme customization, the width of .widecolumn will need to be increased from 450px to 700px (and the 150px margin should be converted to a 45px margin, the same as .narrowcolumn). Also, the kubrikwide.jpg background image will need to be modified with an image editing program to increase the size from 760px to 980px. Then, the individual post view will look as good as the homepage. By following the same steps as above, you should now be prepared to make this final customization yourself. Our next goal is to increase the sizes of the sidebar fonts. Firebug helps us to pinpoint the relevant CSS. #sidebar h2 has a font-size of 1.2em (around line 123 of style.css). Let's change this to 1.75em. #sidebar has font-size of 1em. Let's increase this to 1.25em. To use a graphic in the header, open up kubrickheader.jpg in a new Photoshop document. Use the magic wand tool to select and delete the blue gradient with rounded corners. Now, use the rounded rectangle tool to insert your own custom header area. You can apply another gradient, if desired. We choose to apply a bevel and emboss texture to our grey rectangle. Then, to paste in some photos, decreasing their opacity to 50%. In a short time, we've been able to modify Kubrik by re-writing CSS and using an image-editing program. This is the most basic technique for theme modification. Here is the result:
Read more
  • 0
  • 0
  • 4924

article-image-manipulation-dom-objects-using-firebug
Packt
16 Apr 2010
3 min read
Save for later

Manipulation of DOM Objects using Firebug

Packt
16 Apr 2010
3 min read
Inspecting DOM The DOM inspector allows for full, in-place editing of our document structure, not just text nodes. In the DOM inspector, Firebug auto completes property value when we press the Tab key. The following are the steps to inspect an element under the DOM tab: Press Ctrl+Shift+C—the shortcut key to open Firebug in inspect mode. Let's move the mouse pointer over the HTML element that we want to inspect and click on that element. The HTML script of that element will be shown in Firebug's HTML tab. Right-clicking on the selected DOM element will open a context menu. Let's select the Inspect in DOM Tab option from the context menu. As soon as we do that, Firebug will take us to its DOM tab. Filtering properties, functions, and constants Many times we want to analyze whether a function written by us is associated with an HTML element. Firebug provides us an easy way to figure out whether an event, listener, function, property, or constants are associated with a particular element. The DOM tab is not only a tab but also a drop-down menu. When we click on the down arrow icon on the DOM tab, Firebug will show a drop-down list from which one can select the filtering options and inspect the element thoroughly. The following are the options provided by this menu: Show User-defined Properties Show User-defined Functions Show DOM Properties Show DOM Functions Show DOM Constants Refresh There are two kinds of objects and functions: Part of the standard DOM Part of our own JavaScript code Firebug can notify the difference, and shows us our own script-created objects and functions in bold at the top of the list. The text that is bold and green is a user-defined function. The text that is bold and black is a user-defined property. The text whose size is normal and is green in color is a DOM-defined function. The text whose size is normal and is black in color is a DOM-defined property. The upper case letters (capital letters) are the DOM constants. We can see the actual colored depiction in Firebug's DOM tab. In the following code, the onkeyup() event is a user-defined function for <input/> and calculatefactorial() is a user-defined function for the current window. To test this code, let's type the code in an HTML file, open it with Firefox, and enable Firebug by pressing the F12 key. Inspect the input element in the DOM. <html><head><script>function calculateFactorial(num,event){if(event.keyCode!=13){return;}var fact=1;for(i=1;i<=num;i++){fact*=i;}alert ("The Factorial of "+ num + " is: " +fact)}</script><title>code_6_1.html.html</title></head><body><font face="monospace">Enter a number to calculate its factorial<input type = "text" name="searchBox" onkeyup="calculateFactorial(this.value,event)"/></font></body></html> Intuitive DOM element summariesThere are many different kinds of DOM and JavaScript objects, and Firebug does its best to visually distinguish each, while providing as much information as possible. When appropriate, objects include brief summaries of their contents so that we can see what's there without having to click. Objects are color coded so that HTML elements, numbers, strings, functions, arrays, objects, and nulls are all easy to distinguish.
Read more
  • 0
  • 0
  • 3508

article-image-video-blogging-wordpress
Packt
16 Apr 2010
7 min read
Save for later

Video Blogging in Wordpress

Packt
16 Apr 2010
7 min read
Introduction Video is a major component of the Web today. Luckily, WordPress makes it easy to publish and share video. In this article, we will demonstrate ways of working with video in WordPress and in Flash. You will learn how to embed an .flv file, create an .xml video sitemap, and distribute your videos to sites such as YouTube and Blip. We will also show you how to set up Free WP Tube, a free video blogging theme that allows you to run a video web log (vlog). FLV Embed (Version 1.2.1) If you want to embed .flv files, use a Flash video player, and/or publish a video sitemap, this compact plugin does all three. The homepage is http://www.channel-ai.com/blog/plugins/flv-embed/. FLV Embed uses standards compliant, valid XHTML, and JavaScript. It is based on the JW FLV Media Player, whose homepage is http://www.longtailvideo.com/players/jw-flvplayer/ FLV Embed supports Google video sitemap generation, allowing you to describe, syndicate, and distribute your video content, facilitating indexing in Google video search. If a user is missing Flash or has disabled JavaScript, he or she is provided unobtrusive and appropriate on-screen instructions to correct the problem. Getting ready When a page with video loads, the player displays either the first frame of the video or a thumbnail (referred to as a poster image). The poster image is preferable, especially when a user is choosing between many videos—the first frame of a video may not offer the most representative or compelling description. Your poster image can be a poster or any image you like. Here is an example of our finished product: You will want to think about where you will upload the video files and poster images and,how you will name them. A good place might be wp-content/uploads/video. This plugin requires that you name your poster images the same as your video files. The default image type is jpg, but you can use any valid image file format. All your images must be in the same file format. A batch resize and rename utility is a useful tool. For PC, one free option is the Fast Stone Image Resizer, which you can download at http://www.faststone.org/FSResizerDetail.htm. How to do it... In your dashboard, navigate to Plugins | Add New. Search for "FLV Embed". Click on Install, then on Activate. Visit the plugin configuration panel at Settings | FLV Embed. In the Sitemap menu, check the first box to Enable sitemap feature and automatic custom field addition. FLV Embed will now be able to create your video sitemap by automatically adding a custom field each time you use FLV Embed to insert a video. In the Poster menu, check the box to Display poster image for embedded FLV movies. For both of the fields, Path to poster directory and Path to FLV directory, we suggest you leave these blank, and instead use absolute URLs. If you do use relative (site-specific) URLs, keep in mind that a trailing slash is required. An example is /wp-content/uploads/videos/. In the Player menu, you may want to change the colors or add your site logo as a linkable watermark to the video. Review all the Settings, and click on Save Changes. To embed an FLV file, use the following shortcode in HTML view: [flv:url width height]. For example, you could insert a YouTube video at 480 by 360 (using the absolute URL) like this: [flv:http://youtube.com/watch?v=fLV3MB3DpWN 480 360] A YouTube video cannot use a poster image because the file name of a jpg cannot contain a question mark. You can also insert an FLV that you have uploaded (using the relative path) like this: [flv:http://www.wordpressandflash.com/wp-content/uploads/video/swfobject_test.swf 480 360] Once you have inserted the video, FLV Embed automatically populates the FLV custom field with two URLs, as you can see below. The first is the location of the video, and the second is the location of the poster image: To use a custom poster image, upload any image to wp-content/uploads/video, and rename it to match the filename. You can also use an absolute URL if the poster image file is in another location—the filename must still match. To configure your video XML sitemap, visit the Video Sitemap Options menu by clicking on Settings | Video Sitemap. Here, you can get or modify the feed address. Our example is http://www.wordpressandflash.com/ videofeed.xml You can also adjust additional optional settings, and if you have made any changes to the settings or content and need to rebuild the sitemap or update your custom fields, you can do that here too. How it works... The video sitemap is an extension of the XML sitemap. A video sitemap allows you to publish and syndicate online video content, including descriptive metadata to tag your content for Google Video search. Adding details, such as a title and description, makes it easier for users who are searching to find a given piece of content. Your poster image will also be included as a clickable thumbnail image. The user will be directed to your website to see the video. If FLV Embed cannot automatically generate the XML file, you can simply copy the XML file from the demo and save it to your server. Make sure to set the file permissions to write (664 or 666) by context-clicking in your FTP client and modifying the File Attributes, as seen below: Then, make the appropriate changes to the Video sitemap filename field in the Video Sitemap Options menu, directing the plugin to the XML file you have prepared, and rebuild the sitemap. Here is what your finished feed will look like: There's more... The videofeed.xml file has a simple structure. The first three tags specify encoding, styling, and the video sitemap protocol: <?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl"href="http://www.wordpressandflash.com/wp-content/plugins/flv-embed/sitemap.xsl"?><urlset > Next, a <url> tag wraps each piece of content, which includes a <loc> tag (a link to the content on your site) and a <videogt; tag. The <videogt; tag contains additional tags that specify the video location, the video player location, the poster image location, a title, and description: <url> <loc>http://www.wordpressandflash.com/flv-embed/</loc> <video:video> <video:content_loc>http://www.wordpressandflash.com/wp-content/uploads/video/swfobject_test.swf</video:content_loc> <video:player_loc allow_embed="No">http://www.wordpressandflash.com/wp-content/plugins/flv-embed/flvplayer.swf?file=/wp-content/uploads/video/swfobject_test.swf</video:player_loc> <video:thumbnail_loc>http://www.wordpressandflash.com/wp-content/uploads/videos/swfobject_test.jpg</video:thumbna il_loc> <video:title><![CDATA[FLV Embed]]></video:title> <video:description><![CDATA[FLV Embed is a great plugin thatallows you to display FLV files &#8212; and most other video formats&#8212; in a compact Flash video player. Visit the homepage at:http://www.channel-ai.com/blog/plugins/flv-embed/. Check out thevideo sitemap!Get the latest Flash Player to see this player.[Javascript required to view Flash movie, please turn it [...]]]></video:description> </video:video></url></urlset> With this info, you can manually create a .xml video feed for any site, without a plugin. Commercial use? Commercial use does require a license. A free alternative for commercial use is the Hana FLV Player, whose homepage is http://www.neox.net/w/2008/05/25/hana-flv-playerwordpress-plugin/.
Read more
  • 0
  • 0
  • 2734
Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime
article-image-creating-tumblr-theme
Packt
15 Apr 2010
13 min read
Save for later

Creating a Tumblr Theme

Packt
15 Apr 2010
13 min read
At this moment, surely, you must have heard something about Tumblr, but in case you haven't, we can summarize by telling it's a platform for microblogging. We can publish texts, audio, photos, quotes, links etc in a very easy way. As other users can follow our posts, comment on them or ask questions to us, Tumblr can be considered a great social tool. Apart from being very easy to use, we can also customize it's appearance, so it shares our business look and feel, or simply because we prefer some other appearance. Not only can we customize it's appearance, but also design our own customized themes; that's exactly what we are going to do in this article: What we have and what we want First steps, a basic html template Generating the Tumblr theme from our template Adding a bit of jQuery Keep with us and by the end of the article, you will learn how to create very interesting Tumblr themes, and use also them. What we have and what we want For this article, we are going to use my own account as an example. For now, I'm using a theme that looks this way: This is a great theme, very clean, centered on showing our content. The theme we are going to create is going to be very clean too, and also enough for us to see how we can create a template. Let's take a look at it: As you can see, it's a quite easy design; hope you like it too. Also, you can download the PSD if you wish, maybe it will help you follow the article. Well, once we know which design we want to use, our next step will be to generate a plain html template. First step, a basic html template A basic html template is a good place to start our theme, and that's where we are going to start. First the basic structure: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html ><head> <title>Our first theme</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> </head> <body id="theme"> <div id="container"> <div id="menu"> <a href="#" class="menu_link">About me</a> | <a href="#" class="menu_link">Archives</a> | <a href="#" class="menu_link">Random</a> | <a href="#" class="menu_link">Mobile</a> | <a href="#" class="menu_link">RSS</a> </div> <div id="right_col"> <div class="post"> <div class="post_type"> <div class="blue_box">Text</div> <a href="#" class="content_link">Permalink</a> </div> <div class="date"> <div class="blue_box">Mar 22</div> Posted at 7:54am </div> <div class="post_contents"> <h1>Codeigniter 1.7</h1> <p> <img src="sample_image.jpg" /> <br/> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean fermentum  vestibulum nisl vitae placerat. Praesent nec massa neque. Nam dictum commodo posuere. Sed vitae est risus. Nunc suscipit justo vitae mi faucibus cursus. Morbi eu arcu erat. Nam venenatis, urna non tempus pretium, felis nunc blandit risus, non elementum elit turpis in diam. Proin ullamcorper blandit lacinia. Nulla nibh metus, lacinia at luctus vel, tincidunt et mauris. Sed rutrum, felis in tincidunt porttitor, nulla nunc placerat elit, ac tempor magna arcu non nisi. Proin ac sagittis lorem. Morbi imperdiet consectetur ipsum, volutpat convallis arcu pulvinar in. Nulla eget quam elit, vitae molestie ligula. Praesent rhoncus diam id lorem sagittis consequat ac et magna. </p> <hr> </div> </div> </div> <div id="left_col"> <div id="description"> <h1>Jose Argudo's Blog!</h1> <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean fermentum vestibulum nisl vitae placerat. Praesent nec massa neque. Nam dictum commodo posuere. Sed vitae est risus. Nunc suscipit justo vitae mi faucibus cursus. Morbi eu arcu erat. Nam venenatis, urna non tempus pretium, felis nunc blandit risus, non elementum elit turpis in diam. </p> </div> <div class="banner"> <img src="codeigniter_book.gif" /> &nbsp;&nbsp; Check my Codeigniter book </div> </div> <div id="footer"> <div id="left_footer"> Jose Argudo´s Blog </div> <div id="right_footer"> <img src="arrow.gif" /> </div> </div> </div> </body></html> We have our basic html structure here, quite simple by now, all html without any styling as of now. Our next step will be to add some css to our basic page, which, by now, looks more or less this way: Some reset css will be useful here, I usually use Eric Meyer's one, which you can find here: http://meyerweb.com/eric/tools/css/reset/ We are going to place this code in the head section of our page, just like this: ... <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <style type="text/css" media="screen"> /* CSS reset from Eric Meyer */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td { margin: 0; padding: 0; border: 0; outline: 0; font-size: 100%; vertical-align: baseline; background: transparent; } body { line-height: 1; } ol, ul { list-style: none; } blockquote, q { quotes: none; } blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; } :focus { outline: 0; } ins { text-decoration: none; } del { text-decoration: line-through; } table { border-collapse: collapse; border-spacing: 0; } </style> We have included this css styles in our html file, instead of including it from an external source. Why have we done that? Mostly because we have no ftp access to our Tumblr account, also themes are not packed in a zip file and installed somehow. Soon we will be answering all these questions; for now, placing our css styles directly in our html is the best solution. Now that we have added our reset styles, we can start working in the theme own styles, let's see: /* Theme's own styles */ #theme{ font-family: arial, sans-serif; font-size: 12px; color: #333333; background-color: #F5F5F5; } #container{ width: 960px; margin: 20px auto; } #menu{ height: 22px; } #left_col{ float: left; width: 320px; } #right_col{ float: right; width: 630px; } #footer{ float: none; clear: both; height: 20px; } #description, .banner{ background-color: #0087C5; padding: 12px; color: #ffffff; margin-bottom: 5px; } #description h1{ font-size: 24px; margin-bottom: 15px; } #description p{ line-height: 18px; } .banner{ font-style: italic; font-size: 10px; } .post_type{ width: 54px; float: left; margin-left: 10px; } .date{ width: 54px; float: left; color: #0087C5; font-style: italic; font-size: 10px; } .post_contents{ margin-left: 10px; margin-bottom: 22px; width: 500px; float: right; } .blue_box{ height: 54px; background-color: #0087C5; color: #ffffff; font-size: 14px; padding: 6px; padding-bottom: 0px; margin-bottom: 10px; } .post_contents h1{ font-size: 18px; color: #0087C5; margin-bottom: 22px; } .post_contents img{ margin-bottom: 24px; } .post_contents p{ line-height: 18px; } hr{ height: 1px; color: #0087C5; border: 1px solid #0087C5; } a.content_link:link, a.content_link:visited{ color: #0087C5; font-style: italic; font-size: 10px; text-decoration: none; } a.content_link:hover{ text-decoration: underline; } #menu{ color: #0087C5; } a.menu_link:link, a.menu_link:visited, a:link, a:visited{ color: #0087C5; font-size: 12px; text-decoration: none; } a.menu_link:hover, a:hover{ text-decoration: underline; } #left_footer{ width: 332px; height: 20px; float: left; background-color: #0087C5; color: #ffffff; } #right_footer{ width: 43px; float: right; } .padd{ padding: 4px; } .clear{ clear: both; float: none; } With these styles our template will be looking very similar to our design, maybe later we will need some small changes, but that's not so important. With all this in place, we can start with the interesting work, the Tumblr part. Generating the Tumblr theme from our template Converting this template into a working Tumblr theme is not going to be that hard, mostly we will be using some special tags to determine where some specific contents will be placed. Tumblr uses two type of tags Variable tags Block tags But we will see that better by working with our theme, for example, our page title: <title>Our first theme</title> We will change that for this code: <title>{Title}</title> In bold, we can see a {Title} tag. This tag, when rendered will be substituted by the title of our blog, without any html tag. Easy, isn't it? Most of our work will be quite similar, we are going to see the main tags to use. One of our main work will be showing the posts, after all, our main objective is to show these posts. Tumblr divides posts into eight possible types: Text posts, with only text and html. Photo posts, with one photo and its description Photosets, for when you need to show more than one photo Quote, used when we want to quote other site or source Link to other sites or online resources Chat, interesting option, for showing conversation, where each line, separated be a carriage return, is show in a different color. Audio, from an uploaded or external source Video, similar to audio but with videos. In our html code, we have prepared a zone for showing posts: <div class="post"> <div class="date"> <div class="blue_box">Mar 22</div> Posted at 7:54am </div> <div class="post_type"> <div class="blue_box">Text</div> <a href="#" class="content_link">Permalink</a> </div> <div class="post_contents"> . . . <hr> </div> </div> <br class="clear" /><br/> Now we need to generate one of these post zone for each post type. It's important to note that this is absolutely necessary for the theme to be correct, all post type must be present in our code. Firstly, we need to define these tags: {block:Posts} … {/block:Posts} All the other types of posts will go into these tags, let's summarize it a bit: {block:Posts} {block:Text} Text posts will go inside these tags {/block:Text} {block:Photo} Photo posts will go inside these tags {/block:Photo} {block:Photoset} Photoset posts will go inside these tags {/block:Photoset} {block:Quote} Quote posts will go inside these tags {/block:Quote} {block:Link} Link posts will go inside these tags {/block:Link} {block:Chat} Chat posts will go inside these tags {/block:Chat} {block:Audio} Audio posts will go inside these tags {/block:Audio} {block:Video} Video posts will go inside these tags {/block:Video} {/block:Posts} Now, we will be working on each one of these blocks. Remember that we will be placing them between {block:Posts} … {/block:Posts}, so we will have more or less this code: <div id="right_col"> {block:Posts} All the other block types will go here {/block:Posts}</div> Text Posts We will start with this type of posts, I will put the code and after we take a look at the different options {block:Text} <div class="post"> <div class="date"> {block:Date} {block:NewDayDate} <div class="blue_box">{Month} {DayOfMonth}</div> Posted at {12Hour}{AmPm} {/block:NewDayDate} {block:SameDayDate} <div class="blue_box">{Month} {DayOfMonth}</div> Posted at {12Hour}{AmPm} {/block:SameDayDate} {/block:Date} </div> <div class="post_type"> <div class="blue_box">Text</div> <a href="{Permalink}" class="content_link">Permalink</a> </div> <div class="post_contents"> {block:Title} <h1>{Title}</h1> {/block:Title} <p> {Body} </p> <hr> </div> </div> <br class="clear" /><br/> {/block:Text} Here we have some interesting tags, let's talk a bit about them: {block:Date} — this will render the date, with the options we are defining inside. {block:NewDayDate} — this is used for showing the date, but it's only show for the first post of the day. {block:SameDayDate} — If we want to show the date in each post, we need to use this tag. {Month} — with this tag we will show the day of the post {DayOfMonth} — and with this other we will show the day {12Hour} — the hour in a 12 hour format {AmPm} — and the am or pm text, where applicable {Permalink} — we used this inside our link href in order to generate a permalink {block:Title} — the code inside these tags will only be shown if the article has a title. {Title} — this is the title of the post. {Body} — and the content of the same. Most of the options are in fact very self explanatory, and quite easy to use. The next blog types are similar, but with some differences. Photo Posts The code for photo posts is quite similar, let's take a look to it: {block:Photo} <div class="post"> //same as before <div class="post_contents"> <img src="{PhotoURL-500}" alt="{PhotoAlt}"/> <p> {block:Caption}{Caption}{/block:Caption} </p> <hr> </div> </div> <br class="clear" /><br/>{/block:Photo} As you can see, we have changed the title tag for caption tag, but the structure remains mostly the same. The other main difference is the img tag we have used: <img src="{PhotoURL-500}" alt="{PhotoAlt}"/> The {PhotoAlt} tag will show the alt attribute of the photo, if there's any. {PhotoURL-500} is the more important one, as it will create the url of the image, but for a photo equal or smaller to 500px width. There are other options for this tag, which are as follows: {PhotoURL-400} — just the same tag, but will create a url for a image equal or smaller to 400px. {PhotoURL-250} — a smaller image, this time of 250px. {PhotoURL-100} — just the same as before, but will make urls for images of 100px width or less. {PhotoURL-75sq} — this one is a bit different, as will make a url for a reduced, squared image of the post, of 75px. {PhotoURL-HighRes} — this last tag will create a url for the high resolution image.
Read more
  • 0
  • 0
  • 3973

article-image-getting-started-development-environment-using-microsoft-content-management-server
Packt
15 Apr 2010
8 min read
Save for later

Getting Started with the Development Environment Using Microsoft Content Management Server

Packt
15 Apr 2010
8 min read
Visual Web Developer Websites The key difference between developing MCMS applications with Visual Studio .NET 2003 and Visual Studio 2005 is that ASP.NET applications (and therefore MCMS applications) are now built using the Visual Web Developer component of Visual Studio 2005. Visual Web Developer introduces a new "project system", which no longer uses the project (*.csproj) files and simply accesses web applications via HTTP or the file system. In Visual Studio .NET 2003, MCMS applications were created by choosing the MCMS Web Application project type. This project type was effectively a regular ASP.NET web application project with some modifications required by MCMS, such as additional references, the web authoring console, and modifications to the web.config. In Visual Studio 2005, developing web applications has been separated from developing other project types. The feature to develop a web application has been moved into the Visual Web Developer component. To reflect this design change, you are no longer using New Project but New Web Site from the File menu in Visual Studio 2005 to create a new website. Visual Studio 2005 ships with several website templates. The installation of the developer tools for MCMS extends the list of website templates with three additional templates: MCMS Empty Web Project, MCMS Web Application, and MCMS Web Service. These templates are actually modified versions of the similarly named standard templates shipped with Visual Studio 2005. Creating an MCMS Web Application Let's create an MCMS web application using Visual Studio 2005. Open Visual Studio 2005. From the File menu, choose New, followed by Web Site… In the New Web Site dialog, select the MCMS Web Application within the My Templates section. If the MCMS Web Application template does not appear in the My Templates section, the MCMS Visual Studio 2005 templates have not been correctly installed. Please refer to the Visual Studio Templates section of Article 1 for installation details. In the Location combo box, select HTTP, and in the textbox, enter http://localhost/mcmstest. MCMS applications have to be created using a local installation of IIS and do not support being created using the file system, which makes use of the built-in Visual Web Developer Web Server. Note that the New Web Site wizard will not prevent you from configuring an invalid website using the File System and Visual Web Developer Web Server. In the Language combo box (shown in the following figure), select Visual C#, and click on OK. If you wish, you can also choose VB.NET. The samples in this article series are all written in Visual C#. Visual Studio 2005 will create your project and initialize the MCMS Template Explorer. When it's done, you will be presented with an MCMS website with the basic foundation files. The MCMS Template Explorer within Visual Studio 2005 logs on to the MCMS repository using the credentials of the currently logged-on user. If this operation fails, check your MCMS Rights Groups configuration. The Template Explorer does not allow you to specify alternative credentials. Click on the MCMS Template Explorer tab at the bottom of the Solution Explorer, and note that the Template Gallery is accessible. If you don't see the Template Explorer, it is likely you didn't select HTTP in the Location combo box in step 4. You may also not see the Template Explorer if you are using a locale other than US English, in which case you need to install hotfix 914195 as detailed in Article 1. Click on the Solution Explorer tab at the bottom of the MCMS Template Explorer, and click on the Refresh button. Notice that unlike the web applications from ASP.NET 1.x days, the 'CMS' virtual directory is now part of the website. If you examine the contents of the website, its references, and web.config file, you will see that the necessary MCMS files and configuration changes have been added. Checking the Website Configuration Settings in IIS We can verify that Visual Studio 2005 has configured the MCMS application correctly by using the Internet Information Services snap-in. First, let's ensure that the mcmstest website is indeed running on ASP.NET 2.0. From the Start Menu click on Run, enter inetmgr in the Run textbox, and click on OK. In Internet Information Services, expand the tree view to display the mcmstest application. Right-click the mcmstest application and click on Properties. Click the ASP.NET tab and note that the ASP.NET version is correctly configured as 2.0.50727. When developing on Windows Server 2003, the Virtual Website root must run in the same worker process (that is Application Pool) as all MCMS applications so that the MCMS ISAPI Filter can work as expected. This filter cannot route requests across worker-process boundaries. In effect this means that all MCMS applications will share the same ASP.NET version, as ASP.NET does not support side-by-side execution of different versions inside the same worker process. This is not necessary with IIS on Windows XP as it does not use Worker Process Isolation mode. Next, we will check the authentication settings. For now, we will configure the website to use integrated Windows authentication. Only users with a domain or local user account will have access to the site. Later in Article 6 we will show alternative authentication methods such as Forms Authentication. Click on the Directory Security tab followed by the Edit... button, and note that the permissions are correctly inherited from the Virtual Web Site settings. In this example, we will use integrated Windows authentication. Note that we configured the Virtual Web Site to use Windows authentication in Article 1. Authentication methods can be configured on a per-application basis. Click on Cancel and close Internet Information Services. Developing MCMS Web Applications We are now ready to get started on developing our ASP.NET 2.0-based MCMS applications. There are a number of quirks with the MCMS web application templates, which we need to bear in mind during development. Switch back to Visual Studio 2005. In Solution Explorer, right-click on the website (http://localhost/mcmstest), and click on New Folder. Enter Templates as the folder name. Right-click on the Templates folder and click on Add New Item… In the Add New Item dialog, select the MCMS Template File item and enter Basic.aspx in the Name textbox. Click on Add. The new Basic.aspx template file is created and opened in Source View. Examine the contents of Basic.aspx. Correcting Basic.aspx Notice that the Basic.aspx file has a few problems. Some elements are highlighted by IntelliSense "squiggles", and if we attempt to build the website, a number of errors will prevent a successful build. Let's correct the Basic.aspx template file. In the CodeFile attribute of the Page directive on line one, replace CodeFile="~/Basic.aspx.cs" with CodeFile="basic.aspx.cs" The MCMS Web Application New Item template doesn't recognize that our new template file has been created in a subdirectory, and therefore the CodeFile attribute is incorrect. New templates in the web root are not affected. From the Build menu, choose Build Web Site. Notice that the website now builds, but still includes a number of errors. Correct the DOCTYPE. On line 19, replace <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"> with <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> Correct the <html> element. On line 20, replace <html> with <html >. Delete the comments on lines 4 through 17. The comments within an inline ASP script block (<% %>) are unnecessary. Delete the <meta> tags on lines 10 through 13. <meta name="GENERATOR" content="Microsoft Visual Studio .NET 8.0"><meta name="CODE_LANGUAGE" content="C#"><meta name="vs_defaultClientScript" content="JavaScript"><meta name="vs_targetSchema"content="http://schemas.microsoft.com/intellisense/ie5"> These <meta> tags are unnecessary. Correct the WebControls Register directive. On line 2, replace: <%@ Register TagPrefix="cms" Namespace="MicrosoftContentManagement.WebControls" Assembly="Microsoft.ContentManagement.WebControls"%> with <%@ Register Assembly="Microsoft.ContentManagement.WebControls,Version=5.0.1200.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" Namespace="Microsoft.ContentManagement.WebControls"TagPrefix="cms"%> The original Register directive is not correctly recognized by Visual Studio 2005, and prevents IntelliSense from including the cms tag prefix. From the Build menu, choose Build Web Site. Notice that the website now builds free of any errors and that the cms tag prefix is understood. Your template file should now be as follows: <%@ Page language="c#" AutoEventWireup="false" CodeFile="Basic.aspx.cs" Inherits="Basic.Basic"%><%@ Register Assembly="Microsoft.ContentManagement.WebControls,Version=5.0.1200.0, Culture=neutral, PublicKeyToken= 31bf3856ad364e35"Namespace="Microsoft.ContentManagement.WebControls"TagPrefix="cms"%><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html > <head> <title>Basic</title> <cms:RobotMetaTag runat="server"></cms:RobotMetaTag> </head> <body> <form id="Form1" method="post" runat="server"> </form> </body></html>
Read more
  • 0
  • 0
  • 2354

article-image-users-roles-and-pages-dotnetnuke-5
Packt
14 Apr 2010
7 min read
Save for later

Users, Roles, and Pages in DotNetNuke 5

Packt
14 Apr 2010
7 min read
One of the most important aspects of running a DotNetNuke portal is trying to figure out how to administer the portal. From adding modules to working with users, it may take a while before you have mastered all the administration tasks associated with running a portal. DotNetNuke uses a few terms that may be unfamiliar at first. For example, the term "portal" can be used interchangeably with the more common term "site". Similarly, the terms "tab" and "page" may be used interchangeably within the context of DotNetNuke. Knowing that they are interchangeable will help to understand the topics that we will discuss. User accounts If you are used to working within a network environment, or have worked with different portals in the past, then you are probably comfortable with the term "users". For those of us who may not be very familiar with this term, can think of a website that you have registered with in the past. When the registration had been completed, a user account was provided to you. This account lets you return to the site and log in as a recognized user. Everything that takes place on your portal revolves around users and user accounts. Whether users are required to register in order to use your services (such as a member site) or only a few user accounts are required to manage the content, functionality, and layout of your site, you will need to understand how to create and manage user accounts. Let's start with a general description of a user, and then you will see how to create and manage your users. In order to work through the examples, you will need to bring up your portal and sign in as an administrator account, such as the one created during the initial portal installation. Who is a user? The simplest definition of a user is an individual who consumes the services that your portal provides. However, a user can take on many different roles, from a visitor who is just browsing (unregistered user) or a person who registers to gain access to your services (registered user), to a specialized member of the site such as a content editor, to the facilitator (administrator or host) who is responsible for the content, functionality, and design of your portal. The difference between an administrator account and a host (or super user) account will be explained later in detail. For now, we will focus on the administrator account that is associated with a single portal. Everything in DotNetNuke revolves around the user, so before we can do anything else, we need to learn a little about user accounts. Creating user accounts Creating user accounts Before you create user accounts, you should decide and configure how users will be able to register on the site. You can choose from the following four different types of registrations: None Private Public (default) Verified To set the registration type for your portal, go to the Site Settings link found in the ADMIN menu, as shown in the following screenshot: The User Registration section can be found under Advanced Settings | Security Settings, as shown in the next screenshot: Many sections within DotNetNuke may be collapsed or hidden by default. To view these sections, you will need to expand them by clicking the '+' in front of them. The type of registration you use depends on how you will be using your portal. The following table gives a brief explanation of the different User Registration types: Registration setting Description None Setting the user registration as None will remove the Register link from your portal. In this mode, users can only be added by the administrator or host users. Thanks to the new features in DotNetNuke 5, users who have been given the proper permissions can also manage user accounts. This allows the administrator or host users to delegate account management to other individuals with the proper access. If you plan to have all sections of your site available to everyone (including unregistered users), then selecting None as your registration option is a good choice. Private If you select Private, then the Register link will reappear. After users fi ll out the registration form, they will be informed that their request for registration will be reviewed by the administrator. The administrator will decide whom to give access to the site. Unless an administrator approves (authorizes in DNN terms) a user, they will not be able to log in. Public Public is the default registration for a DotNetNuke portal. When this is selected, the users will be able to register for yoursite by entering the required information. Once the registration form is fi lled out, they will be given access to the site and can log in without requiring any action on the part of an administrator. Verified If you select Verified as your registration option, then the users will be sent an e-mail with a verifi cation code, once they fi ll out the required information. This ensures that the e-mail address they enter in the registration process is valid. The fi rst time they sign in, they will be prompted for the verifi cation code. Alternatively, they can click on the Verification link in the e-mail sent to them. After they have been verifi ed, they will need to type in only their login name and password to gain access to the site. Please note that proper SMTP confi guration is required to ensure delivery of the verifi cation e-mails. Setting required registration fields The administrator has the ability to decide what information the user will be required to enter when registering. If you are logged in as an administrator, then you can accomplish this through a combination of User Settings and Profile Properties. A user who is given the appropriate permissions can also modify these settings; however, this requires a few additional configuration steps that we will not cover in this section. To manage the Profile Properties for your site, select the ADMIN | User Accounts link. The User Accounts section appears as shown in the following screenshot: In the preceding screen, select Manage Profile Properties, either by selecting the link at the bottom of the module container or by selecting the link in the Action menu—the menu that pops up when you point on the drop-down button adjacent to the module title, User Accounts (more information on Action menu is provided later). When you select this link, you will be redirected to a screen (see the next screenshot) that displays a list of the currently configured Profile Properties. You can manage some attributes of the Profile Properties from within this screen. For instance, you can delete a property by clicking on the 'X' icon in the second column. Alternatively, you can change the display order of the properties by clicking on one of the Dn or Up icons in the third and fourth columns. If you change the order this way, make sure you click the Apply Changes button at the bottom of the page to save any changes. Your changes will be lost if you leave this screen without clicking the Apply Changes button. If you want even more control, then you can edit a single property by clicking on the pencil icon in the first column. You will see this icon extensively in the DNN user interface. It allows you to Edit/Modify the item it is associated with. You can also add a new property by selecting the Add New Profile Property action from the Action menu. In either case, you will be redirected to another page, where you can enter information about the property. You will be redirected to the Add New Property Details page, as shown in the following screenshot: Note that if you are editing an existing property, the Property Name field cannot be changed, so make sure you get it right the first time. If you need to change the Property Name, then you will need to delete and recreate the property. Most of these fields are self explanatory, but we will describe a few of these fields.
Read more
  • 0
  • 0
  • 3702

article-image-users-roles-and-pages-dotnetnuke-5-extension
Packt
14 Apr 2010
8 min read
Save for later

Users, Roles, and Pages in DotNetNuke 5- An Extension

Packt
14 Apr 2010
8 min read
Understanding DotNetNuke roles and role groups We just discussed how to add a user to your site. But are all users created equal? To understand how users are allowed to interact with the portal, we will need to take a look at what a role is and how it factors into the portal. There are plenty of real-world examples of roles we can look at. A police station, for example, can have sergeants, patrol cops, and detectives, and with each position come different responsibilities and privileges. In a police station, there are multiple people filling those positions (roles) with each of those belonging to the same position (role) sharing the same set of responsibilities and privileges. Roles in our portal work the same way. Roles are set up to divide the responsibilities needed to run your portal, as well as to provide different content to different groups of users of your portal. We want our portal to be easy for the administrators to manage. To do this, we will need to settle on the different user roles needed for our site. To determine this, we first need to decide on the different types of users who will access the portal. We will detail these user types in the following list: Administrator: The administrators will have very high security. They will be able to modify, delete, or move anything on the site. They will be able to add and delete users and control all security settings. (This role comes built-in with DotNetNuke.) The default administrator account is created during the creation of the portal. Home Page Admin: The home page admins will have the ability to modify only the information on the home page. They will be responsible for changing what users see when they first access your site. (We will be adding this role.) Forum Admin: The forum moderators will have the ability to monitor and modify posts in your forum. They will have the ability to approve or disapprove messages posted. (We will be adding this role.) Registered User: The registered users will be able to post messages in the forum and be able to access sections of the site set aside for registered users only. (This role comes built into DotNetNuke; however, we would need to provide the proper permissions to this role to perform the mentioned tasks.) Unauthenticated User: The unauthenticated user is the most basic of all the user types. Any person browsing your site will fall under this category, until they have successfully logged in to the site. This user type will be able to browse certain sections of your portal, but will be restricted from posting in the forum and will not be allowed in the Registered Users Only section. (This role comes built into DotNetNuke; however, we would need to provide the proper permissions to this role to perform the mentioned tasks.) Once you formulate the different user roles that will access the site, you will need to re strict users' access. For example, we only want the Home Page Admin to be able to edit items on the home page. To accomplish this, DotNetNuke uses role-based security. Role-based security allows you to give access to portions of your website based on what role the user belongs to. User-based security is also available per page or content section of the portal. However, the benefit of using a role-based security method is that you only have to define the access privileges for a role once. Then you just need to add users to that role and they will possess the privileges that the role defines. The following diagram gives you an idea of how this works: Looking at the diagram, we notice two things: Users can be assigned to more than one role More than one user can be assigned to a single role This gives us great flexibility when deciding on the authorization that users will possess in our portal. To create the roles we have detailed, sign in with an administrator account, and select ADMIN | Security Roles on the main menu or click the Roles link in the Common Tasks section of the control panel. This is available on the top of every DNN page for authorized users. You might need to click the double down arrows on the top-right of the page to maximize the panel. The Security Roles page appears as shown in the following screenshot: Notice that DotNetNuke comes with three roles already built into the system: the Administrators role (which we have been using), the Registered Users role, and the Subscribers role. Before we jump right into creating the role, let us first discuss the role group feature of DotNetNuke, as we will be using this feature when we create our roles. A role group is a collection of roles used, mainly, in large sites with a large number of roles, as a way of managing the roles more effectively. For the site we are building, the benefit of using role groups will be minimal. However, in order to demonstrate how to use them, we will create one for our administrative roles. While on the Security Roles page, click the Add New Role Group link located below the list of roles. This will present us with the Edit Role Group page containing two form fields. Once these fields are filled out, click the Update link at the bottom of this page. A Filer By Role Group drop-down list should now be displayed above the list of roles on the Security Roles page. If you select the Administrative Roles group that we just created, two noticeable things happen on this page. Firstly, the list of roles become empty as no roles are currently assigned to this role group. Secondly, an edit (pencil) icon and a delete (red cross X) icon now appear next to the drop-down list. These icons can be used to update or remove any custom role groups that exist on the site. These red 'X' icons are standard icons used throughout DotNetNuke to represent the ability to delete/remove items. Now that we have created our role group, we want to create the role for Home Page Admin. To do this, you again have two choices. Either select Add New Role from the dropdown in the upper left, or click on the Add New Role link. This will bring up the Edit Security Roles page, as shown in the next screenshot. We will use this page to create the Home Page Admin role that we need. The Basic Settings shown in the screenshot are as follows: Role Name: Make the name of your role short, but descriptive. The name should attempt to convey its purpose. Description: Here you may detail the responsibilities of the role. Role Group: Set this field to the Administrative Roles group that we created earlier. Public Role: Checking this will give registered users of your site the ability to sign up for this role themselves. We will be creating a Newsletter role and will demonstrate how this works when it is created. Auto Assignment: If this is checked, users will automatically be assigned to this role as soon as they register for your portal. CAUTION Do not check the Public Role and Auto Assignment checkboxes, unless you are sure. Checking these options would allow all users of the site to become members of the associated role, which would grant them the privileges assigned to the role. As we want to decide who will be able to modify our home page, we will leave both of these unchecked. To save the settings, click on the Update link. The Advanced Settings section allows you to set up a fee for certain security roles. We will not be configuring any of these settings for this particular exercise. However, we will briefly discuss how these settings can be used in Assigning security roles to users section. Now to complete the roles that we will require for Coffee Connections, we will add two more security roles The first role will be called Newsletter. We will be using this role to allow users to sign up for the newsletter we will be hosting at the Coffee Connections site. Set up the security role with the following information: Role Name: Newsletter Description: Allows users to register for the Coffee Connections Newsletter Role Group: Leave this as the default < Global Roles > as it is not going to be used to grant administrative rights to its members Public Role: Yes (checked) Auto Assignment: No (unchecked) Click on the Update link to save this role. The second role will be called Forum Admin. We will be using this role to administer the forums at the Coffee Connections site. Set up the security role with the following information: Role Name: Forum Admin Description: Allows user to administer Coffee Connections Forum Role Group: Administrative Roles Public Role: No (unchecked) Auto Assignment: No (unchecked)
Read more
  • 0
  • 1
  • 4591
article-image-programming-php-nuke
Packt
13 Apr 2010
27 min read
Save for later

Programming PHP-Nuke

Packt
13 Apr 2010
27 min read
After a quick look at the file and folder structure of a module, we then begin creating a new module for PHP-Nuke. This module will allow items of content to be submitted for modules that do not support user-submitted content. In this article, we will code functionality for users to submit encyclopedia entries for administrator approval. However, this will not involve making any modifications to the Encyclopedia module, and can be extended to cover other modules as well. What Happens When a Page is Requested? Let's see what happens when a visitor wants to view an article on the site, and the process that PHP-Nuke goes through to construct the page containing this information. Viewing an article means that the visitor will be navigating to a page similar to this: http://localhost/nuke/modules.php?name=News&file=article&sid=1 The page requested by the visitor is modules.php, so let's have a look at that. Note that although there are many PHP files in the PHP-Nuke installation, there are only four files actually requested in the normal course of interaction with the site: index.php modules.php admin.php backend.php The other files in the PHP-Nuke installation are used by these files when required. Where Does PHP-Nuke Get Information From? PHP-Nuke is able to collect information about what module the visitor wants to see, what operation they want to perform, and details of the user from request variables coming from these places: The query string in the URL: The URL of the page holds information that tells PHP-Nuke which module to select, which part of the module to use, what item of content to show, and so on. The query string information is used to select the page from the system that needs to be shown to the visitor. Posted variables: When a user enters information in a form, and submits this back to the server, this information will be available to PHP-Nuke. This posted information is how PHP-Nuke gets input from the user to create items of content, enter terms to search for, and so on. Cookie variables: There is user account information stored in a cookie (and administrator account information if the user has such an account). This is used to identify the user, so they do not have to keep logging on every time they view a page or come to the site. When the user logs out, this information is deleted from the cookie. The information that PHP-Nuke gets from these sources has to be treated very carefully within the system. These sources are the only means through which visitors communicate with the site, and are also the channels through which hacks or attacks might be conducted on the site. The patches we applied in Article 2 while installing the system address precisely this issue, and they make sure that the data PHP-Nuke collects from a visitor is in exactly the right form for working with. Requesting a Page Once the modules.php page is requested, the first step followed is to include the mainfile.php file. This file does the following things: It checks and processes the request variables (namely the input to the application), to avoid possibly harmful tags, or other indicators of some form of SQL injection attack. It creates global variables for each request variable. It sets up a connection to the database. It gets the site configuration such as the site name, site logo, and so on, from the database. The mainfile.php file also contains a number of core functions such as checking if the user is logged in or is an administrator, choosing the blocks to display, and filtering text, among others. These will be used at different points in the creation of the page. After this file has been included, the next thing to happen in modules.php is that PHP-Nuke gets the requested module from the $name global variable, which corresponds to the name query string variable (as in modules.php?name=News), and checks if this module is active. If the module isn't active, and the visitor isn't an administrator, a 'Module Not Active' message is displayed, and the page output is done. If the module is active, then PHP-Nuke checks if the visitor has rights to access this module. PHP-Nuke checks to see if the access is restricted to a particular user group, and if so, is the user a member of that group? PHP-Nuke also checks if the module is for subscribers only, and if so, is the user a subscriber to the site? If the visitor doesn't have the right permissions to view the module, then a 'No Access' message is displayed, and the page output is done. If the module selected by the visitor is active, and they do have permission to view it, then PHP-Nuke can get on with passing control to that module. Control is passed to the selected module by attempting to include the index.php file in the folder of the selected module. However, if there is a file variable in the query string, then the file with that name is included instead. If these files can't be found, a 'Module Cannot Be Found' error is displayed to the visitor. Thus if the user requests a page like modules.php?name=News&file=article&sid=1, the article.php file in the News folder will be included by PHP-Nuke. If the user requests a page like modules.php?name=News&sid=1, then the index.php file in the News folder will be included. Attempting to request a page like modules.php?name=News&file=nosuchfile returns a page with a 'No such page' message, since there is no file called nosuchfile.php in the News folder. The 'No such page' message is generated by PHP-Nuke, since it's in control of the process. If the user has selected an active module for which they have view permission, and are requesting a page that is part of the module, then control passes to the module, and it's up to that module to do its work and create the page. We'll see how this works later in the article, but for now, our overview of how PHP-Nuke gets the page creation underway is complete. Seeing how PHP-Nuke works isn't particularly exciting, what is more exciting is seeing how we can extend the power of PHP-Nuke by creating new blocks and modules. Along the way, we'll see most of the components required for 'programming' with PHP-Nuke, and you'll get a good idea of how to go about your own development projects. Creating a Block Our development efforts begin with creating a File block. A File block is a PHP script that is stored in the blocks folder. It must have a filename of the form block-NAME.php, where NAME will be used by PHP-Nuke as the title for the block. The filename should not contain any spaces. The goal of a block is simple. It just has to create one variable, $content, that holds the content of the block. After that, the PHP-Nuke core will bring the theme into play to take care of displaying the block. The block we will create is a better version of the Dinosaur of the Day static HTML block we created in Article 4. The block will display the name of the Dinosaur of the Day, and a thumbnail image of the lucky lizard. However, on the next day, a different dinosaur will be chosen, and the block display will be updated. This is how the block works: We will create a database table to hold the date, the title of the dinosaur for that day, and a link to the thumbnail image of that dinosaur. We will create a text data file that will contain the name of a dinosaur and a link to its thumbnail image on each line. The data in this file will be the dinosaur pool from which the dinosaur of the day is chosen at random. When the block code is executed, it will look in the database table to see if there is any information for the current date. If there is, it will retrieve it and build the block output. If there is no information for the current date, the data from the text file will be loaded in. One of the entries in that file will be selected at random, and that data will be inserted into the database. This will become the Dinosaur of the Day. That data will then be used to create the block output. We will use the text file to hold the 'Dinosaur of the Day' candidates rather than a database table so that we do not have to create a separate administration feature to add these details. To add more dinosaurs to the list, we simply upload a new version of the text file. Make sure that you copy the dinosaur thumbnails from the code download into the imagesdinosaurstnails folder of your PHP-Nuke installation root. Time For Action—Creating the Database Table Open up phpMyAdmin in your web browser, and select the nuke database from the drop-down list in the left-hand panel. Click on the SQL tab, and enter the following code into the textbox, then click on Go. CREATE TABLE dinop_dinoportal_dotd (id INT( 10 ) NOT NULL AUTO_INCREMENT ,day VARCHAR( 16 ) NOT NULL ,title VARCHAR( 128 ) NOT NULL ,image VARCHAR( 250 ) NOT NULL ,PRIMARY KEY ( 'id' )) TYPE = MYISAM ; What Just Happened? We just created our database table. There is only one table needed, with a simple design. There are four fields in the table. The id field will hold an auto-incrementing unique numerical value and the other fields will hold the current date, the title of the dinosaur, and the link to the image of the dinosaur. Time For Action—Creating the Text File Open up your text editor, and enter the following: Tyrannosaurus Rex,images/dinosaurs/tnails/tyro.gifStegosaurus,images/dinosaurs/tnails/stego.gifTriceratops,images/dinosaurs/tnails/triceratops.gif Save this file as dotd_list.txt in the blocks folder. What Just Happened? The dotd_list.txt file will be the data source for choosing a new Dinosaur of the Day image. You will notice that we are storing the data here in the form 'name of the dinosaur', 'path to the image', so it will be easy to extract the information when we need it. Time For Action—Creating the Block Code Open up your text editor, and enter the following code into a blank file: <?phpif ( !defined('BLOCK_FILE') ){ Header("Location: ../index.php"); die();}global $prefix, $db;$today = date('d-m-Y');$sql = "SELECT * from ".$prefix."_dinoportal_dotd WHERE day='$today'";$result = $db->sql_query($sql);$content = "";$dino_title = "";$image = ""; $numrows = $db->sql_numrows($result); if ($numrows) { $row = $db->sql_fetchrow($result); $dino_title = $row['title']; $image = $row['image']; } else { $filename = "blocks/dotd_list.txt"; $possibles =@ file($filename); if ($possibles) { $choice = rand(1, count($possibles)); $imp = explode("," , $possibles[$choice-1]); $dino_title = $imp[0]; $image = $imp[1]; $sql = "INSERT INTO ".$prefix."_dinoportal_dotd(day,title,image) VALUES ('$today', '$dino_title', '$image')"; $result = $db->sql_query($sql); } $choice = rand(1, count($possibles)); $imp = explode("," , $possibles[$choice-1]); $dino_title = $imp[0]; $image = $imp[1]; }if ($dino_title){ $content = "Today's dinosaur is:<br><center><b>$dino_title</b><center><br>"; $content .= "<center><img src="$image" alt="$dino_title"></center><br>";}?> Save this file as block-DinosaurOfTheDay.php in the blocks folder of your PHP-Nuke installation. What Just Happened? We just entered the code for the Dinosaur of the Day block, and we'll step through it now. This first part of the code stops this file being requested directly by a visitor— the BLOCK_FILE constant is defined in mainfile.php, and without that constant being defined, the visitor would be redirected back to the homepage of the site. Block files are never requested directly by the visitor, they are included by PHP-Nuke. These first few lines of code are found in every block file: if ( !defined('BLOCK_FILE') ){ Header("Location: ../index.php"); die();} Now we can get started. First, we set ourselves to access some of the global variables of the application, and we will have our first look at the objects to access data from the database. The only global variables we need here are the ones for data access—$prefix, which holds the value of the database tables prefix, and $db, which is used to actually perform database operations. global $prefix, $db; Next, we grab today's date, formatted as digits like this 24-05-2005. $today = date('d-m-Y'); Now we set up the SQL statement to retrieve the information corresponding to this date from the database: $sql = "SELECT * from ".$prefix."_dinoportal_dotd WHERE day='$today'"; Now we execute the SQL statement: $result = $db->sql_query($sql); It is possible that there is no data corresponding to today's date, so we check the number of rows returned from this last query. If there are zero rows, there will be no information. $numrows = $db->sql_numrows($result); If there are some rows returned, we can start creating the block output. We use the sql_fetchrow() method to retrieve a row of data from the result of the query. This row is returned as an array, and we set some variables from the array. We'll only grab one row. If for some reason, there is more than one entry for today's date, we simply ignore the others. if ($numrows){ $row = $db->sql_fetchrow($result); $dino_title = $row['title']; $image = $row['image'];} Now we move on to the situation where there is no information for today's date, and we have to create it. The first thing we do is to read the contents of the dotd_list.txt file into an array—there will be one entry in the array for each line in the text file. However, we have to consider what will happen if there is some kind of problem reading the file. else{ $filename = "blocks/dotd_list.txt"; Note that the path to the file for the dodt_list.txt file is blocksdotd_list.txt. This may seem strange, since both this file and the block file are in the same blocks folder. However, PHP will be looking for this file from the executing script, which will be one of index.php, modules.php, or admin.php, all of which are outside the blocks folder. Thus we need to add the blocks folder in the path to the dotd_list.txt file. Now we try to grab the file itself: $possibles =@ file($filename); The file function opens the named file, and reads the input into an array called $possibles. The use of the @ character here will suppress any error messages—if there is a problem opening or reading the file, no untidy error messages will be displayed to the user, and execution can continue. Of course, if there is a problem reading the file then there will be a problem with $possibles. So we check this next—if there has been some problem reading the file then $possibles will be false: if ($possibles){ If there is something stored in $possibles, then this check will be passed, and we can proceed to choose one element from it at random. We choose a random number, between 1 and the number of lines in the text file. $choice = rand(1, count($possibles)); All we have to do now is choose that element from the array (we have to subtract one from the random number because the first element of the array is 0, rather than 1), and then split up that line to get at the title and the path to the image. $imp = explode("," , $possibles[$choice-1]);$dino_title = $imp[0];$image = $imp[1]; We split the line using the explode() function. The explode() function converts a string to an array by splitting it at certain characters. We will split the string at the ',' character, and we get an array with two entries. The first entry is the name of the dinosaur; the second is the path of the image. Now we have the details of our Dinosaur of the Day, we can add it to the database using an INSERT SQL statement. $sql = "INSERT INTO ".$prefix."_dinoportal_dotd(day,title,image) VALUES ('$today', '$dino_title', '$image')"; $result = $db->sql_query($sql); }} At this point, we should have a Dinosaur of the Day, one way or another, and so we can finalize the block output. However, we check the value of the $dino_title variable just in case there has been some problem either retrieving data or creating the new Dinosaur of the Day. If there has been a problem with either of these, there will be no value for the $dino_title variable, and if so, this code will ensure that the block content will remain empty, rather than producing some useless output. if ($dino_title){ $content = "Today's dinosaur is:<br><center><b>$dino_title</b><center><br>"; $content .= "<center><img src="$image" alt="$dino_title"></center><br>";} That's it, our block is complete! The key points of this block were the initial few lines that stopped the file from being requested directly, and this was also our first encounter with the data access code. Another thing to note from this example is the effort we made to ensure that the block output was only created if everything went smoothly. We suppressed errors when trying to read in a file, we checked that the reading of a file had actually given us some data, and then we didn't create any output if there was a problem with dino_title variable, which would be an indicator of some problem. All this means that if there is a problem, the visitor will not be confronted with lots of error messages, which could disturb the visitor and lead to a poor impression of your site, or even break the layout of the page, or reveal some information about your code that could be used against you. All that remains now is to set up this File block using the steps we saw in Article 4, and we are away! Data Access in PHP-Nuke In the code for creating the block we had a couple of lines with data access functions: $result = $db->sql_query($sql);$numrows = $db->sql_numrows($result);$row = $db->sql_fetchrow($result); PHP-Nuke uses a 'data abstraction layer', which means that you call functions against an object, which translates them into specific calls against the database of your choice. Generally, MySQL is the database used with PHP-Nuke, but you could choose another database server to power your site. A more pertinent advantage is that you don't need to use database-specific functions to access data in PHP-Nuke; you only need to learn about the functions of the data access object (You will still need to know some SQL to create queries that will be executed on the database). The code for the data access object is found in the file dbmysql.php. In fact, the db folder contains code for different types of database server, but this file is the one selected by default by PHP-Nuke for working with the MySQL database server. The data access object is a genuine object, that is, it's an instance of a class, sql_db in this case. Classes are one of the basics of object-oriented programming, but other than this instance PHP-Nuke does not make much use of object-oriented programming. A discussion of object-oriented programming in PHP is beyond the scope of this article series, and it won't particularly help here since PHP-Nuke makes so little use of it. All that we need to know is how to access the methods (functions) of the data access object. Object-oriented programming is covered in more detail in any book on PHP programming, and you can read an article about it at http://www.devarticles.com/c/a/PHP/Object-Oriented-Programming-in-PHP/. The data-access object provides a number of methods that you can use to execute a query, retrieve a row of data, check the number of rows returned by the query, or get the last inserted auto-increment field. Working with the object follows a similar process to the standard way of working with data in PHP using functions like mysql_query() or mysql_fetch_field(). To access data in PHP-Nuke, you will need two global variables, $prefix and $db. The $prefix variable holds the table prefix of the database tables, and this needs to be used in your SQL queries. The $db variable is the data access object itself. In our block example, we had these lines to create a SQL query and then execute it: $sql = "SELECT * from ".$prefix."_dinoportal_dotd WHERE day='$today'";$result = $db->sql_query($sql); Note the $db->sql_query() syntax. This syntax is used in PHP to call a method on an object, in this case the sql_query() method of the $db object. The sql_query() method executes an SQL query as its name suggests. You provide a string with the query that's to be executed as a parameter. Following the execution of a query, you can retrieve a row using the sql_fetchrow() method: $row = $db->sql_fetchrow($result); This method returns an array, and you can refer to the fields in the data using $row['fieldname'], as we do in the block example to get the title and image fields: $dino_title = $row['title'];$image = $row['image']; If you want to insert or update data, you need to create the relevant SQL query and then use the sql_query() function to do it: $sql = "INSERT INTO ".$prefix."_dinoportal_dotd(day,title,image) VALUES ('$today', '$dino_title', '$image')";$result = $db->sql_query($sql); This is only a small selection of the methods of the data access object. Another interesting one is the sql_nextid() method, which you can use after an INSERT statement to get the value of the last auto-increment field created. However, these are the methods that you will see the most of as you look around the code in PHP-Nuke. Module File and Folder Structure Before we get started creating a new module, let's have a look at the file structure of a typical module. A module is simply a collection of files (usually only PHP files) contained in a folder that goes in the modules folder in the root of the PHP-Nuke installation. The name of the folder is the name that PHP-Nuke will recognize the module by. However, we can't just place the files into the module folder in any order. There is an organization of files, subfolder names, and filenames that modules need to follow in order to function properly with PHP-Nuke. The image below shows the contents of the News module folder: We have already seen how PHP-Nuke switches between files in the module folder based on the value of the file query string variable. If there is no value for this variable, the index.php file of the module is used. Files that sit inside the module folder are the 'front-end' files, which will be used during a standard user's visit to the module. The code for the administration part of a module resides in the admin folder within the module folder. In earlier versions of PHP-Nuke (before 7.5), the administration code for any module would have to go into the admin folder (the one in the root of the PHP-Nuke installation), and would be mixed with the 'core' administration code. The decision to have a module's administration code contained within the module folder itself means that the module is much more self-contained, keeps people away from the core code itself, and generally makes the module easier to set up, install, and maintain. We'll see more about what is found in the admin folder when we create the administration area of our new module later in this article. We saw in Article 4 that the Language block allows you to change PHP-Nuke's user interface language. This ability to switch languages is something that has made PHP-Nuke so popular all around the world. This multi-language support is achieved by module developers avoiding coding any 'localizable' text into the module output. Localizable text is text that needs to be translated if a different interface language is selected. Instead of coding the text, PHP constants are used, with the values of the constants defined in a language file. The language files are found in the language folder of the module, and there is a separate language file for each language, each with a filename of the form lang-LANGUAGE.php. All PHP-Nuke needs to do is select the correct file based on the desired language. Creating a User Submissions Module Writing a new module allows you to extend PHP-Nuke to get it to do exactly what you want it to do. What we will do here is to create a general-purpose module that will allow users to submit content for modules that do not support user-submitted material. We'll call it UserSubmissions. It will work in the way the Submit News module works for stories: The user will submit the material through a form. The administrator will be notified of the new submission by email. The administrator will be able to see a list of the submitted material in the administration area, and can edit, delete, or approve the material to go into the database. The point of this module is that it does not touch the modules for which it allows the submission of content; everything will happen in the UserSubmissions module. In this article, we will only code in functionality for users to submit encyclopedia entries. It is straightforward to extend this to allow submissions for the Content module, or questions for the FAQ module. Conceptually what the module does is to: Present a form to the user similar to the one the administrator would use for entering an encyclopedia entry. Take the user's input and store it in a 'general' way in a single database table. After the administrator checks the input, the data is then stored in the encyclopedia's database tables using the same kind of code that the Encyclopedia module uses. We will see exactly what the 'general' way we store the data in is later. Basically, the module will take all the parts of the encyclopedia entry—the title, the text, the encyclopedia ID—and put them all together into one bundle, which can then be easily retrieved and broken out to form the individual pieces of data for the administrator to view, approve, or delete. Rather than presenting the development as a series of steps for you to follow, we will break the code up into various tasks, and then examine each piece of code or activity. You can type in the code as it is presented, although it is probably easiest to grab the example code for this module from the code download, and refer to it as we go. Module Development Steps The steps that we will follow to create the module are these: Create the module folder Create the database tables Code the front end (visitor view) of the module Adapt the code for multi-language support Set up module administration Code the administration area Rather unusually, we're going to start by coding the front end of the site. The reason this is unusual is that modules typically display some kind of data (that is what all the modules we have encountered in the article series do), and you would first need to enter this data into the database. This data is usually entered by the administrator, through the administration interface. With some example data in place, the front end of the site will be able to display it. It will be difficult to test the front end if it is supposed to display data and there is no data to display! This module does not require any existing data in the database to work. In fact, the data is entered by a standard visitor, and the administrator only has to look at this data, and edit or delete it. There is no facility in the administrator part of the module for the administrator to add new data into the module, which would rather defeat the point of this module! Thus we can start on the front end of the module quite happily. Let's start off with creating the module folder. Creating the Module Folder Create a new folder in the modules folder called UserSubmissions. This will be our module folder. Within this folder, create two new folders called admin and language. The admin folder will contain our administration code, and the language folder will contain the user interface language files. We create another folder, inside the admin folder, also called language. This folder will hold the language files for the module's administration interface. That's the first step out of the way, so let's move on to the database tables. Creating the Database Tables The module has only one database table. The table will be called <prefix>_usersubmissions. You can follow the same steps in phpMyAdmin as we did earlier for creating the block database table to create this table: CREATE TABLE dinop_usersubmissions ( id int(11) NOT NULL auto_increment, data text NOT NULL, parent_id int(11) NOT NULL default '0', type varchar(36) NOT NULL default '1', user_id int(11) NOT NULL default '0', date timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, title varchar(255) NOT NULL default '', user_name varchar(250) NOT NULL default '', PRIMARY KEY (id)) COMMENT='Table for holding user submitted content' ; Each row in this table will represent a single item of submitted content. The row will be identified by its id field. With only one table, you may be wondering how this module is going to be able to hold data from different modules. The answer is that the submitted data will be bundled up into a single object, then 'serialized' and stored in the data field. When the data is needed for viewing, it will be unbundled, and 'unserialized' back into a form that can be worked with. The 'title' of the submission will be stored in the title field. The type of module that we are storing data for will be held in the type field of the row. The details of the submitter will be stored in the user_id and user_name fields. We actually only use the user_name field in this version of the module, but we store both for future use. The date the item is submitted is held in the field called date. This field is a MySQL TIMESTAMP, and whenever a row is inserted, the current date and time will be inserted into the field automatically by the database, so we will not need to record the date and time ourselves. The final field is parent_id. Recall how an encyclopedia entry belongs to an Encyclopedia; a content page belongs to a Content category; a FAQ question belongs to a particular category, and so on. For each of these types of content, you needed to provide a 'parent' object that the content would belong to. That is where our parent_id field comes in. The ID of the parent object will be stored in this field. For an encyclopedia entry, this will be the ID of the chosen Encyclopedia.
Read more
  • 0
  • 0
  • 6188

article-image-organizing-your-content-effectively-using-joomla-15-sequel
Packt
06 Apr 2010
5 min read
Save for later

Organizing your Content Effectively using Joomla 1.5- A Sequel

Packt
06 Apr 2010
5 min read
Time for action - move content from one category to another The Activities section contains some articles that you might want to move to the News section. Let's clean up the Activities - Meetings category and move anything topical into the News - General News category: Navigate to Content | Article Manager. From the list, select the items you want to move from the Meetings category to the General News category. In this example, we've selected two articles: Click on Move on the toolbar. You'll be taken to the Move Articles screen: In the Move to Section/Category list, select News/General News. At the far right-hand side, you can check which articles are being moved. Click on Save. In the Article Manager screen, the three articles are now part of the News section. You can check this by clicking on the News link on the frontend Main Menu. What just happened? You've stood the real life challenge of content management! Now, you're not only able to create a sound content structure for your website, but you also know how to improve on it. By adding a new container for all news items and moving existing news content there you've now made room for growth on the SRUP site. Have a go hero - moving entire categories Sometimes you might want to move an entire category and all its contents to another section. Try this out for yourself-it's not much different from moving articles. Imagine you'd like to move the Reviews category from the Ugly Paintings section to another section. In the Category Manager, select the category you want to move and click on the Move button. Select the section you want to move things to, and click on Save. It's just as straightforward to move the entire category including all of its article contents back again. This flexibility is great when you're setting up or rearranging your site. Renaming sections or categories As we've just seen, Joomla! allows you to easily rearrange your site structure and its contents. You can also rename sections and categories that already contain articles; no content will be lost. Time for action - rename a section On your client's website there's an Activities section. Your client wants to make it clear this section is not about activities organized by other art societies-it's only about SRUP. Could you please change the name of the section to SRUP Activities? It's a breeze. Navigate to Content | Section Manager and click on the title of the Activities section to open it for editing. In the Section: [Edit] screen, change the Title to SRUP Activities. In the Alias: field, remove the existing alias (remember, the Alias is Joomla!'s internal name for the article used when creating user-friendly URLs). Leave this box blank; Joomla! will fill it with srup-activities when you apply or save your changes. You can check that now by clicking on Apply. You'll notice the Alias box is filled out automatically. Click on Save. What just happened? By changing a section or category name, all of Joomla!'s internal references to the name are updated automatically. All articles and categories in the renamed section will reflect the changes you made. In the Article Manager, for example, all items that belonged to the Activities section are now updated to show they are in the SRUP Activities section. No manual labor here and more importantly, nothing is lost! On the frontend, the new section name shows up on the section overview page when the user clicks on the Activities link: Have a go hero - name and rename! Using appropriate, short, and descriptive labels for sections and categories (and for the menu links pointing to them) is really essential. After all, these are the words that guide your visitors to the content you want them to discover. It's a good idea to tweak these labels until you're perfectly happy with them. To modify the names of categories, navigate to the Category Manager; it's similar to changing section names. Maybe you would like to change menu link labels too, as these don't automatically change with the category or section name. Try to find short and appropriate menu link labels. To change menu link labels, navigate to the Main Menu, select any of the menu items and change what's in the Title field (that is, SRUP Activities). When changing Titles (of Sections/Categories/Menu Link Items) make sure to clear the contents of the Alias box. Joomla! will automatically create an Alias for the new Title. Changing section and category settings You've already created a good deal of sections and categories without altering any of the default settings. In some cases, however, you might want some more control over the section or category you're editing. In the table below you can see the options that are available in the Section/Category: [New] or Section/Category: [Edit] screen. Basically, you can customize these settings for two purposes: To determine whether a section or category is visible (and which user groups can see it) To add a short descriptive text whenever the section or category contents are displayed. We'll cover both the Section and Category edit screen in the overview next, as all settings and options are identical. The only difference is that when adding a category, Joomla! wants you to specify the section that holds the new category. This is what the Section: [New] and the Category: [New] screen look like:
Read more
  • 0
  • 0
  • 1582

article-image-understanding-dotnetnuke-core-architecture-extension
Packt
06 Apr 2010
8 min read
Save for later

Understanding the DotNetNuke Core Architecture- An Extension

Packt
06 Apr 2010
8 min read
The global files The Global.asax.vb and Globals.vb files share similar names but the parts they play in DotNetNuke are vastly different. The Global.asax.vb is used by DotNetNuke to handle application-level events raised by the ASP.NET runtime. The Globals.vb file, on the other hand, is a public module (which is the same as a static class in C#) that contains global utility functions. Before we take a look at these fi les, we first want to look at what object is being passed around in these transactions. Global.asax.vb Much of the logic that used to reside in the Global.asax.vb fi le has now been abstracted to the HTTP modules. We will look into the code that remains. Application_Start When the fi rst request is made to your application (when the fi rst user accesses the portal), a pool of HttpApplication instances are created and the Application_Start event is fi red. This will (theoretically) fire just once and on the first HttpApplication object in the pool. When there is inactivity on your portal for a certain amount of time, the application (or application pool) will be recycled. When the pool is recycled, your application will restart (and this event will fi re again) when the next request is made for your application. As the new version of DotNetNuke uses the .NET website structure, you will find the Global.asax.vb fi le in the App_Code folder. In the Application_Start, we are loading all of the confi gured providers to ensure they are available to the rest of the framework when needed. These are performed in the Application_Start because we want them to be called only once. Private Sub Application_Start(ByVal Sender As Object, ByVal E AsEventArgs)If Config.GetSetting("ServerName") = "" ThenServerName = Server.MachineNameElseServerName = Config.GetSetting("ServerName")End IfComponentFactory.Container = New SimpleContainer()'Install most Providers as Singleton LifeStyleComponentFactory.InstallComponents _(New ProviderInstaller("data", GetType(DotNetNuke.Data.DataProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("caching", GeType(Services.Cache.CachingProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("logging", GetType(Services.Log.EventLog.LoggingProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("scheduling", GetType(Services.Scheduling.SchedulingProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("searchIndex", GetType(Services.Search.IndexingProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("searchDataStore", GetType(Services.Search.SearchDataStoreProvider)))ComponentFactory.InstallComponents_(New ProviderInstaller("friendlyUrl", GetType(Services.Url.FriendlyUrl.FriendlyUrlProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("members", GetType(DotNetNuke.Security.Membership.MembershipProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("roles", GetType(DotNetNuke.Security.Roles.RoleProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("profiles", GetType(DotNetNuke.Security.Profile.ProfileProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("permissions", GetType(DotNetNuke.Security.Permissions.PermissionProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("outputCaching", GetType(DotNetNuke.Services.OutputCache.OutputCachingProvider)))ComponentFactory.InstallComponents _(New ProviderInstaller("moduleCaching", GetType(DotNetNuke.Services.ModuleCache.ModuleCachingProvider)))Dim provider As DotNetNuke.Security.Permissions.PermissionProvider = _DotNetNuke.ComponentModel.ComponentFactory.GetComponent _(Of DotNetNuke.Security.Permissions.PermissionProvider)()If provider Is Nothing ThenComponentFactory.RegisterComponentInstance _(Of DotNetNuke.Security.Permissions.PermissionProvider) _(New DotNetNuke.Security.Permissions.PermissionProvider())End If'Install Navigation and Html Providers as NewInstanceLifestyle (ie a new instance is generated each time the type isrequested, as there are often multiple instances on the page)ComponentFactory.InstallComponents _(New ProviderInstaller("htmlEditor", _GetType(Modules.HTMLEditorProvider.HtmlEditorProvider), _ComponentLifeStyleType.Transient))ComponentFactory.InstallComponents _(New ProviderInstaller("navigationControl", _GetType(Modules.NavigationProvider.NavigationProvider), _ComponentLifeStyleType.Transient))End Sub In previous versions of DotNetNuke, there was a great deal happening in this method. However, this code has been moved into various methods inside of the Initialize class. This was done to support the integrated pipeline mode of IIS 7. If you would like to take a look at what is happening inside of the Initialize class, it can be found in the Common folder of the DotNetNuke.Library project. Examining Application_BeginRequest The Application_BeginRequest is called for each request made to your application. In other words, this will fi re every time a page (tab), or other web request handlers such as a web service or an ashx handler, is accessed in your portal. This section is used to implement the scheduler built into DotNetNuke. Starting in version 2.0, two items, "users online" and "site log", require recurring operations. Also in this method is the call to the Initialize.Init() method that was moved out of the Appli cation_Startup method as mentioned previously. You can fi nd out more about the scheduler by looking at the DotNetNuke Scheduler.pdf document (only if you download the documentation pack). Also note that, there is a host setting that defi nes the running mode of a scheduler, you can check for a scheduler run on every request to your portal, or run the scheduler in a timer mode. Private Sub Global_BeginRequest(ByVal sender As Object, _ByVal e As EventArgs) Handles Me.BeginRequestDim app As HttpApplication = CType(sender, HttpApplication)Dim Request As HttpRequest = app.RequestIf Request.Url.LocalPath.ToLower.EndsWith("scriptresource.axd") _OrElse Request.Url.LocalPath.ToLower.EndsWith("webresource.axd") _OrElse Request.Url.LocalPath.ToLower.EndsWith("gif") _OrElse Request.Url.LocalPath.ToLower.EndsWith("jpg") _OrElse Request.Url.LocalPath.ToLower.EndsWith("css") _OrElse Request.Url.LocalPath.ToLower.EndsWith("js") ThenExit SubEnd If' all of the logic which was previously in Application_Start' was moved to Init() in order to support IIS7 integrated pipelinemode' ( which no longer provides access to HTTP context withinApplication_Start )Initialize.Init(app)'run schedule if in Request modeInitialize.RunSchedule(Request)End Sub The Globals.vb file As part of the namespace-reorganization effort associated with DotNetNuke version 3.0, general utility functions, constants, and enumerations have all been placed in a public module (as just mentioned, module here refers to VB.NET module keyword, not a DotNetNuke module) named Globals . As items in a .NET module are inherently shared, you do not need to instantiate an object in order to use the functions found here. In this module, you will find not only global constants, as shown in the following code: Public Const glbRoleAllUsers As String = "-1"Public Const glbRoleSuperUser As String = "-2"Public Const glbRoleUnauthUser As String = "-3"Public Const glbRoleNothing As String = "-4"Public Const glbRoleAllUsersName As String = "All Users"Public Const glbRoleSuperUserName As String = "Superuser"Public Const glbRoleUnauthUserName As String ="Unauthenticated Users"Public Const glbDefaultPage As String = "Default.aspx"Public Const glbHostSkinFolder As String = "_default"Public Const glbDefaultControlPanel As String = "Admin/ControlPanel/IconBar.ascx"Public Const glbDefaultPane As String = "ContentPane"Public Const glbImageFileTypes As String = "jpg,jpeg,jpe,gif,bmp,png,swf"Public Const glbConfigFolder As String = "Config"Public Const glbAboutPage As String = "about.htm"Public Const glbDotNetNukeConfig As String = "DotNetNuke.config"Public Const glbSuperUserAppName As Integer = -1Public Const glbProtectedExtension As String = ".resources"Public Const glbEmailRegEx As String = "b[a-zA-Z0-9._%-+']+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,4}b"Public Const glbScriptFormat As String = "<script type=""text/javascript"" src=""{0}"" ></script>"   but a tremendous number of public functions to help you do everything, from retrieving the domain name, as shown: Public Function GetDomainName(ByVal Request As HttpRequest, ByValParsePortNumber As Boolean) As String to setting the focus on a page: Public Sub SetFormFocus(ByVal control As Control) This one file contains a wealth of information for the developer. As there are more than 3070 lines in the fi le and the methods are fairly straightforward, we will not be stepping through this code. The Globals.vb fi le can now be found in the DotNetNuke.Library project in the Common folder. Putting it all together We have spent some time looking at some of the major pieces that make up the core architecture. You might be asking yourself how all this works together. In this section, we will walk you through an overview version of what happens when a user requests a page on your portal. A shown in the preceding diagram, when a user requests any page on your portal, the HTTP Modules that have been declared in the web.config file are hooked into the pipeline. Typically, these modules use the Init method to attach event handlers to application events. The request then goes through the Global.asax page. As just mentioned, some of the events fi red here will be intercepted and processed by the HTTP modules, but the call to run the scheduler will happen in this fi le. Next, the page that was requested, Default.aspx, will be processed. As we stated at the beginning of this article, all requests are sent to the Default.aspx page and all the controls and skins needed for the page are created dynamically by reading the tabID from the requested URL. So let's begin by looking at the HTML for this page.
Read more
  • 0
  • 0
  • 1913
article-image-understanding-dotnetnuke-core-architecture
Packt
06 Apr 2010
13 min read
Save for later

Understanding the DotNetNuke Core Architecture

Packt
06 Apr 2010
13 min read
Architecture overview As opposed to traditional web applications that may rely on a multitude of web pages to deliver content, DotNetNuke uses a single main page called Default.aspx. The content for this page is generated dynamically by using a tabID value to retrieve the skin and modules needed to build the page requested, from the DotNetNuke database. Before we move on, we should discuss what is meant by a tab and a page. As you read this article, you will notice the word "tab" is sometimes used when referring to pages in your DotNetNuke portal. In the original IBuySpy application, pages were referred to as tabs because they resembled tabs when added to the page. IBuySpy application, the skeleton ASP.NET Framework, was created by Microsoft to demonstrate ASP.NET features, and DotNetNuke was originally derived from it. This continued in the original versions of the DotNetNuke project. Starting with version 3.0, and continuing with version 5.2.x, there has been an ongoing effort to rename most of these instances to reflect what they really are: pages. Most references to "tabs" have been changed to "pages", but the conversion is not complete. For this reason, you will see both—tabs and pages—in the database, in the project files, and in this text. We will use these terms interchangeably throughout this text as we look into the core architecture of DNN. We will begin with a general overview of what happens when a user requests a page on your DotNetNuke portal. The process for rendering a page in DotNetNuke works like this: a user navigates to a page on your portal; this calls the Default.aspx page, passing the tabid parameter in the querystring to let the application identify the page being requested. The example http://www.dotnetnuke.com/Default. aspx?tabid=476 demonstrates this. DotNetNuke 3.2 introduced something called URL rewriting. This takes the querystring shown above and rewrites it so that it is in a format that helps increase search engine hits. We will cover the HTTP module that is responsible for this in more detail later in this article. The rewritten URL would resemble http://localhost/DotNetNuke/Home.aspx. This assumes that the page name for tabid 476 is Home. While referring to URLs in this article we will be using the non-rewritten version of the URL. URL rewriting can be turned off in the friendly URL section of the Host Settings page. The querystring value (?tabid=476) is sent to the database, where the information required for the page is retrieved, as shown in the following diagram: The portal that the user is accessing can be determined in a number of ways, but as you can see from the Tabs table (see the following screenshot), each page/tab contains a reference to the portal it belongs to in the PortalID field. Once the server has a reference to the page that the user requested (using the tabID), it can determine what modules belong to that page. Although there are many more tables involved in this process, you can see that these tables hold not only the page and modules needed to generate the page, but also what pane to place them on (PaneName) and what container skin to apply (ContainerSrc). All of this information is returned to the web server, and the Default.aspx page is constructed with it and returned to the user who requested it along with the required modules and skins, as shown in the following diagram. Now, this is of course a very general overview of the process, but as we work through this article, we will delve deeper into the code that makes this process work, and in the end, show a request work its way through the framework to deliver a page to a user. Diving into the core There are over 160,000 lines of code in the DotNetNuke application. There is no practical (or even possible) way to cover the entire code base. In this section, we will go in depth into what I believe are the main portions of the code base: the PortalSettings as well as the companion classes found in the portals folder; the web.config file including the HTTP Modules and Providers; and the Global.asax and Globals.vb files. We will start our discussion of the core with two objects that play an integral part in the construction of the architecture. The Context object and the PortalSettings class will both be referred to quite often in the code, and so it is important that you have a good understanding of what they do. Using the Context object in your application ASP .NET has taken intrinsic objects like the Request and the Application objects and wrapped them together with other relevant items into an intrinsic object called Context. The Context object (HttpContext) can be found in the System.Web namespace. In the following table, you will find some of the objects that make up the HttpContext object. Title Description Application Gets the HttpApplicationState object for the current HTTP request. Cache Gets the Cache object for the current HTTP request. Current Gets the HttpContext object for the current HTTP request. This is a static (shared in VB) property of the HttpContext class, through which you access all other instance properties discussed in this table, that together enable you to process and respond to an HTTP request. Items Gets a key-value collection that can be used to organize and share data between an IHttpModule and an IHttpHandler during an HTTP request. Request Gets the HttpRequest object for the current HTTP request. This is used to extract data submitted by the client, and information about the client itself (for example, IP ), and the current request. Response Gets the HttpResponse object for the current HTTP response. This is used to send data to the client together with other response-related information such as headers, cacheability, redirect information, and so on. Server Gets the HttpServerUtility object that provides methods used in processing web requests. Session Gets the HttpSessionState instance for the current HTTP request. User Gets or sets security information for the current HTTP request. Notice that most of the descriptions talk about the "current" request object, or the "current" response object. The Global.asax file, which we will look at soon, reacts on every single request made to your application, and so it is only concerned with whoever is "currently" accessing a resource. The HttpContext object contains all HTTP-specific information about an individual HTTP request. In particular, the HttpContext.Current property can give you the context for the current request from anywhere in the application domain. The DotNetNuke core relies on the HttpContext.Current property to hold everything from the application name to the portal settings and through this makes it available to you. The PortalSettings class The portal settings play a major role in the dynamic generation of your pages and as such will be referred to quite often in the other portions of the code. The portal settings are represented by the PortalSettings class which you will find in the EntitiesPortalPortalSettings.vb file. As you can see from the private variables in this class, most of what goes on in your portal will at some point needto access this object. This object will hold everything from the ID of the portal to the default language, and as we will see later, is responsible for determining the skins and modules needed for each page. Private _PortalId As IntegerPrivate _PortalName As StringPrivate _HomeDirectory As StringPrivate _LogoFile As StringPrivate _FooterText As StringPrivate _ExpiryDate As DatePrivate _UserRegistration As IntegerPrivate _BannerAdvertising As IntegerPrivate _Currency As StringPrivate _AdministratorId As IntegerPrivate _Email As StringPrivate _HostFee As SinglePrivate _HostSpace As IntegerPrivate _PageQuota As IntegerPrivate _UserQuota As IntegerPrivate _AdministratorRoleId As IntegerPrivate _AdministratorRoleName As StringPrivate _RegisteredRoleId As IntegerPrivate _RegisteredRoleName As StringPrivate _Description As StringPrivate _KeyWords As StringPrivate _BackgroundFile As StringPrivate _GUID As GuidPrivate _SiteLogHistory As IntegerPrivate _AdminTabId As IntegerPrivate _SuperTabId As IntegerPrivate _SplashTabId As IntegerPrivate _HomeTabId As IntegerPrivate _LoginTabId As IntegerPrivate _UserTabId As IntegerPrivate _DefaultLanguage As StringPrivate _TimeZoneOffset As IntegerPrivate _Version As StringPrivate _ActiveTab As TabInfoPrivate _PortalAlias As PortalAliasInfoPrivate _AdminContainer As SkinInfoPrivate _AdminSkin As SkinInfoPrivate _PortalContainer As SkinInfoPrivate _PortalSkin As SkinInfoPrivate _Users As IntegerPrivate _Pages As Integer The PortalSettings class itself is simple. It is filled by using one of the constructors that accepts one or more parameters. These constructors then call the private GetPortalSettings method . The method is passed a tabID and a PortalInfo object. You already know that the tabID represents the ID of the page being requested, but the PortalInfo object is something new. This class can be found in the same folder as the PortalSettings class and contains the basic information about a portal such as PortalID, PortalName, and Description. However, from the PortalSettings object, we can retrieve all the information associated with the portal. If you look at the code inside the constructors, you will see that the PortalController object is used to retrieve the PortalInfo object. The PortalInfo object is saved in cache for the time that is specifi ed on the Host Settings page. A drop-down box on the Host Settings page (DesktopModulesAdminHostSettingsHostSettings.ascx) is used to set the cache. No caching:0 Light caching:1 Moderate caching:3 Heavy caching:6 The value in this dropdown ranges from 0 to 6; the code in the DataCache object takes the value set in the drop-down and multiplies it by 20 to determine the cache duration. Once the cache time is set, the method checks if the PortalSettings object already resides there. Retrieving these settings from the database for every request would cause your site to run slowly, so placing them in a cache for the duration you select helps increase the speed of your site. Recent versions of DotNetNuke have focused heavily on providing an extensive caching service. An example of this can be seen in the following code: Dim cacheKey As String = String.Format(DataCache.PortalCacheKey,PortalId.ToString())Return CBO.GetCachedObject(Of PortalInfo)(New CacheItemArgs(cacheKey, DataCache.PortalCacheTimeOut,DataCache.PortalCachePriority, PortalId),AddressOf GetPortalCallback) We can see in the previous code that the CBO object is used to return an object from the cache. CBO is an object that is seen frequently throughout the DotNetNuke core. This object's primary function is to return the populated business object. This is done in several ways using different methods provided by CBO. Some methods are used to map data from an IDataReader to the properties of a business object. However, in this example, the Get CachedObject method handles the logic needed to determine if the object should be retrieved from the cache or from the database. If the object is not already cached, it will use the GetPortalCallback method passed to the method to retrieve the portal settings from the database. This method is located in the PortalController class (EntitiesPortalPortalController.vb) and is responsible for retrieving the portal information from the database. Dim portalID As Integer = DirectCast(cacheItemArgs.ParamList(0),Integer)Return CBO.FillObject(Of PortalInfo)(DataProvider.Instance _.GetPortal(portalID, Entities.Host.Host.ContentLocale.ToString)) This will fi ll the PortalInfo object (EntitiesPortalPortalInfo.vb), which as we mentioned, holds the portal information. This object in turn is returned to the GetCachedObject method. Once this is complete, the object is then cached to help prevent the need to call the database during the next request for the portal information. There is also a section of code (not shown) that verifi es whether the object was successfully stored in the cache and adds an entry to the event log if the item failed to be cached. ' if we retrieved a valid object and we are using cachingIf objObject IsNot Nothing AndAlso timeOut > 0 Then' save the object in the cacheDataCache.SetCache(cacheItemArgs.CacheKey, objObject, _cacheItemArgs.CacheDependency, Cache.NoAbsoluteExpiration, _TimeSpan.FromMinutes(timeOut), cacheItemArgs.CachePriority, _cacheItemArgs.CacheCallback)…End If After the portal settings are saved, the properties of the current tab information are retrieved and populated in the ActiveTab property. The current tab is retrieved by using the tabID that was originally passed to the constructor. This is handled by the VerifyPortalTab method and done by getting a list of all of the tabs for the current portal. Like the portal settings themselves, the tabs are saved in cache to boost performance. The calls to the caching provider, this time, are handled by the TabController (EntitiesTabsTabController.vb). In the last VerifyPortalTab method, the code will loop through all of the host and non-host tabs, returned by the TabController, for the site until the current tab is located. ' find the tab in the portalTabs collectionIf TabId <> Null.NullInteger ThenIf portalTabs.TryGetValue(TabId, objTab) Then'Check if Tab has been deleted (is in recycle bin)If Not (objTab.IsDeleted) ThenMe.ActiveTab = objTab.Clone()isVerified = TrueEnd IfEnd IfEnd If' find the tab in the hostTabs collectionIf Not isVerified AndAlso TabId <> Null.NullInteger ThenIf hostTabs.TryGetValue(TabId, objTab) Then'Check if Tab has been deleted (is in recycle bin)If Not (objTab.IsDeleted) ThenMe.ActiveTab = objTab.Clone()isVerified = TrueEnd IfEnd IfEnd If If the tab was not found in either of these collections, the code attempts to use the splash page, home page, or the fi rst page of the non-host pages. After the current tab is located, further handling of some of its properties is done back in the GetPortalSettings method. This includes formatting the path for the skin and default container used by the page, as well as collecting information on the modules placed on the page. Me.ActiveTab.SkinSrc = _SkinController.FormatSkinSrc(Me.ActiveTab.SkinSrc, Me)Me.ActiveTab.SkinPath = _SkinController.FormatSkinPath(Me.ActiveTab.SkinSrc)…For Each kvp As KeyValuePair(Of Integer, ModuleInfo) In _objModules.GetTabModules(Me.ActiveTab.TabID)' clone the module object _( to avoid creating an object reference to the data cache )Dim cloneModule As ModuleInfo = kvp.Value.Clone' set custom propertiesIf Null.IsNull(cloneModule.StartDate) ThencloneModule.StartDate = Date.MinValueEnd IfIf Null.IsNull(cloneModule.EndDate) ThencloneModule.EndDate = Date.MaxValueEnd If' containerIf cloneModule.ContainerSrc = "" ThencloneModule.ContainerSrc = Me.ActiveTab.ContainerSrcEnd IfcloneModule.ContainerSrc = _SkinController.FormatSkinSrc(cloneModule.ContainerSrc, Me)cloneModule.ContainerPath = _SkinController.FormatSkinPath(cloneModule.ContainerSrc)' process tab panesIf objPaneModules.ContainsKey(cloneModule.PaneName) = False ThenobjPaneModules.Add(cloneModule.PaneName, 0)End IfcloneModule.PaneModuleCount = 0If Not cloneModule.IsDeleted ThenobjPaneModules(cloneModule.PaneName) = _objPaneModules(cloneModule.PaneName) + 1cloneModule.PaneModuleIndex = _objPaneModules(cloneModule.PaneName) - 1End IfMe.ActiveTab.Modules.Add(cloneModule)Next We have now discussed some of the highlights of the PortalSettings object as well as how it is populated with the information it contains. In doing so, we also saw abrief example of the robust caching service provided by DotNetNuke. You will see the PortalSettings class referenced many times in the core DotNetNuke code, so gaining a good understanding of how this class works will help you to become more familiar with the DNN code base. You will also fi nd this object to be very helpful while building custom extensions for DotNetNuke. The caching provider itself is a large topic, and reaches beyond the scope of this article. However, simply understanding how to work with it in the ways shown in these examples should satisfy the needs of most developers. It is important to note that, you can get any type of object cached by DNN by passing in a key for your object to DataCache.SetCache method, together with the data, and some optional arguments. While fetching the object back from DataCache.GetCache, you pass in the same key, and check the result. A non-null (non-Nothing in VB) return value means you have fetched the object successfully from the cache, otherwise you would need to fetch it from the database.
Read more
  • 0
  • 0
  • 4138

article-image-installation-and-getting-started-firebug
Packt
05 Apr 2010
3 min read
Save for later

Installation and Getting Started with Firebug

Packt
05 Apr 2010
3 min read
What is Firebug? Firebug is a free, open source tool that is available as a Mozilla Firefox extension, and allows debugging, editing, and monitoring of any website's CSS, HTML, DOM, and JavaScript. It also allows performance analysis of a website. Furthermore, it has a JavaScript console for logging errors and watching values. Firebug has many other tools to enhance the productivity of today's web developer. Firebug integrates with Firefox to put a wealth of development tools at our fingertips while we browse a website. Firebug allows us to understand and analyze the complex interactions that take place between various elements of any web page when it is loaded by a browser. Firebug simply makes it easier to develop websites/applications. It is one of the best web development extensions for Firefox. Firebug provides all the tools that a web developer needs to analyze, debug, and monitor JavaScript, CSS, HTML, and AJAX. It also includes a debugger, error console, command line, and a variety of useful inspectors. Although Firebug allows us to make changes to the source code of our web page, the changes are made to the copy of the HTML code that has been sent to the browser by the server. Any changes to the code are made in the copy that is available with the browser. The changes don't get reflected in the code that is on the server. So, in order to ensure that the changes are permanent, corresponding changes have to be made in the code that resides on the server. The history of Firebug Firebug was initially developed by Joe Hewitt, one of the original Firefox creators, while working at Parakey Inc. Facebook purchased Parakey in July, 2007. Currently, the open source development and extension of Firebug is overseen by the Firebug Working Group. It has representation from Mozilla, Google, Yahoo, IBM, Facebook, and many other companies. Firebug 1.0 Beta was released in December 2006. Firebug usage has grown very fast since then. Approximately 1.3 million users have Firebug installed as of January 2009. The latest version of Firebug is 1.5. Today, Firebug has a very open and thriving community. Some individuals as well as some companies have developed very useful plugins on top of Firebug. The need for Firebug During the 90s, websites were mostly static HTML pages, JavaScript code was considered a hack, and there were no interactions between page components on the browser side. The situation is not the same anymore. Today's websites are a product of several distinct technologies and web developers must be proficient in all of them—HTML, CSS, JavaScript, DOM, and AJAX, among others. Complex interactions happen between various page components on the browser side. However, web browsers have always focused on the needs of the end users; as a result, web developers have long been deprived of a good tool on the client/browser side to help them develop and debug their code. Firebug fills this gap very nicely—it provides all the tools that today's web developer needs in order to be productive and efficient with code that runs in the browser. Firebug capabilities Firebug has a host of features that allow us to do the following (and much more!): Inspect and edit HTML Inspect and edit CSS and visualize CSS metrics Use a performance tuning application Profile and debug JavaScript Explore the DOM Analyze AJAX calls
Read more
  • 0
  • 0
  • 4306
Modal Close icon
Modal Close icon