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-user-authentication-codeigniter-17-using-twitter-oauth
Packt
21 May 2010
6 min read
Save for later

User Authentication with Codeigniter 1.7 using Twitter oAuth

Packt
21 May 2010
6 min read
(Read more interesting articles on CodeIgniter 1.7 Professional Development here.) How oAuth works Getting used to how Twitter oAuth works takes a little time. When a user comes to your login page, you send a GET request to Twitter for a set of request codes. These request codes are used to verify the user on the Twitter website. The user then goes through to Twitter to either allow or deny your application access to their account. If they allow the application access, they will be taken back to your application. The URL they get sent to will have an oAuth token appended to the end. This is used in the next step. Back at your application, you then send another GET request for some access codes from Twitter. These access codes are used to verify that the user has come directly from Twitter, and has not tried to spoof an oAuth token in their web browser. Registering a Twitter application Before we write any code, we need to register an application with Twitter. This will give us the two access codes that we need. The first is a consumer key, and the second is a secret key. Both are used to identify our application, so if someone posts a message to Twitter through our application, our application name will show up alongside the user's tweet. To register a new application with Twitter, you need to go to http://www.twitter.com/apps/new. You'll be asked for a photo for your application and other information, such as website URL, callback URL, and a description, among other things. You must select the checkbox that reads Yes, use Twitter for login or you will not be able to authenticate any accounts with your application keys. Once you've filled out the form, you'll be able to see your consumer key and consumer secret code. You'll need these later. Don't worry though; you'll be able to get to these at any time so there's no need to save them to your hard drive. Here's a screenshot of my application: Downloading the oAuth library Before we get to write any of our CodeIgniter wrapper library, we need to download the oAuth PHP library. This allows us to use the oAuth protocol without writing the code from scratch ourselves. You can find the PHP Library on the oAuth website at www.oauth.net/code. Scroll down to PHP and click on the link to download the basic PHP Library; or just visit: http://oauth.googlecode.com/svn/code/php/—the file you need is named OAuth.php. Download this file and save it in the folder system/application/libraries/twitter/—you'll need to create the twitter folder. We're simply going to create a folder for each different protocol so that we can easily distinguish between them. Once you've done that, we'll create our Library file. Create a new file in the system/application/libraries/ folder, called Twitter_oauth.php. This is the file that will contain functions to obtain both request and access tokens from Twitter, and verify the user credentials. The next section of the article will go through the process of creating this library alongside the Controller implementation; this is because the whole process requires work on both the front-end and the back-end. Bear with me, as it could get a little confusing, especially when trying to implement a brand new type of system such as Twitter oAuth. Library base class Let's break things down into small sections. The following code is a version of the base class with all its guts pulled out. It simply loads the oAuth library and sets up a set of variables for us to store certain information in. Below this, I'll go over what each of the variables are there for. <?phprequire_once(APPPATH . 'libraries/twitter/OAuth.php');class Twitter_oauth{ var $consumer; var $token; var $method; var $http_status; var $last_api_call;}?> The first variable you'll see is $consumer—it is used to store the credentials for our application keys and the user tokens as and when we get them. The second variable you see on the list is $token—this is used to store the user credentials. A new instance of the oAuth class OAuthConsumer is created and stored in this variable. Thirdly, you'll see the variable $method—this is used to store the oAuth Signature Method (the way we sign our oAuth calls). Finally, the last two variables, $http_status and $last_api_call, are used to store the last HTTP Status Code and the URL of the last API call, respectively. These two variables are used solely for debugging purposes. Controller base class The Controller is the main area where we'll be working, so it is crucial that we design the best way to use it so that we don't have to repeat our code. Therefore, we're going to have our consumer key and consumer secret key in the Controller. Take a look at the Base of our class to get a better idea of what I mean. <?phpsession_start();class Twitter extends Controller{ var $data; function Twitter() { parent::Controller(); $this->data['consumer_key'] = ""; $this->data['consumer_secret'] = "";} The global variable $data will be used to store our consumer key and consumer secret. These must not be left empty and will be provided to you by Twitter when creating your application. We use these when instantiating the Library class, which is why we need it available throughout the Controller instead of just in one function. We also allow for sessions to be used in the Controller, as we want to temporarily store some of the data that we get from Twitter in a session. We could use the CodeIgniter Session Library, but it doesn't offer us as much flexibility as native PHP sessions; this is because with native sessions we don't need to rely on cookies and a database, so we'll stick with the native sessions for this Controller.
Read more
  • 0
  • 0
  • 3370

article-image-creating-custom-content-type-paster-plone-3
Packt
20 May 2010
18 min read
Save for later

Creating a Custom Content Type with Paster in Plone 3

Packt
20 May 2010
18 min read
(Further resources on Plone see here.) we have used a graphic application (ArgoUML) to draw a UML model that was automatically transformed by ArchGenXML into an Archetypes-based content type for Plone. In this article, we'll create a content type product with another tool, paster. It's not a graphic application, but it's as easy to use as writing a few characters. We used paster earlier to create a buildout-based Zope instance and an egg-structured Plone product. Here we'll use it to create a full Archetype product, its schema fields, and even the required tests to make sure everything is working as intended Creating an Archetypes product with paster There are several steps to take with paster to produce a full and useful content type. The first one should be the creation of the structure, meaning the product directory organization. Getting ready The final destination of this product, at least at development stage, is the src folder of our buildout directory. There is where we place our packages source code while we are working on them, until they become eggs (to see how to turn them into eggs read Submitting products to an egg repository). Thus go to your buildout directory and then get inside the src folder: cd ./src Make sure you have the latest ZopeSkel installed. ZopeSkel is the name of a Python package with a collection of skeletons and templates to create commonly used Zope and Plone projects via a paster command. easy_install -U ZopeSkel   How to do it… Create a package for the new add-on product: We are going to create a new package called pox.video. The pox prefix is taken from PloneOpenX (the website we are working on) and will be the namespace of our product. paster create -t archetype Fix the main configure.zcml file to prevent errors: Open the just created configure.zcml file in the pox/video folder and comment the internationalization registration like this: <!-- <i18n:registerTranslations directory="locales" /> --> Update the Zope instance with the new product: To let Zope know that there's new code to be used, let's update our instance buildout.cfg file. In the main [buildout] section, modify the eggs and develop parameters like this: [buildout] ... eggs = ... pox.video ... develop = src/pox.video Automatically add the product in a Plone site: We can install our brand new product automatically during buildout. So add a pox.video line inside the [plonesite] part's products parameter: [plonesite] recipe = collective.recipe.plonesite ... products = ... pox.video Rebuild and relaunch the Zope instance: Build your instance and, if you want to, launch it to check that the pox.video product is installed (not strictly necessary though). ./bin/buildout ./bin/instance fg How it works… So far we have a skeleton product, which is composed basically of boilerplate (we will build on it further). However, it has all the necessary code to be installed, which is important. The paster command of Step 1 in How to do it… creates a package using the archetype available template. When run, it will output some informative text and then a short wizard will be started to select some options. The most important are the first five ones: Option Value Enter project name pox.video Expert mode? Choose whatever option you like. Project Title Video Version 1.0 Description Video content type for PloneOpenX website Add whatever you want to the remaining options (if you chose other than easy mode), or just hit Enter to each one. After selecting the last option, you'll get an output like this (a little longer actually): Creating template basic_namespace Creating directory .pox.video Creating template archetype Recursing into +namespace_package+ Recursing into +package+ Recursing into content Recursing into interfaces Recursing into portlets Recursing into profiles Recursing into tests ... The project you just created has local commands. These can be used from within the product. usage: paster COMMAND Commands: addcontent Adds plone content types to your project For more information: paster help COMMAND The first group of lines tells us something about the created directory structure. We have a pox.video (project name) folder, containing a pox (namespace) folder, which contains a video (package) folder, which in turn contains several sub-packages: content, interfaces, portlets, profiles, and tests. In the following sections, we are going to deal with all of them except portlets, which will be tackled in Creating a portlet package, Customizing a new portlet according to our requirements, and Testing portlets. The second group of lines (after the ellipsis) gives us very important information: we can use particular local commands inside our fresh product. More of this in the next section. Step 2 in the preceding procedure is to tell Zope about the new available package. By adding pox.video in the eggs parameter, we add it in Zope's PYTHONPATH. We also have to add the package's location in the develop parameter. If not, the buildout process would try to fetch it from some of the URLs listed in the find-links parameter. During start up, Zope 2 loads (Five does the job actually) configuration files, usually configure.zcml, for all the products and packages inside the folders that are listed in the [instance] section's products parameter. For other Python packages outside those folders, a ZCML slug is required for the product to be loaded. Fortunately, from Plone 3.3 onwards, the ZCML slug is not needed if packages to be installed use z3c.autoinclude, which automatically detects and includes ZCML files. Although we were not aware of that, when we created the pox.video package with paster, z3c.autoinclude was added as an entry point in the setup.py file. Open it in the main pox.video folder to check it: ... setup(name='pox.video', version=version, description="Video content type for PloneOpenX website", ... entry_points=""" # -*- entry_points -*- [z3c.autoinclude.plugin] target = plone """, ... ) For those packages that don't have this feature, we must explicitly insert a reference to the package in the zcml parameter of the [instance] section like we did in Taking advantage of an enhanced interactive Python debugger with ipdb: [instance] ... # If you want to register ZCML slugs for any packages, # list them here. # e.g. zcml = my.package my.other.package zcml = iw.debug There's more… Do not forget to test your changes (paster changes in fact)! Fortunately, paster creates automatically a tests sub-package and a package-level README.txt file with the first part of a test (logging into our website). Feel free to take a look at it, as it is a very good example of doctest. Nevertheless, it really doesn't test too much for the time being. It will be more productive after adding some features to the product. You may find it really useful to read the content types section from the online Plone Developer Manual at http://plone.org/documentation/manual/developer-manual/archetypes. See Also Submitting products to an egg repository Taking advantage of an enhanced interactive Python debugger with ipdb Adding a content type into a product Adding fields to a content type Adding a custom validator to a content type Creating a portlet egg with paster Customizing a new portlet according to our requirements Testing portlets Adding a content type into a product In Creating an Archetypes product with paster, we were able to create a package shell with all the necessary code to install a product, although it was unproductive. We are now going to add some useful functionality by means of, again, our dear paster. Getting ready When we ran paster in Creating an Archetypes product with paster, we highlighted some of its output, copied below: The project you just created has local commands. These can be used from within the product. Paster local commands are available inside the project folder. So let's move inside it: cd ./src/pox.video How to do it… To add a new content type inside the product, run the following command: paster addcontent contenttype How it works… This will run the addcontent paster command with its contenttype template. After a short wizard asking for some options, it will produce all the code we need. Option Value Enter Video Enter contenttype_description FLV video file Enter folderish False Enter global_allow True Enter allow_discussion True/False, whatever - You'll get an output like this: ... Inserting from README.txt_insert into /pox.video/pox/video/README.txt Recursing into content Recursing into interfaces Recursing into profiles Recursing into default Recursing into types If you need more than just one content type in your product, you can run the paster addcontent contenttype command as many times as you want. There's no need to modify, buildout.cfg file, as we have already made all the required changes. If you didn't make these modifications, please refer to Creating an Archetypes product with paster. Open the interface file in ./src/pox.video/pox/video/interface/video.py: from zope import schema from zope.interface import Interface from pox.video import videoMessageFactory as _ class IVideo(Interface): """Description of the Example Type""" # -*- schema definition goes here -*- Empty interfaces, like this one, are called marker interfaces. Although they provide some information (they can be used to associate a class with some functionality as we will see in Using the ZCA to extend a third party product: Collage), they lack attributes and methods information (that is, their promised functionalities), and consequently and worse, they don't document. Interfaces don't exist in Python. However, Zope 3 has incorporated this concept to let components interact easier. All attributes and methods declarations in interfaces are a contract (not a binding one, though) with the external world. For more information about zope.interface, visit http://wiki.zope.org/Interfaces/FrontPage. The new content type class is in the video.py file located in the ./src/pox.vieo/pox/video/content package. Let's go through it and explain its pieces. """Definition of the Video content type """ from zope.interface import implements, directlyProvides from Products.Archetypes import atapi from Products.ATContentTypes.content import base from Products.ATContentTypes.content import schemata from pox.video import videoMessageFactory as _ from pox.video.interfaces import IVideo from pox.video.config import PROJECTNAME All paster-generated content types inherit from basic ATContentTypes, which is good given the large number of products available for them. Check the Products.ATContentTypes package for plenty of good working examples. VideoSchema = schemata.ATContentTypeSchema.copy() + atapi.Schema(( # -*- Your Archetypes field definitions here ... -*- )) Schemas specify the fields available in content types. In our case, the Video content type is a plain copy of ATContentTypeSchema, which already includes all fields necessary to support Dublin Core convention. Dublin Core is supported thanks to the BaseObject and ExtensibleMetadata modules in the Products.Archetypes package. VideoSchema here is the result of the addition (yes, we can actually add schemas) of two other schemas: the aforementioned ATContentTypeSchema and the new empty one created with the atapi.Schema(()) method, which expects a tuple argument (check the double brackets). Up to ZopeSkel 2.16 (paster's package) the storage of title and description fields are changed to AnnotationStorage. This reduces performance and therefore it would be better to change it by removing these lines letting Archetypes deal with regular AttributeStorage: # Set storage on fields copied from ATContentTypeSchema, # making sure # they work well with the python bridge properties. VideoSchema['title'].storage = atapi. AnnotationStorage() VideoSchema['description'].storage = atapi. AnnotationStorage() here are plans to remove this from ZopeSkel, but there's no release date yet for it. After schema definition, we call finalizeATCTSchema to re-order and move some fields inside our schema according to Plone standard. It's advisable to get familiar with its code in the Products.ATContentTypes.content.schema module: schemata.finalizeATCTSchema(VideoSchema, moveDiscussion=False) Once defined its schema the real class is created. As we said earlier, it inherits from base.ATCTContent; this will be changed for our Video content type. class Video(base.ATCTContent): """FLV video file""" implements(IVideo) meta_type = "Video" schema = VideoSchema title = atapi.ATFieldProperty('title') description = atapi.ATFieldProperty('description') # -*- Your ATSchema to Python Property Bridges Here ... -*- atapi.registerType(Video, PROJECTNAME) The first line in our class body specifies that it implements IVideo interface (interfaces/video.py file). then VideoSchema is associated with the class. ATFieldProperty is required to create ATSchema to Python Property bridges. These are recommended for fields of a schema using AnnotationStorage. If you still have title and description fields storage as AnnotationStorage, you should keep these lines. Otherwise you can safely remove them. And finally, the atapi.registerType() call adds all getters and setters to the Video class. This is Archetypes' magic. You define just a schema and Archetypes will automatically create all methods needed to interact with the class. There's more… We do have some more interesting code now, that's why we should be more careful with it and test it. Again, paster has appended several functional tests in the README.txt file, including the creation (as Manager and Contributor users), modification, and deletion of a Video object. Test the product with the following command: ./bin/instance test -s pox.video We'd like to highlight the block of statements regarding the creation of content as a contributor member: Let's logout and then login as 'contributor', a portal member that has the contributor role assigned. >>> browser.getLink('Log out').click() >>> browser.open(portal_url) >>> browser.getControl(name='__ac_name').value = 'contributor' >>> browser.getControl(name='__ac_password').value = default_password >>> browser.getControl(name='submit').click() This contributor member isn't mentioned anywhere inside the test. Nevertheless the login action doesn't fail. How can that be possible if there's no contributor member included by default in the PloneTestCase base class, like default_user or portal_owner? If we check the base.py file inside the tests sub-package of our product, we'll see that the FunctionalTestCase class has a special afterSetUp method, which is called just before the real test begins and registers the contributor member above. Could we have created the user inside the test? Definitely, because test code is a set of Python statements and we can do whatever we want with them. Is it sensible to perform this kind of set up actions inside the test code? Absolutely not. functional tests should be conceived as black-box tests, from the sheer end-user point of view. This means that code inside a functional test shouldn't assume anything about the underlying environment, but behave as if a regular user were acting through the user interface. Anything we need during testing that shouldn’t be done by the user must be placed outside the test code, as in this example. See also Creating an Archetypes product with paster Working with paster generated test suites Zope Functional testing Using the ZCA to extend a third party product: Collage Changing the base class in paster content types All paster-created (non-folderish) content types inherit from the basic ATCTContent class, which comes with ATContentTypeSchema. However, this is a very basic content type: title, description, and some more metadata fields. On top of this, we intend to upload videos to our website, not just text. ATContentTypes are native to Plone and many community developers had released extensions or plugins for them, such as LinguaPlone. That's why it is prudent to stay close to them. We are now going to change the ATCTContent parent class for ATFile to automatically inherit all the benefits, including the file upload field. How to do it… Open the video.py file inside the content sub-package of your product and make these changes. Be aware of commented lines — they can be just removed, but we wanted to keep them here to remark what's going on. Import the base class and interface to be used: from Products.Archetypes import atapi # from Products.ATContentTypes.content import base from Products.ATContentTypes.content import file from Products.ATContentTypes.interface.file import IATFile from Products.ATContentTypes.content import schemata This way we are importing interface and class of the out-of-the-box File content type. Change original schema: # VideoSchema = schemata.ATContentTypeSchema.copy() + # atapi.Schema(( VideoSchema = file.ATFileSchema.copy() + atapi.Schema(( # -*- Your Archetypes field definitions here ... -*- )) Now, our VideoSchema includes File fields. Change the base class and implemented interface: # class Video(base.ATCTContent): class Video(file.ATFile): """ pox Video """ # implements(IVideo) implements(IATFile,IVideo The last step is to change the parent class of Video so that now it inherits from ATFile instead of just ATCTContent. And then we adjust the interfaces this class now implements. Change the view action URL: Open profiles/default/types/Video.xml file and amend the url_expr attribute in View action by adding /view. <?xml version="1.0"?> <object name="Video" meta_type="Factory-based Type Information with dynamic views" i18n_domain="pox.video" action_id="view" category= "object" condition_expr="" url_expr="string:${object_url}/view" visible="True"> <permission value="View" /> </action> ... </object> Tell Plone to use the new action URL in listings: Add a propertiestool.xml file inside the profiles/default folder with this code: <?xml version="1.0"?> <object name="portal_properties" meta_type= "Plone Properties Tool"> <object name="site_properties" meta_type="Plone Property Sheet"> <property name="typesUseViewActionInListings" type= "lines" purge="False"> <element value="Video"/> </property> </object> </object> Relaunch your instance and reinstall the product. By restarting the Zope instance all of the latest changes will be applied: ./bin/instance fg Go to http://localhost:8080/plone/prefs_install_products_form and reinstall the product. Create a new Video object:Inside your Plone site, click on the Add new... drop-down menu and select the Video option. It should look like this. How it works… Since the first creation of the almost empty product (full of boilerplate, though), in Creating an Archetypes product with paster, we haven't tried to use it, except for the tests we have run. We can now say that it has grown up and it's ready to be seen in action. In Steps 1 to 3 above, we changed some of the basics in paster's original class and its schema to inherit all the benefits of another existing content type: ATFile. If you had tried to create a Video content type before Step 4, after saving, you would have been automatically prompted to download the file you just uploaded. Why's that? The reason is we inherited our class from ATFile, which has a special behavior (like ATImage) regarding its URLs. Files and images uploaded to a Plone site (using regular content types) are downloadable via their natural URL. For example, if you browse to http://yoursite.com/document.pdf, you will be asked to download the file. Alternatively, if you want to open the web page with a download link and metadata, you should use http://yoursite.com/document.pdf/view. That's why we had to change the view URL for our content type in Video.xml (Step 4) for users to be able to open an inline video player (as we plan to). All files included in the profiles folder are used by GenericSetup during installation of the product and are to give some information that Python code doesn't provide, such as whether we'll let users post comments inside the content types (allow_discussion). Plone object listings (including search results) tend to create links to contents without the /view suffix. We must explicitly tell Plone that when listing videos, the suffix should be appended to prevent a download attempt. Fortunately, Plone has foreseen this could have happened. Thus there's no need to modify or override every single list. If the content type name is listed in the special typesUseViewActionInListings property, it will work as expected. Plone object listings (including search results) tend to create links to contents without the /view suffix. We must explicitly tell Plone that when listing videos, the suffix should be appended to prevent a download attempt. Fortunately, Plone has foreseen this could have happened. Thus there's no need to modify or override every single list. If the content type name is listed in the special typesUseViewActionInListings property, it will work as expected. Changes in Step 5 will make the site_properties update its typesUseViewActionInListings property. By including purge="False" in <property /> tag, we prevent other existing values (typically File and Image) in the property from being removed.
Read more
  • 0
  • 0
  • 1873

article-image-building-image-slideshow-using-scripty2
Packt
20 May 2010
10 min read
Save for later

Building an Image Slideshow using Scripty2

Packt
20 May 2010
10 min read
In our previous Scripty2 article, we saw the basics of the Scripty2 library, we saw the UI elements available, the fx transitions and some effects. We even build an small example of a image slide effect. This article is going to be a little more complex, just a little, so we are able to build a fully working image slideshow. If you haven't read our previous article, it would be interesting and useful if you do so before continuing with this one. You can find the article here: Scripty2 in Action Well, now we can continue. But first, why don't we take a look at what we are going to build in this article: As we can see in the image we have three main elements. The biggest one will be where the main images are placed, there is also one element where we will make the slide animation to take place. There's also a caption and some descriptive text for the image and, to the right, we have the miniatures slider, clicking on them will make the main image to also slide out and the new one to appear. Seems difficult to achieve? Don't worry, we will make it step by step, so it's very easy to follow. Our main steps are going to be these: First we we will create the html markup and css necessary, so our page looks like the design. Next step will be to create the slide effect for the available images to the right. And our final step will be to make the thumbnail slider to work. Good, enough talking, let's start with the action. Creating the necessary html markup and css We will start by creating an index.html file, with some basic html in it, just like this: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xml_lang="es-ES" lang="es-ES" ><head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><title>Scripty2</title><link rel="stylesheet" href="css/reset.css" type="text/css" /><link rel="stylesheet" href="css/styles.css" type="text/css" /> </head><body id="article"> <div id="gallery"> </div> <script type="text/javascript" src="js/prototype.s2.min.js"></script> </body></html> Let's take a look at what we have here, first we are including a reset.css file, this time, for a change, we are going to use the Yahoo one, which we can find in this link: http://developer.yahoo.com/yui/3/cssreset/ We will create a folder called css, and a reset.css file inside it, where we will place the yahoo code. Note that we could also link the reset directly from the yahoo site: <link rel="stylesheet" type="text/css" href="http://yui.yahooapis.com/3.1.1/build/cssreset/reset-min.css"> Just below the link to the reset.css file, we find a styles.css file. This file will be the place where we are going to place our own styles. Next we find a div: <div id="gallery"> </div> This will be our placeholder for the entire slideshow gallery. Then one last thing, the link to the Scripty2 library: <script type="text/javascript" src="js/prototype.s2.min.js"></script> Next step will be to add some styles in our styles.css file: html, body{ background-color: #D7D7D7; }#gallery{ font-family: arial; font-size: 12px; width: 470px; height: 265px; background-color: #2D2D2D; border: 1px solid #4F4F4F; margin: 50px auto; position: relative;} Mostly, we are defining a background colour for the page, and then some styles for our gallery div like its width, height, margin and also a background colour. With all this our page will look like the following screenshot: We will need to keep working on this, first in our index.html file, we need a bit more of code: <div id="gallery"> <div id="photos_container"> <img src="./img/image_1.jpg" title="Lorem ipsum" /> <img src="./img/image_1.jpg" title="Lorem ipsum" /> <img src="./img/image_1.jpg" title="Lorem ipsum" /> <img src="./img/image_1.jpg" title="Lorem ipsum" /> </div> <div id="text_container"> <p><strong>Lorem Ipsum</strong><br/>More lorem ipsum but smaller text</p> </div> <div id="thumbnail_container"> <img src="./img/thumbnail_1.jpg" title="Lorem ipsum"/> <img src="./img/thumbnail_1.jpg" title="Lorem ipsum"/> <img src="./img/thumbnail_1.jpg" title="Lorem ipsum"/> <img src="./img/thumbnail_1.jpg" title="Lorem ipsum"/> </div></div> We have placed three more divs inside our main gallery one. One of them would be the photos-photos_container, where we place our big photos. The other two will be one for the text- text_container, and the other for the thumbnail images, that will be thumbnail_container. After we have finished with our html, we need to work with our css. Remember we are going to do it in our styles.css: #photos_container{ width: 452px; height: 247px; background-color: #fff; border: 1px solid #fff; margin-top: 8px; margin-left: 8px; overflow: hidden;}#text_container{ width: 377px; height: 55px; background-color: #000000; color: #ffffff; position: absolute; z-index: 2; bottom: 9px; left: 9px;}#text_container p{ padding: 5px; }#thumbnail_container{ width: 75px; height: 247px; background-color: #000000; position: absolute; z-index: 3; top: 9px; right: 9px;}#thumbnail_container img{ margin: 13px 13px 0px 13px; }strong{ font-weight: bold; } Here we have added styles for each one of our divs, just quite basic styling for the necessary elements, so our page now looks a bit more like the design: Though this looks a bit better than before, it still does nothing and that's going to be our next step. Creating the slide effect First we need some modifications to our html code, this time, though we could use id tags in order to identify elements, we are going to use rel tags. So we can see a different way of doing things. Let's then modify the html: <div id="photos_container"> <img src="./img/image_1.jpg" title="Lorem ipsum" rel="1"/> <img src="./img/image_1.jpg" title="Lorem ipsum" rel="2"/> <img src="./img/image_1.jpg" title="Lorem ipsum" rel="3"/> <img src="./img/image_1.jpg" title="Lorem ipsum" rel="4"/> </div> <div id="text_container"> <p><strong>Lorem Ipsum</strong><br/>More lorem ipsum but smaller text</p> </div> <div id="thumbnail_container"> <img src="./img/thumbnail_1.jpg" title="Lorem ipsum" rel="1"/> <img src="./img/thumbnail_1.jpg" title="Lorem ipsum" rel="2"/> <img src="./img/thumbnail_1.jpg" title="Lorem ipsum" rel="3"/> <img src="./img/thumbnail_1.jpg" title="Lorem ipsum" rel="4"/> </div> Note the difference, we have added rel tags to each one of the images in every one of the divs. Now we are going to add a script after the next line: <script type="text/javascript" src="js/prototype.s2.min.js"></script> The script is going to look like this: <script type="text/javascript"> $$('#thumbnail_container img').each(function(image){ image.observe('click', function(){ $$('#photos_container img[rel="'+this. readAttribute('rel')+'"]').each(function(big_image){ alert(big_image.readAttribute('title')); }) }); });</script> First we select all the images inside the #thumbnail_container div: $$('#thumbnail_container img') Then we use the each function to loop through the results of this selection, and add the click event to them: image.observe('click', function(){ This event will fire a function, that will in turn select the bigger images, the one in which the rel attribute is equal to the rel attribute of the thumbnail: $$('#photos_container img[rel="'+this.readAttribute('rel')+'"]').each(function(big_image){ We know this will return only one value, but as the selected could, theoretically, return more than one value, we need to use the each function again. Note that we use the this.readAttribute('rel') function to read the value of the rel attribute of the thumbnail image. Then we use the alert function to show the title value of the big image: alert(big_image.readAttribute('title')); If we now click on any of the thumbnails, we will get an alert, just like this: We have done this just to check if our code is working and we are able to select the image we want. Now we are going to change this alert for something more interesting. But first, we need to do some modifications to our styles.css file, we will modify our photos container styles: #photos_container{ width: 452px; height: 247px; background-color: #fff; border: 1px solid #fff; margin-top: 8px; margin-left: 8px; overflow: hidden; position: relative;} Only to add the position relate in it, this way we will be able to absolute position the images inside it, adding these styles: #photos_container img{ position: absolute;} With these changes we are done with the styles for now, return to the index.html file, we are going to introduce some modifications here too: <script type="text/javascript" src="js/prototype.s2.min.js"></script> <script type="text/javascript"> var current_image = 1; $$('#photos_container img').each(function(image){ var pos_y = (image.readAttribute('rel') * 247) - 247; image.setStyle({top: pos_y+'px'}); })... I've placed the modifications in bold, so we can concentrate on them. What have we here? We are creating a new variable, current_image, so we can save which is the current active image. At page load this will be the first one, so we are placing 1 to its value. Next we loop through all of the big images, reading its rel attribute, to know which one of the images are we working with. We are multiplying this rel attribute with 247, which is the height of our div. We are also subtracting 247 so the first image is at 0 position. note Just after this, we are using prototype's setStyle function to give each image its proper top value. Now, I'm going to comment one of the css styles: #photos_container{ width: 452px; height: 247px; background-color: #fff; border: 1px solid #fff; margin-top: 8px; margin-left: 8px; //overflow: hidden; position: relative;} You don't need to do this, I' will do it for you, so we can see the result of our previous coding, and then I will turn back and leave it as it was before, prior to continuing. So our page would look like the next image: As we see in the image, all images are one on top of the others, this way we will be able to move them in the y axis. We are going to achieve that by modifying this code: $$('#thumbnail_container img').each(function(image){ image.observe('click', function(){ $$('#photos_container img[rel="'+this. readAttribute('rel')+'"]').each(function(big_image){ alert(big_image.readAttribute('title')); }) }); });
Read more
  • 0
  • 0
  • 10438

article-image-using-flowplayer-plone-3
Packt
19 May 2010
5 min read
Save for later

Using Flowplayer in Plone 3

Packt
19 May 2010
5 min read
This article is the third and the final section of this article series. The following articles are the initial parts of the series. Managing Audio Content in Plone 3.3 Audio Enhancements with p4a.ploneaudio in Plone 3.3 (For more resources on Plone, see here.) Including audio into HTML Generally, we have two options to include a sound file on a HTML page: Streaming Non streaming Another option is to simply include a link to the file like this: <a href="example.mp3" type="audio/x-mpeg" title="MP3 audiofile , ... kB">example.mp3 </a> This is the standard way Plone includes files into the visual editor. The advantage of this approach is that virtually everyone is able to access the file in some way. What happens with the file after it has been downloaded depends on the client browser and how it is configured. The shortcoming of this method is that we depend on an external player to listen to the audio. Probably one needs to download the file and start the player manually. Including audio with plugin elements Another option to spread an audio file is to use the embed element or the object element. The former looks like this: <embed src="example.mp3"> The embed element was introduced by Netscape in browser version 2.0. However, it has not yet made it into the HTML standard, and probably will never do so. An alternative element to the Netscape variant is the object element that was introduced by Microsoft. Including an example.mp3 file located in the data folder looks like this: <object type="audio/x-mpeg" data="data/example.mp3" width="200" height="20"> <param name="src" value="data/example.mp3"> <param name="autoplay" value="false"> <param name="autoStart" value="0"> alt : <a href="data/example.mp3">example.mp3</a> </object> Including audio with the embed or the object element assumes that there is a plugin installed on the client side that can play the multimedia format. In most cases, we can't tell what the client is equipped with and want a more robust solution. The third way to include audio into your site is Flash. We still need a plugin on the client side, but Flash is more widespread than audio player plugins. There are a couple of free audio players written in Flash. An older but easy-to-use Flash player is EMFF. A custom view with an embedded audio player What we do now is to write a custom view for the audio-enhanced File content type of Plone. We reuse the mm.enhance product we created in the previous article and add the additional code there. We utilize the p4a.audio.interfaces.IAudioEnhanced interface to register our view on. Let's do so: <browser:page for="p4a.audio.interfaces.IAudioEnhanced" name="my-audioplayer-view" class=".browser.AudioPlayerView" permission="zope2.View" template="player.pt" /> <browser:resourceDirectory directory="thirdparty/emff" name="emff" /> The page is named my-audioplayer-view and has the AudioPlayerView view class in the browser module. Further, we register a thirdparty/emff directory where we can put the Flash resources of the Flash player. Next, we need to create this view class and add the player.pt template. We fill the template with the HTML code we get from the EMFF code generator: Using the EMFF code generator Choose the first option URL to MP3, though it doesn't really matter what you write into the text field. The value is overridden with the name we retrieve from our context object. For the HTML version, you can either choose HTML or XHTML as Plone 3 doesn't output valid XHTML itself. Nevertheless, XHTML might still be the better option, as it is future proof. Selecting XHTML closes the param elements inside of the object element. We can copy this literally into our Zope page template. <object type="application/x-shockwave-flash" data="emff_lila_info.swf" width="200" height="55"> <param name="movie" value="emff_lila_info.swf" /> <param name="bgcolor" value="#00ff00" /> <param name="FlashVars" value="src=example.mp3&amp;autostart=yes" /> </object> The Plone template is a standard one using the main_template. We copy the generated code from the bottom of the codegenerator window and put into the main slot of the template. There are just two changes we make. One is the location of the Flash file, which is registered as a resource, and the other is the audio file itself, which is our context. <html xml_lang="en" lang="en" i18n:domain="p4a.audio" metal:use-macro="context/main_template/macros/master"> <body> <div metal:fill-slot="main"> <object type="application/x-shockwave-flash" data=" ++resource++emff/emff_lila_info.swf" width="200" height="55"> <param name="movie" value="emff_lila_info.swf" /> <param name="bgcolor" value="#ffffff" /> <param name="FlashVars" value="src=example.mp3&amp;autostart=yes" tal:attributes="value string_src=${context/getId}&amp; autostart=yes" /> </object> </div> </body> </html> Next, we download the necessary Flash code from its website. The player is provided as a ZIP package with all sources and stuff included. We need to copy the chosen skin file to the thirdparty/emff directory of our mm.enhance product. In our example we used the emff_lila_info skin.
Read more
  • 0
  • 0
  • 1818

article-image-django-12-e-commerce-generating-pdf-reports-python-using-reportlab
Packt
19 May 2010
6 min read
Save for later

Django 1.2 E-commerce: Generating PDF Reports from Python using ReportLab

Packt
19 May 2010
6 min read
(Read more interesting articles on Django 1.2 e-commerce here.) ReportLab is an open source project available at http://www.reportlab.org. It is a very mature tool and includes binaries for several platforms as well as source code. It also contains extension code written in C, so it's relatively fast. It is possible for ReportLab to insert PNG and GIF image files into PDF output, but in order to do so we must have the Python Imaging Library (PIL) installed. We will not require this functionality in this article, but if you need it for a future project, see the PIL documentation for setup instructions. The starting point in the ReportLab API is drawing to canvas objects. Canvas objects are exactly what they sound like: blank slates that are accessible using various drawing commands that paint graphics, images, and words. This is sometimes referred to as a low-level drawing interface because generating output is often tedious. If we were creating anything beyond basic reporting output, we would likely want to build our own framework or set of routines on top of these low-level drawing functions. Drawing to a canvas object is a lot like working with the old LOGO programming language. It has a cursor that we can move around and draw points from one position to another. Mostly, these drawing functions work with two-dimensional (x, y) coordinates to specify starting and ending positions. This two-dimensional coordinate system in ReportLab is different from the typical system used in many graphics applications. It is the standard Cartesian plane, whose origin (x and y coordinates both equal to 0) begins in the lower-left hand corner instead of the typical upper-right hand corner. This coordinate system is used in most mathematics courses, but computer graphics tools, including HTML and CSS layouts, typically use a different coordinate system, where the origin is in the upper-left. ReportLab's low-level interface also includes functions to render text to a canvas. This includes support for different fonts and colors. The text routines we will see, however, may surprise you with their relative crudeness. For example, word-wrapping and other typesetting operations are not automatically implemented. ReportLab includes a more advanced set of routines called PLATYPUS, which can handle page layout and typography. Most low-level drawing tools do not include this functionality by default (hence the name "low-level"). This low-level drawing interface is called pdfgen and is located in the reportlab.pdfgen module. The ReportLab User's Guide includes extensive information about its use and a separate API reference is also available. The ReportLab canvas object is designed to work directly on files. We can create a new canvas from an existing open file object or by simply passing in a file name. The canvas constructor takes as its first argument the filename or an open file object. For example: from reportlab.pdfgen import canvasc = canvas.Canvas("myreport.pdf") Once we obtained a canvas object, we can access the drawing routines as methods on the instance. To draw some text, we can call the drawString method: c.drawString(250, 250, "Ecommerce in Django") This command moves the cursor to coordinates (250, 250) and draws the string "Ecommerce in Django". In addition to drawing strings, the canvas object includes methods to create rectangles, lines, circles, and other shapes. Because PDF was originally designed for printed output, consideration needs to be made for page size. Page size refers to the size of the PDF document if it were to be output to paper. By default, ReportLab uses the A4 standard, but it supports most popular page sizes, including letter, the typical size used in the US. Various page sizes are defined in reportlab.lib.pagesizes. To change this setting for our canvas object, we pass in the pagesize keyword argument to the canvas constructor. from reportlab.lib.pagesizes import letterc = canvas.Canvas('myreport.pdf', pagesize=letter) Because the units passed to our drawing functions, like rect, will vary according to what page size we're using, we can use ReportLab's units module to precisely control the output of our drawing methods. Units are stored in reportlab.lib.units. We can use the inch unit to draw shapes of a specific size: from reportlab.lib.units import inchc.rect(1*inch, 1*inch, 0.5*inch, 1*inch) The above code fragment draws a rectangle, starting one inch from the bottom and one inch from the left of the page, with sides that are length 0.5 inches and one inch, as shown in the following screenshot: Not particularly impressive is it? As you can see, using the low-level library routines require a lot of work to generate very little results. Using these routines directly is tedious. They are certainly useful and required for some tasks. They can also act as building blocks for your own, more sophisticated routines. Building our own library of routines would still be a lot of work. Fortunately ReportLab includes a built-in high-level interface for creating sophisticated report documents quickly. These routines are called PLATYPUS; we mentioned them earlier when talking about typesetting text, but they can do much more. PLATYPUS is an acronym for "Page Layout and Typography Using Scripts". The PLATYPUS code is located in the reportlab.platypus module. It allows us to create some very sophisticated documents suitable for reporting systems in an e-commerce application. Using PLATYPUS means we don't have to worry about things such as page margins, font sizes, and word-wrapping. The bulk of this heavy lifting is taken care of by the high-level routines. We can, if we wish, access the low-level canvas routines. Instead we can build a document from a template object, defining and adding the elements (such as paragraphs, tables, spacers, and images) to the document container. The following example generates a PDF report listing all the products in our Product inventory: from reportlab.platypus.doctemplate import SimpleDocTemplatefrom reportlab.platypus import Paragraph, Spacerfrom reportlab.lib import sytlesdoc = SimpleDocTemplate("products.pdf")Catalog = []header = Paragraph("Product Inventory", styles['Heading1']) Catalog.append(header)style = styles['Normal']for product in Product.objects.all(): for product in Product.objects.all(): p = Paragraph("%s" % product.name, style) Catalog.append(p) s = Spacer(1, 0.25*inch) Catalog.append(s)doc.build(Catalog) The previous code generates a PDF file called products.pdf that contains a header and a list of our product inventory. The output is displayed in the accompanying screenshot.
Read more
  • 0
  • 0
  • 26836

article-image-django-12-e-commerce-data-integration
Packt
19 May 2010
7 min read
Save for later

Django 1.2 E-commerce: Data Integration

Packt
19 May 2010
7 min read
(Read more interesting articles on Django 1.2 e-commerce here.) We will be using a variety of tools, many builtin to Django. These are all relatively stable and mature, but as with all open source technology, new versions could change their usage at any time. Exposing data and APIs One of the biggest elements of the web applications developed in the last decade has been the adoption of so-called Web 2.0 features. These come in a variety of flavors, but one thing that has been persistent amongst them all is a data-centric view of the world. Modern web applications work with data, usually stored in a database, in ways that are more modular and flexible than ever before. As a result, many web-based companies are choosing to share parts of their data with the world in hopes of generating "buzz", or so that interested developers might create a clever "mash-up" (a combination of third-party application software with data exposed via an API or other source). These mash-ups take a variety of forms. Some simply allow external data to be integrated or imported into a desktop or web-based application. For example, loading Amazon's vast product catalog into a niche website on movie reviews. Others actually deploy software written in web-based languages into their own application. This software is usually provided by the service that is exposing their data in the form of a code library or web-accessible API. Larger web services that want to provide users with programmatic access to their data will produce code libraries written in one or more of the popular web-development languages. Increasingly, this includes Python, though not always, and typically also includes PHP, Java, or Perl. Often when an official data library exists in another language, an enterprising developer has ported the code to Python. Increasingly, however, full-on code libraries are eschewed in favor of open, standards-based, web-accessible APIs. These came into existence on the Web in the form of remote procedure call tools. These mapped functions in a local application written in a programming language that supports XML-RPC to functions on a server that exposed a specific, well-documented interface. XML and network transport protocols were used "under the hood" to make the connection and "call" the function. Other similar technologies also achieved a lot of use. For example, many web-services provide Simple Object Access Protocol (SOAP) interface, which is the successor to XML-RPC and built on a very similar foundation. Other standards, sometimes with proprietary implementations, also exist, but many new web-services are now building APIs using REST-style architecture. REST stands for Representational State Transfer and is a lightweight and open technique for transmitting data across the Web in both server-to-server and client-to-server situations. It has become extremely popular in the Web 2.0 and open source world due to its ease of use and its reliance on standard web protocols such as HTTP, though it is not limited to any one particular protocol. A full discussion of REST web services is beyond the scope of this article. Despite their simplicity, there can arise many complicated technical details. Our implementation in this article will focus on a very straightforward, yet powerful design. REST focuses on defining our data as a resource that when used with HTTP can map to a URL. Access to data in this scheme is simply a matter of specifying a URL and, if supported, any look-up, filter, or other operational parameters. A fully featured REST web service that uses the HTTP protocol will attempt to define as many operations as possible using the basic HTTP access methods. These include the usual GET and POST methods, but also PUT and DELETE, which can be used for replacement, updating, or deletion of resources. There is no standard implementation of a REST-based web service and as such the design and use can vary widely from application to application. Still, REST is lightweight enough and relies on a well known set of basic architectures that a developer can learn a new REST-based web service in a very short period of time. This gives it a degree of advantage over competing SOAP or XML-RPC web services. Of course, there are many people who would dispute this claim. For our purposes, however, REST will work very well and we will begin by implementing a REST-based view of our data using Django. Writing our own REST service in Django would be very straightforward, partly because URL mapping schemes are very easy to design in the urls.py file. A very quick and dirty data API could be created using the following super-simple URL patterns: (r'^api/(?P<obj_model>w*)/$', 'project.views.api')(r'^api/(?P<obj_model>w*)/(?P<id>d*)/$', 'project.views.api') And this view: from django.core import serializersdef api(request, obj_model, obj_id=None): model = get_model(obj_model.split(".")) if model is None: raise Http404 if obj_id is not None: results = model.objects.get(id=obj_id) else: results = model.objects.all()json_data = serializers.serialize('json', results)return HttpResponse(json_data, mimetype='application/json')) This approach as it is written above is not recommended, but it shows an example of one of the simplest possible data APIs. The API view returns the full set of model objects requested in JSON form. JSON is a simple, lightweight data format that resembles JavaScript syntax. It is quickly becoming the preferred method of data transfer for web applications. To request a list of all products, for example, we only need to access the following URL path on our site: /api/products.Product/. This uses Django's app.model syntax to refer to the model we want to retrieve. The view uses get_model to obtain a reference to the Product model and then we can work with it as needed. A specific model can be retrieved by including an object ID in the URL path: /api/products.Product/123/ would retrieve the Product whose ID is 123. After obtaining the results data, it must be encoded to JSON format. Django provides serializers for several data formats, including JSON. These are all located in the django.code.serializers module. In our case, we simply pass the results QuerySet to the serialize function, which returns our JSON data. We can limit the fields to be serialized by including a field's keyword argument in the call to serialize: json_data = serializers.serialize('json', results, fields=('name','price')) We can also use the built-in serializers to generate XML. We could modify the above view to include a format flag to allow the generation of JSON or XML: def api(request, obj_model, obj_id=None, format='json'): model = get_model(*obj_model.split()) If model is None: raise Http404 if obj_id is not None: results = model.objects.get(id=obj_id) else: results = model.objects.all()serialized_data = serializers.serialize(format, results)return HttpResponse(serialized_data, mimetype='application/' + format) Format could be passed directly on the URL or better yet, we could define two distinct URL patterns and use Django's keyword dictionary: (r'^api/(?P<obj_model>w*)/$', 'project.views.api'),(r'^api/(?P<obj_model>w*)/xml/$', 'project.views.api',{'format': 'xml'}),(r'^api/(?P<obj_model>w*)/yaml/$', 'project.views.api',{'format': 'yaml'}),(r'^api/(?P<obj_model>w*)/python/$', 'project.views.api',{'format': 'python'}), By default our serializer will generate JSON data, but we've got to provide alternative API URLs that support XML, YAML, and Python formats. These are the four built-in formats supported by Django's serializers module. Note that Django's support for YAML as a serialization format requires installation of the third-party PyYAML module. Building our own API is in some ways both easy and difficult. Clearly we have a good start with the above code, but there are many problems. For example, this is exposing all of our Django model information to the world, including our User objects. This is why we do not recommend this approach. The views could be password protected or require a login (which would make programmatic access from code more difficult) or we could look for another solution.
Read more
  • 0
  • 0
  • 3979
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-audio-enhancements-p4aploneaudio-plone-33
Packt
17 May 2010
14 min read
Save for later

Audio Enhancements with p4a.ploneaudio in Plone 3.3

Packt
17 May 2010
14 min read
(For more resources on Plone, see here.) Audio enhancements with p4a.ploneaudio One of the most advanced products to boost the audio features of Plone is p4a.ploneaudio. Like its image sister p4a.ploneimage it doesn't bring a content type on its own, but expands existing ones. As you might have guessed already, the File content type and the folderish ones (Folder, Large Folder, and Collection) are chosen for the enhancement. To install it, add the following code to the buildout of our instance: [buildout]...[instance]...eggs =${buildout:eggs}Plonep4a.ploneaudiozcml =p4a.ploneaudio After running the buildout and restarting the instance, we need to install the product as an add-on product. We find it with the product name Plone4Artists Audio (p4a.ploneaudio). Enhancing files Installing the p4a.ploneaudio product enables the enhancement of the File content type of ATContentTypes. Unlike with the image enhancement, not all files are automatically enhanced with additional audio features. p4a.ploneaudio comes with a MIME type filter. If we upload a file with one of the meta types such as audio/MPEG or application/Ogg, the file will automatically be audio enhanced. All other files (such as ZIP files, EXE files, and so on) stay untouched. Technically speaking, this works via a named adapter. The first thing we see is the modified default view for audio-enhanced files: On the right side we see some technical information about the audio file itself. We find the size, the format (MP3 or Ogg), the bitrate, the frequency, and the length of the track there. As with any other content type, we have the title and the description at the top of the content area. For the audio-enhanced content, the description has changed from a simple text field to a rich text field. We find four buttons after the usual CMS bar (the belowcontenttitle slot) containing the author and the modification date of the file. Each of these buttons retrieves the audio file in a different way: Button Action Description     Play An embedded audio player written in Flash. Clicking on it starts playback immediately.     Pop up Opens a pop-up window with a Flash player playing the audio track. This is useful if someone wants to play the track while continuing to browse the site.     Stream Clicking on the button returns an M3U playlist with the URL of the file. The browser/operating system needs to take care of the handling of the stream. If you have a Mac, the file will be streamed to iTunes.     Download This is the standard behavior of Plone’s File content-type. The file is returned to the client like any other file object. It can be saved or opened with the favorite media player of the operating system.   We find a long list of additional information on the audio track below the player and streaming buttons. This information is the metadata stored with the audio file and is gathered during the enhancing process. It can be changed and written back to the file. Let's take a closer look on the enhancement process. One important step here is marking the content object with two interfaces: One is p4a.subtyper.interfaces.ISubtyped. This interface marks the content as subtyped. The other interface is p4a.audio.interfaces.IAudioEnhanced. This interface describes the type of enhancement, which is audio content in this case. All other views and components available with the enhanced version bind to the p4a.audio.interfaces.IAudioEnhanced interface. Additionally, p4a.ploneaudio tries to extract as much of the metadata information from the file as possible. The title is retrieved from the metadata and changed. Let's see what happens when a file is added in detail: It is possible to write custom audio enhancers if needed. The enhancers are queried as named adapters by the MIME type on the IAudioDataAccessor interface. Enhancing containers With p4a.ploneaudio, we can turn any Plone folder into an audio-enhanced folder by choosing Audio container from the Subtype menu. Two marker interfaces are attached to the container: p4a.subtyper.interfaces.ISubtyped p4a.audio.interfaces.IAudioContainerEnhanced The default view of the container is changed utilizing the IAudioContainerEnhanced interface. The new default view is audio-container.html, which comes with the p4a.ploneaudio product. In the View option we see one box for each track. For each track, the title and the cover artwork is shown. There is also an embedded Flash player to play each track directly. There are some other container views that come with the product as follows: audio-album.html (album view) audiocd-popup.html (pop-up view) cd_playlist.xspf And the default edit view for IAudioContainerEnhaced-marked folders is overridden. The audio-album.html view presents the audio content of the container in a slightly different way. The single tracks are arranged in a table. Two variants of this view are exposed in the Display menu of the container—Album view and Album view with no track numbers. Both these views are more compact than the default audio container view and have the same layout, except that the second one omits the column with the track numbers. As for the other audio container views, there is a player for each track. Moreover, there are two buttons on the top of the page. One is the commonly used RSS icon and the other one is the familiar pop-up player button . By clicking on the RSS icon, we get to the stream (or podcast of the folder). This may not be too interesting for a standard static Folder, but can be for a Collection where the content changes more dynamically. The pop-up player for a folder looks and operates slightly differently as the file standalone variant. It contains all tracks that are part of the audio container as a playlist. Lastly, there is the cd_playlist.xspf view. This view is not exposed via a display or as an action. It provides the audio data as a XSPF stream. The XML Shareable Playlist Format: XSPF The XML Shareable Playlist Format (XSPF) is an XML file format for storing playlists of digital audio. The audio can be located locally or online. Last.fm uses XSPF for providing its playlists. An example playlist looks like this: <?xml version="1.0" encoding="UTF-8"?><playlist version="1" ><trackList><track><location>example.mp3</location><creator>Tom</creator><album>Thriller</album><annotation>A comment</annotation></track><track><location>another.mp3</location><creator>Tom</creator></track></trackList></playlist> There is a list of applications supporting the XSPF format on the XSPF (http://xspf.org/) website. The default XSPF view of p4a.ploneaudio does not include all possible information. It provides the following attributes: location: This is the required location (URL) of the song. image: The album artwork that may be included with each track. annotation: p4a.ploneaudio collects information about the title, artist, album, and year in this field. It is easy to write a custom implementation of an XSPF view. Look at the available fields at the XSPF home page and the XSPF implementation of p4a.audio. We find the template named cd_playlist.xspf.pt in the p4a.audio.browser module. p4a.ploneaudio and the Plone catalog Besides the customized default views and the additional views, p4a.ploneaudio comes with a number of other changes. One very important change is the disclosure of some audio metadata fields to the portal catalog. This allows us to use them in catalog queries in general and smart folders in particular. The following indexes are added: Artist Genre Track Format The Artist attribute is exposed as metadata information in the catalog. Also, the genre and the artist name are added to the full text index SearchableText. The values for the genre field are hardcoded. The ID3 tag genre list is used together with the Winamp extensions. They are stored as a vocabulary. The term titles are resolved for Searchable Text, but not for the index and the Collection field. Accessing audio metadata in Collections To access catalog information in collections, it needs to be exposed to the collections tool. This can either be done by a product or TTW in the Plone configuration panel. p4a.ploneaudio comes with a modification of the fields: Artist (Artist name) Genre (Genre) Format (MIME Types) Let's say we want a collection of all our MP3 files. All we have to do is add a MIME Types criterion to our collection and set audio/mpeg as the value: ATAudio migration If you have an older Plone site (2.5), you probably have ATAudio installed to deal with audio content.ATAudio has features similar to p4a.ploneaudio. This is not surprising as p4a.ploneaudio was derived from ATAudio. The main difference is that ATAudio provides a content type, while p4a.ploneaudio reuses an existing one. If you want to switch from ATAudio to p4a.ploneaudio for one or the other reason, p4a.ploneaudio comes with a migration routine. One reason for switching might be that ATAudio is not actively developed any more and probably doesn't work with recent versions of Plone. For migrating, you need to call migrate-ataudioconfiglet.html and follow the instructions there. The migration view is available only if ATAudio is installed. There is a little bit of a catch-22 situation because p4a.ploneaudio doesn't run on Plone 2.5 and ATAudio doesn't run on Plone 3. This means there is no good starting point for the migration. At least, there is a version of ATAudio that does install in Plone 3 in the collective repository: http://svn.plone.org/svn/collective/ATAudio/tags/0.7- plone3migration/. Extracting metadata with AudioDataAccessors The IAudioDataAccessor interface is used for extracting metadata from binary audio content. If uploading a file, Plone tries to acquire a named adapter for the interface with the MIME type as the key. It has the following layout: class IAudioDataAccessor(interface.Interface):"""Audio implementation accessor (ie MP3, ogg, etc)."""audio_type = schema.TextLine(title=_(u'Audio Type'),required=True,readonly=True)def load(filename):"""Load from filename"""def store(filename):"""Store to filename""" The audio_type field contains a human-readable description of the audio type. In the case of MP3, the Unicode string "MPEG-1 Audio Layer 3" is used. The load and store methods are used for reading/writing the metadata from/to the audio file. The definition for the MP3 adapter looks like this: <adapterfor="p4a.audio.interfaces.IPossibleAudio"factory="._audiodata.MP3AudioDataAccessor"provides="p4a.audio.interfaces.IAudioDataAccessor"name="audio/mpeg"/> The component is registered for the p4a.audio.interfaces.IPossibleAudio interface. All classes marked with this interface are capable of getting converted to an audio-enhanced object. The standard product marks the File content type with this interface. The adapter provides the p4a.audio.interfaces.IAudioDataAccessor interface, which is the marker for the lookup. The name attribute of adapter is the key for the lookup and needs to be set to the MIME type of the audio file that should be processed. Now, the factory does the actual work of loading and storing the metadata from the audio file. p4a.ploneaudio and FLAC For the moment, p4a.ploneaudio only supports MP3 and Ogg Vorbis. This is a reasonable choice. Both formats are streamable and were made for good audio quality with small file sizes. We want to add FLAC support. We use mutagen for metadata extraction. Mutagen is an audio metadata extractor written in Python. It is capable of reading and writing many audio metadata formats including: FLAC M4A Monkey's Audio MP3 Musepack Ogg Vorbis True Audio WavPack OptimFROG Let's remember the flow chart of the audio adding process. We recall that we need a metadata extractor for our FLAC MIME type. First, we need to register a named adapter for this purpose. This is very similar to the MP3 adapter we saw before: <adapterfor="p4a.audio.interfaces.IPossibleAudio"factory=".flac._audiodata.FlacAudioDataAccessor"provides="p4a.audio.interfaces.IAudioDataAccessor"name="application/x-flac"/> The adapter is used for objects implementing the p4a.audio.interfaces.IPossibleAudio interface. The factory does the work of extracting the metadata. The adapter provides the p4a.audio.interfaces.IAudioDataAccessor interface. This is what the adapter is made for and the name is application/x-flac, which is the MIME type of FLAC audio files. Next, we define the metadata accessor: from mutagen.flac import Open as openaudio...from p4a.audio.ogg._audiodata import _safe First, we need some third-party imports. For the metadata extraction, we use the FLAC accessor of the mutagen library. _safe is a helper method. It returns the first element if the given parameter is a list or a tuple, or the element itself. class FlacAudioDataAccessor(object):"""An AudioDataAccessor for FLAC"""implements(IAudioDataAccessor)def __init__(self, context):self._filecontent = context The first lines are the boilerplate part. The adapter class implements the interface the adapter provides. In the constructor, we get the context of the adapter and save it in the _filecontent variable of the instance. @propertydef audio_type(self):return 'FLAC'@propertydef _audio(self):return IAudio(self._filecontent)@propertydef _audio_data(self):annotations = IAnnotations(self._filecontent)return annotations.get(self._audio.ANNO_KEY, None) The audio_type property is just for information purposes and required by the interface. It is displayed in the audio view. The _audio property is a shortcut for accessing the IAudio adapter for the context. The audio_data property is a shortcut for accessing the metadata annotated to the context. def load(self, filename):flacfile = openaudio(filename)self._audio_data['title'] = _safe(flacfile.get('title', ''))self._audio_data['artist'] = _safe(flacfile.get('artist', ''))self._audio_data['album'] = _safe(flacfile.get('album', ''))self._audio_data['year'] = _safe(flacfile.get('date', ''))self._audio_data['idtrack'] = _safe(flacfile.get('tracknumber', ''))self._audio_data['genre'] = _safe(flacfile.get('genre', ''))self._audio_data['comment'] = _safe(flacfile.get('description', ''))self._audio_data['bit_rate'] = long(flacfile.info.bits_per_sample)self._audio_data['length'] = long(flacfile.info.length)self._audio_data['frequency'] = long(flacfile.info.sample_rate) The load method is required by the IAudioDataAccessor interface. It fetches the metadata using the mutagen method from the audio file and stores it as an annotation on the context. def store(self, filename):flacfile = openaudio(filename)flacfile['title'] = self._audio.title or u''flacfile['artist'] = self._audio.artist or u''flacfile['album'] = self._audio.album or u''flacfile['date'] = self._audio.year or u''flacfile['tracknumber'] = self._audio.idtrack or u''flacfile.save() The store method is required by the IAudioDataAccessor interface as well and its purpose is to write the metadata from the context annotation back to the audio file. We have covered the following in this article series: Manipulation of audio content stored as File content in Plone The different formats used for the binary storage of audio data Storing and accessing MP3 audio metadata with the ID3 tag format Managing metadata, formats, and playlists with p4a.ploneaudio in Plone Including a custom embedded audio player in Plone Using the Flowplayer product to include an audio player standalone in rich text and as a portlet >> Continue Reading: Using Flowplayer in Plone 3. Further resources on this subject: Plone 3 Multimedia [book] Using Flowplayer in Plone 3 [article] Audio Enhancements with p4a.ploneaudio in Plone 3.3 [article] Red5: A video-on-demand Flash Server [article]   Plone 3.3 Products Development Cookbook [book] Creating a custom type with paster in plone 3 [article] Improving Plone 3 Product Performance [article]   Plone 3 for Education [book] Calendaring with Plone 3 for Education [article] Showcasing Personnel with Faculty/Staff Directory using Plone 3 [article] Plone 3 Themes [article] Blogs and forums using Plone 3 [article]   Plone 3 Theming [book] Creating, Installing and Tweaking your theme using Plone 3 [article] Skinner's Toolkit for Plone 3 Theming (Part 1) [article] Skinner's Toolkit for Plone 3 Theming (Part 2) [article] Add-on Tools and Theming Tips for Plone 3 [article]   Practical Plone 3: A Beginner's Guide to Building Powerful Websites [book] Find and Install Add-Ons that Expand Plone Functionality [article] Structure the Content on your Plone Site [article] Safely manage different vesions of content with plone [article] Creating New Types of Plone Portlets [article] Show Additional Information to Users and Visitors of Your Plone Site [article]
Read more
  • 0
  • 0
  • 1635

article-image-managing-audio-content-plone-33
Packt
17 May 2010
10 min read
Save for later

Managing Audio Content in Plone 3.3

Packt
17 May 2010
10 min read
(For more resources on Plone, see here.) There are at least four use cases when we think of integrating audio in a web application: We want to provide an audio database with static files for download. We have audio that we want to have streamed to the Internet (for example, as a podcast). We want a audio file/live show streamed to the Internet as an Internet radio service. We want some sound to be played when the site is loaded or shown. In this article series, we will discuss three of the four cases. The streaming support is limited to use case 2. We can stream to one client like a podcast does, but not to many clients at once like an Internet Radio does. We need special software such as Icecast or SHOUTcast for this purpose. Further, we will investigate how we solve use cases 1, 2, and 3 with the Plone CMS and extensions. Technically, these are the topics covered in this article series: Manipulation of audio content stored as File content in Plone The different formats used for the binary storage of audio data Storing and accessing MP3 audio metadata with the ID3 tag format Managing metadata, formats, and playlists with p4a.ploneaudio in Plone Including a custom embedded audio player in Plone Using the Flowplayer product to include an audio player standalone in rich text and as a portlet Previewing the audio element of HTML5 Extracting metadata from a FLAC file using mutagen   Uploading audio files with an unmodified Plone installation The out of the box support of Plone for audio content is limited. What is possible to do is to upload an audio file utilizing the File content type of Plone to the ZODB. A File is nothing more and nothing less than a simple binary file. Plone does not make any difference between a MP3 file and a ZIP, an EXE, or an RPM binary file. When adding File content to Plone, we need to upload a file (of course!). We don't necessarily need to specify a title, as the filename is used if the title is omitted. The filename is always taken for the short name (ID) of the object. This limits the number of files with any specific name to one in a container While uploading a file, Plone tries to recognize the MIME type and the size of the file. This is the smallest subset of information shared by all binary files the content type File was intended for. Normally, detecting the MIME type for standard audio is not a problem if the file extension is correctly set. Clicking on the link in the default view either downloads the file or opens it with the favorite player of your operating system. This behavior depends on the settings made on the target browser and corresponds with the method 1 of our audio use cases. It goes without saying that we can add the default metadata to files and organize them in folders. Like Images, File objects do not have a workflow associated in a default Plone setup. They inherit the read and write permissions from the container they are placed into. Still, we can add an existing workflow to this content type or create a new one via the portal_workflow tool if we want. That's pretty much it. Fortunately, we can utilize some extensions to enhance the Plone audio story greatly. What we will see in this article is as follows: First, we will go over some theoretical ground. We will see what formats are available for storing audio content and which is best for which purpose. Later we will investigate the Plone4Artists extension for Plone's File content type—p4a.ploneaudio. We will talk about metadata especially used for audio content and how to manipulate it. As a concrete example, we will use mutagen to extract metadata from a FLAC file to add FLAC support to p4a.ploneaudio. Finally, we will have a word on streaming audio data through the Web and see how to embed a Flash player into our Plone site. We will see how we can do this programmatically and also with the help of a third-party product called collective.flowplayer. At the very end of the article, we have a small technical preview on HTML5 where a dedicated audio element is available. This element allows us to embed audio directly into our HTML page without the detour with Flash. Accessing audio content in Plone Once we upload a file we want to work with to Plone, we will link it with other content and display it in one way or another. There are several ways of accessing audio data in Plone. It can be accessed in the visual editor by editors, in templates by integrators and in Python code by developers. Kupu access Unlike for images, there is no special option in the visual editor to embed file/audio content into a page. The only way to access an audio file with Kupu is to use an internal link. The file displays as a normal link and is executed when clicked. Executed means (as for the standalone file) saved or opened with the music player of your operating system as is done in the standard view of the File content type. Of course, it is possible to reference external audio files as well. Page template access As there is no special access method in Kupu, there is none in page templates. If we need to access a file there, we can use the absolute_url method of the audio content object. This computes a link we can refer to. So the only way to access a file from another context is to refer to its URL. <a tal_attributes="href audiocontext/absolute_url"tal:content="audiocontext/Title">audio</a> Python script access If we need to access the content of an (audio) file in a Python script, we can get the binary data with the Archetype accessor getFile. >>> binary = context.getFile() This method returns the data wrapped into a Zope OFS.File object. To access the raw data as a string, we need to do the following: >>> rawdata = str(binary.data) Accessing the raw data of an audio file might be useful if we want to do format transformations on the fly or other direct manipulation of the data. Field access If we write our own content type and want to save audio data with an object, we need a file field. This field stores the binary data and takes care of communicating with the browser with adequate view and edit widgets. The file field is defined in the Field module of the Archetype product. Additional to the properties, it exclusively defines that it inherits from the ObjectField base class. The following properties are important. Key Default value type 'file' default ' ' primary False widget FileWidget content_class File default_content_type 'application/octet-stream'   The type property provides a unique name for the field. We usually don't need to change this. The default property defines the default value for this field. It is normally empty. If we want to change it, we need to specify an instance of the content_class property. One field of the schema can be marked as primary. This field can be retrieved by the getPrimaryField accessor. When accessing the content object with FTP, the content of the primary field is transmitted to the client. Like every other field, the file field needs a widget. The standard FileWidget is defined in the Widget module of the Archetypes product. The content_class property declares the instance, where the actual binary data is stored. As standard, the File class from Zope's OFS.Image module is used. This class supports chunk-wise transmission of the data with the publisher. Field can be accessed like any other field by its accessor method. This method is either defined as a property of the field or constructed from its name. If the name were "audio", the accessor would be getAudio. The accessor is generated from the "get" prefix with the capitalized name of the field. Audio formats Before we go on with Plone and see how we can enhance the story of audio processing and manipulate audio data, we will glance at audio formats. We will see how raw audio data is compressed to enable effective audio storage and streaming. We need to have some basic audio know-how about some of the terminology to understand how we can effectively process audio for our own purposes. As with images, there are several formats in which audio content can be stored. We want to learn a bit of theoretical background. This eases the decision of choosing the right format for our use case. An analog acoustic signal can be displayed as a wave: If digitalized, the wave gets approximated by small rectangles below the curve. The more rectangles are used the better is the sound (fidelity) of the digital variant. The width of the rectangles is called the sampling rate. Usual sampling rates include: 44.1 kHz (44,100 samples per second): CD quality 32 kHz: Speech 14.5 kHz: FM radio bandwidth 10 kHz: AM radio Each sample is stored with a fixed number of bits. This value is called the audio bit depth or bit resolution. Finally, there is a third value that we already know from the analog side. It is the channel. We have one channel for mono and two channels for stereo. For the digital variant, this means a doubling of data if stereo is used. So let's do a calculation. Let's assume we have an audio podcast with a length of eight minutes, which we want to stream in stereo CD quality. The sampling rate corresponds with the highest frequency of sound that is stored. For accurate reproduction of the original sound, the sample rate has to be at least twice that ofthe highest frequency sound. Most humans cannot hear frequencies higher than 20 kHz. The corresponding sampling rate to 20 kHz is a sampling rate of 44100 samples. We want to use a bit resolution of 16. This is the standard bit depth for audio CDs. Lastly, we have two channels for stereo: 44100 x 16 x 2 x 60 x 8= 677376000 bits = 84672000 bytes ˜ 80.7 MB This is quite a lot of data for eight minutes of CD-quality sound. We do not want to store so much data and more importantly, we do not want to send so much data over the Internet. So what we do is compress the data. Zipping the data would not give us a big effect because of the binary structure of digital audio data. There are different types of compressions for different types of data. ZIPs are good for text, JPEG is good for images, and MP3 is good for music—but why? Each of these algorithms takes the nature of the data into account. ZIP looks for redundant characters, JPEG unifies similar color areas, and MP3 strips the frequencies humans do not hear from the raw data.
Read more
  • 0
  • 0
  • 1701

article-image-customizing-your-vim-work-area
Packt
14 May 2010
12 min read
Save for later

Customizing Your Vim for work area

Packt
14 May 2010
12 min read
(Read more interesting articles on Hacking Vim 7.2 here.) Work area personalization In this article, we introduce a list of smaller, good-to-know modifications for the editor area in Vim. The idea with these recipes is that they all give you some sort of help or optimization when you use Vim for editing text or code. Adding a more visual cursor Sometimes, you have a lot of syntax coloring in the file you are editing. This can make the task of tracking the cursor really hard. If you could just mark the line the cursor is currently in, then it would be easier to track it. Many have tried to fix this with Vim scripts, but the results have been near useless (mainly due to slowness, which prevented scrolling longer texts at an acceptable speed). Not until version 7 did Vim have a solution for this. But then it came up with not just one, but two possible solutions for cursor tracking. The first one is the cursorline command , which basically marks the entire line with, for example, another background color without breaking the syntax coloring. To turn it on, use the following command: :set cursorline The color it uses is the one defined in the CursorLine color group. You can change this to any color or styling you like, for example: :highlight CursorLine guibg=lightblue ctermbg=lightgray If you are working with a lot of aligned file content (such as tab-separated data), the next solution for cursor tracking comes in handy: :set cursorcolumn This command marks the current column (here the cursor is placed) by coloring the entire column through the entire file, for example. As with the cursor line, you can change the settings for how the cursor column should be marked. The color group to change is named cursorcolumn. Adding both the cursor line and column marking makes the cursor look like a crosshair, thus making it impossible to miss. Even though the cursorline and cursorcolumn functionalities are implemented natively in Vim, it can still give quite a slowdown when scrolling through the file. Adding line numbers Often when compiling and debugging code, you will get error messages stating that the error is in some line. One could, of course, start counting lines from the top to find the line, but Vim has a solution to go directly to some line number. Just execute :XXX where XXX is the line number, and you will be taken to the XXX line. Alternatively, you can go into normal mode (press the Esc key) and then simply use XXXgg or XXXG (again XXX is the line number). Sometimes, however, it is nice to have an indication of the line number right there in the editor, and that's where the following command comes in handy: :set number Now, you get line numbers to the left of each line in the file. By default, the numbers take up four columns of space, three for numbers, and one for spacing. This meansthat the width of the numbers will be the same until you have more than 999 lines. If you get above this number of lines, an extra column will be added and the content will be moved to the right. Of course, you can change the default number of columns used for the line numbers. This can be achieved by changing the following property: :set numberwidth=XXX Replace XXX with the number of columns that you want. Even though it would be nice to make the number of columns higher in order to get more spacing between code and line numbers, this is not achievable with the numberwidth property. This is because the line numbers will be right-aligned within the columns. In the following figure, you can see how line numbers are shown as right-aligne when a higher number of columns are set in numberwidth: You can change the styling of the line numbers and the columns they are in by making changes to the LineNr color group. Spell checking your language We all know it! Even if we are really good spellers, it still happens from time to time that we misspell a word or hit the wrong keys. In the past, you had to run your texts (that you had written in Vim) through some sort of spell checker such as Aspell or Ispell . This was a tiresome process that could only be performed as a final task, unless you wanted to do it over and over again. With version 7 of Vim, this troublesome way of spell checking is over. Now, Vim has got a built-in spell checker with support for more than 50 languages from around the world. The new spell checker marks the wrongly written words as you type them in, so you know right away that there is an error. The command to execute to turn on this helpful spell checker feature is: :set spell This turns on the spell checker with the default language (English). If you don't use English much and would prefer to use another language in the spell checker, then there is no problem changing this. Just add the code of the language you would like to use to the spelllang property . For example: :set spelllang=de Here, the language is set to German (Deutsch) as the spell checker language of choice. The language name can be written in several different formats. American English, for example, can be written as: en_us us American Names can even be an industry-related name such as medical. If Vim does not recognize the language name, Vim will highlight it when you execute the property-setting command. If you change the spelllang setting to a language not already installed, then Vim will ask you if it should try to automatically retrieve it from the Vim homepage. Personally, I tend to work in several different languages in Vim, and I really don't want to tell Vim all the time which language I am using right now. Vim has a solution for this. By appending more language codes to the spelllang property (separated by commas), you can tell Vim to check the spelling in more than one language. :set spelllang=en,da,de,it Vim will then take the languages from the start to the end, and check if the words match any word in one of these languages. If they do, then they are not marked as a spelling error. Of course, this means that you can have a word spelled wrong in the language you are using but spelled correctly in another language, thereby introducing a hidden spelling error. You can find language packages for a lot of languages at the Vim FTP site: ftp://ftp.vim.org/pub/vim/runtime/spell. Spelling errors are marked differently in Vim and Gvim. In regular Vim, the misspelled word is marked with the SpellBad color group (normally, white on red). In Gvim, the misspelled word is marked with a red curvy line underneath the word. This can, of course, be changed by changing the settings of the color group. Whenever you encounter a misspelled word, you can ask Vim to suggest better ways to spell the word. This is simply done by placing the cursor over the word, going into the normal mode (press Esc), and then pressing Z + =. If possible, Vim will give you a list of good guesses for the word you were actually trying to write. In front of each suggestion is a number. Press the number you find in front of the right spelling (of the word you wanted) or press Enter if the word is not there. Often Vim gives you a long list of alternatives for your misspelled word, but unless you have spelled the word completely wrong, chances are that the correct word is within the top five of the alternatives. If this is the case, and you don't want to look through the entire list of alternatives, then you can limit the output with the following command: :set spellsuggest=X Set X to the number of alternative ways of spelling you want Vim to suggest. Adding helpful tool tips In the Modifying tabs recipe, we learned about how to use tool tips to store more information using less space in the tabs in Gvim. To build on top of that same idea with this recipe, we move on and use tool tips in other places in the editor. The editing area is the largest part of Vim. So, why not try to add some extra information to the contents of this area by using tool tips? In Vim, tool tips for the editing area are called balloons and they are only shown when the cursor is hovering over one of the characters. The commands you will need to know in order to use the balloons are: The first command is the one you will use to actually turn on this functionality in Vim. :set ballooneval The second command tells Vim how long it should wait before showing the tool tip/balloon (the delay is in milliseconds and as a default is set to 600). :set balloondelay=400 The last command is the one that actually sets the string that Vim will show in the balloon. :set ballonexpr="textstring" This can either be a static text string or the return of some function. In order to have access to information about the place where you are hovering over a character in the editor, Vim provides access to a list of variables holding such information: abc<space>and abc<enter> Both expand 123abc<space> Will not expand as the abbreviation is part of a word abcd<space> Will not expand because there are letters after the abbreviation abc Will not expand until another special letter is pressed So with these variables in hand, let's look at some examples. Example 1: The first example is based on one from the Vim help system. It shows how to make a simple function that will show the information from all the available variables. function! SimpleBalloon() return 'Cursor is at line/column: ' . v:beval_lnum . '/' . v:beval_col . ' in file ' . bufname(v:beval_bufnr) . '. Word under cursor is: "' . v:beval_text . '"' endfunction set balloonexpr=SimpleBalloon() set balloonevalcode 59 The result will look similar to the following screenshot: Example 2: Let's look at a more advanced example that explores the use of balloons for specific areas in editing. In this example, we will put together a function that gives us great information balloons for two areas at the same time: Misspelled words: The balloon gives ideas for alternative words Folded text: The balloon gives a preview of what's in the fold So, let's take a look at what the function should look for, to detect if the cursor is hovering over either a misspelled word or a fold line (a single line representing multiple lines folded behind it). In order to detect if a word is misspelled, the spell check would need to be turned on: :set spell If it is on, then calling the built-in spell checker function—spellsuggest()—would return alternative words if the hovered word was misspelled. So, to see if a word is misspelled, just check if the spellsuggest() returns anything. There is, however, a small catch. spellsuggest() also returns alternative, similar words if the word is not misspelled. To get around this, another function has to be used on the input word before putting it into the spellsuggest() function . This extra function is the spellbadword(). This basically moves the cursor to the first misspelled word in the sentence that it gets as input, and then returns the word. We just input a single word and if it is not misspelled, then the function cannot return any words. Putting no word into spellsuggest() results in getting nothing back, so we can now check if a word is misspelled or not. It is even simpler to check if a word is in a line, in a fold. You simply have to call the foldclosed()function on the line number of the line over which the cursor is hovering (remember v:beval_lnum ?), and it will return the number of the first line in the current fold; if not in a fold, then it returns -1. In other words, if foldclosed(v:beval_lnum) returns anything but -1 and 0, we are in a fold. Putting all of this detection together and adding functionality to construct the balloon text ends up as the following function: function! FoldSpellBalloon() let foldStart = foldclosed(v:beval_lnum ) let foldEnd = foldclosedend(v:beval_lnum) let lines = [] " Detect if we are in a fold if foldStart < 0 " Detect if we are on a misspelled word let lines = spellsuggest( spellbadword(v:beval_text)[ 0 ], 5, 0 ) else " we are in a fold let numLines = foldEnd - foldStart + 1 " if we have too many lines in fold, show only the first 14 " and the last 14 lines if ( numLines > 31 ) let lines = getline( foldStart, foldStart + 14 ) let lines += [ '-- Snipped ' . ( numLines - 30 ) . ' lines --' ] let lines += getline( foldEnd - 14, foldEnd ) else "less than 30 lines, lets show all of them let lines = getline( foldStart, foldEnd ) endif endif " return result return join( lines, has( "balloon_multiline" ) ? "n" : " " ) endfunction set balloonexpr=FoldSpellBalloon() set ballooneval The result is some really helpful balloons in the editing area of Vim that can improve your work cycle tremendously. The following screenshot shows how the information balloon could look when it is used to preview a folded range of lines from a file: Instead, if the balloon is used on a misspelled word, it will look like the following screenshot: In Production Boosters, you can learn more about how to use folding of lines to boost productivity in Vim.
Read more
  • 0
  • 0
  • 4103

article-image-personalizing-vim
Packt
14 May 2010
12 min read
Save for later

Personalizing Vim

Packt
14 May 2010
12 min read
(Read more interesting articles on Hacking Vim 7.2 here.) Some of these tasks contain more than one recipe because there are different aspects for personalizing Vim for that particular task. It is you, the reader, who decides which recipes (or parts of it) you would like to read and use. Before we start working with Vim, there are some things that you need to know about your Vim installation, such as where to find the configuration files. Where are the configuration files? When working with Vim, you need to know a range of different configuration files. The location of these files is dependent on where you have installed Vim and the operating system that you are using. In general, there are three configuration files that you must know where to find: vimrc gvimrc exrc The vimrc file is the main configuration file for Vim. It exists in two versions—global and personal. The global vimrc file is placed in the folder where all of your Vim system files are installed. You can find out the location of this folder by opening Vim and executing the following command in normal mode: :echo $VIM The examples could be: Linux: /usr/share/vim/vimrc Windows: c:program filesvimvimrc The personal vimrc file is placed in your home directory. The location of the home directory is dependent on your operating system. Vim was originally meant for Unixes, so the personal vimrc file is set to be hidden by adding a dot as the first character in the filename. This normally hides files on Unix, but not on Microsoft Windows. Instead, the vimrc file is prepended with an underscore on these systems. So, examples would be: Linux: /home/kim/.vimrc Windows: c:documents and settingskim_vimrc Whatever you change in the personal vimrc file will overrule any previous setting made in the global vimrc file. This way you can modify the entire configuration without having to ever have access to the global vimrc file. You can find out what Vim considers as the home directory on your system by executing the following command in normal mode: :echo $HOME Another way of finding out exactly which vimrc file you use as your personal file is by executing the following command in the normal mode: :echo $MYVIMRC The vimrc file contains ex (vi predecessor) commands, one on each line, and is the default place to add modifications to the Vim setup. In the rest of the article, this file is just called vimrc. Your vimrc can use other files as an external source for configurations. In the vimrc file, you use the source command like this: source /path/to/external/file Use this to keep the vimrc file clean, and your settings more structured. (Learn more about how to keep your vimrc clean in Appendix B, Vim Configuration Alternatives). The gvimrc file is a configuration file specifically for Gvim. It resembles the vimrc file previously described, and is placed in the same location as a personal version as well as a global version. For example: Linux: /home/kim/.gvimrc and /usr/share/vim/gvimrc Windows: c:documents and settingskim_gvimrc and c:program filesvimgvimrc This file is used for GUI-specific settings that only Gvim will be able to use. In the rest of the article, this file is called gvimrc. The gvimrc file does not replace the vimrc file, but is simply used for configurationsthat only apply to the GUI version of Vim. In other words, there is no need to haveyour configurations duplicated in both the vimrc file and the gvimrc file. The exrc file is a configuration file that is only used for backwards compatibility with the old vi / ex editor. It is placed at the same location (both global and local) as vimrc, and is used the same way. However, it is hardly used anymore except if you want to use Vim in a vi-compatible mode. Changing the fonts In regular Vim, there is not much to do when it comes to changing the font because the font follows one of the terminals. In Gvim, however, you are given the ability to change the font as much as you like. The main command for changing the font in Linux is: :set guifont=Courier 14 Here, Courier can be exchanged with the name of any font that you have, and 14 with any font size you like (size in points—pt). For changing the font in Windows, use the following command: :set guifont=Courier:14 If you are not sure about whether a particular font is available on the computer or not, you can add another font at the end of the command by adding a comma between the two fonts. For example: :set guifont=Courier New 12, Arial 10 If the font name contains a whitespace or a comma, you will need to escape it with a backslash. For example: :set guifont=Courier New 12 This command sets the font to Courier New size 12, but only for this session. If you want to have this font every time you edit a file, the same command has to be added to your gvimrc file (without the : in front of set). In Gvim on Windows, Linux (using GTK+), Mac OS, or Photon, you can get a font selection window shown if you use this command: :set guifont=*. If you tend to use a lot of different fonts depending on what you are currently working with (code, text, logfiles, and so on.), you can set up Vim to use the correct font according to the file type. For example, if you want to set the font to Arial size 12 every time a normal text file (.txt) is opened, this can be achieved by adding the following line to your vimrc file: autocmd BufEnter *.txt set guifont=Arial 12 The window of Gvim will resize itself every time the font is changed. This means, if you use a smaller font, you will also (as a default) have a smaller window. You will notice this right away if you add several different file type commands like the one previously mentioned, and then open some files of different types. Whenever you switch to a buffer with another file type, the font will change, and hence the window size too. You can find more information about changing fonts in the Vim help system under Help | guifont. Changing color scheme Often, when working in a console environment, you only have a black background and white text in the foreground. This is, however, both dull and dark to look at. Some colors would be desirable. As a default, you have the same colors in the console Vim as in the console you opened it from. However, Vim has given its users the opportunity to change the colors it uses. This is mostly done with a color scheme file. These files are usually placed in a directory called colors wherever you have installed Vim. You can easily change the installed color schemes with the command: :colorscheme mycolors Here, mycolors is the name of one of the installed color schemes. If you don't know the names of the installed color schemes, you can place the cursor after writing: :colorscheme Now, you can browse through the names by pressing the Tab key. When you find the color scheme you want, you can press the Enter key to apply it. The color scheme not only applies to the foreground and background color, but also to the way code is highlighted, how errors are marked, and other visual markings in the text. You will find that some color schemes are very alike and only minor things have changed. The reason for this is that the color schemes are user supplied. If some user did not like one of the color settings in a scheme, he or she could just change that single setting and re-release the color scheme under a different name. Play around with the different color schemes and find the one you like. Now, test it in the situations where you would normally use it and see if you still like all the color settings. While learning Basic Vim Scripting, we will get back to how you can change a color scheme to fit your needs perfectly. Personal highlighting In Vim, the feature of highlighting things is called matching. With matching, you can make Vim mark almost any combination of letters, words, numbers, sentences, and lines. You can even select how it should be marked (errors in red, important words in green, and so on). Matching is done with the following command: :match Group /pattern/ The command has two arguments. The first one is the name of the color group that you will use in the highlight. Compared to a color scheme, which affects the entire color setup, a color group is a rather small combination of background (or foreground) colors that you can use for things such as matches. When Vim is started, a wide range of color groups are set to default colors, depending on the color scheme you have selected. To see a complete list of color groups, use the command: :so $VIMRUNTIME/syntax/hitest.vim. The second argument is the actual pattern you want to match. This pattern is a regular expression and can vary from being very simple to extremely complex, depending on what you want to match. A simple example of the match command in use would be: :match ErrorMsg /^Error/ This command looks for the word Error (marked with a ^) at the beginning of all lines. If a match is found, it will be marked with the colors in the ErrorMsg color group (typically white text on red background). If you don't like any of the available color groups, you can always define your own. The command to do this is as follows: :highlight MyGroup ctermbg=red guibg=red gctermfg=yellowguifg=yellow term=bold This command creates a color group called MyGroup with a red background and yellow text, in both the console (Vim) and the GUI (Gvim). You can change the following options according to your preferences: ctermb Background color in console guibg Background color in Gvim ctermf Text color in console guifg Text color in Gvim gui Font formatting in Gvim term Font formatting in console (for example, bold) If you use the name of an existing color group, you will alter that group for the rest of the session. When using the match command, the given pattern will be matched until you perform a new match or execute the following command: :match NONE The match command can only match one pattern at a time, so Vim has provided you with two extra commands to match up to three patterns at a time. The commands are easy to remember because their names resemble those of the match command: :2match:3match You might wonder what all this matching is good for, as it can often seem quite useless. Here are a few examples to show the strength of matching. Example 1: Mark color characters after a certain column In mails, it is a common rule that you do not write lines more than 74 characters (a rule that also applies to some older programming languages such as, Fortran-77). In a case like this, it would be nice if Vim could warn you when you reached this specific number of characters. This can simply be done with the following command: :match ErrorMsg /%>73v.+/ Here, every character after the 73rd character will be marked as an error. This match is a regular expression that when broken down consists of: %> Match after column with the number right after this 73 The column number V Tells that it should work on virtual columns only .+ Match one or more of any character Example 2: Mark tabs not used for indentation in code When coding, it is generally a good rule of thumb to use tabs only to indent code, and not anywhere else. However, for some it can be hard to obey this rule. Now, with the help of a simple match command, this can easily be prevented. The following command will mark any tabs that are not at the beginning of the line (indentation) as an error: :match errorMsg /[^t]zst+/ Now, you can check if you have forgotten the rule and used the Tab key inside the code. Broken down, the match consists of the following parts: [^ Begin a group of characters that should not be matched t The tab character ] End of the character group zs A zero-width match that places the 'matching' at the beginning of the line ignoring any whitespaces t+ One or more tabs in a row This command says: Don't match all the tab characters; match only the ones that are not used at the beginning of the line (ignoring any whitespaces around it). If instead of using tabs you want to use the space character for indentation, you can change the command to: :match errorMsg /[t]/ This command just says: Match all the tab characters. Example 3: Preventing errors caused by IP addresses If you write a lot of IP addresses in your text, sometimes you tend to enter a wrong value in one (such as 123.123.123.256). To prevent this kind of an error, you can add the following match to your vimrc file: match errorMsg /(2[5][6-9]|2[6-9][0-9]|[3-9][0-9][0-9])[.][0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}|[0-9]{1,3}[.](2[5][6-9]|2[6-9][0-9]| [3-9][0-9][0-9])[.][0-9]{1,3}[.][0-9]{1,3}|[0-9]{1,3}[.][0-9]{1,3}[.](2[5] [6-9]|2[6-9][0-9]|[3-9][0-9][0-9])[.][0-9]{1,3}|[0-9]{1,3}[.][0-9]{1,3}[.][0-9]{1,3}[.](2[5][6-9]|2[6-9][0-9]|[3-9][0-9][0-9])/ Even though this seems a bit too complex for solving a small possible error, you have to remember that even if it helps you just once, it is worth adding. If you want to match valid IP addresses, you can use this, which is a much simpler command: match todo /((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).) {3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/
Read more
  • 0
  • 0
  • 6786
article-image-manage-your-money-simple-invoices
Packt
13 May 2010
6 min read
Save for later

Manage Your Money with Simple Invoices

Packt
13 May 2010
6 min read
As a freelancer I have one primitive motive. I want to do work and get paid. Getting paid means I need to generate invoices and keep track of them. I've tried to manage my invoices via spreadsheets and documents, but keeping track of my payments in a series of disconnected files is a fragile and inefficient process. Simple Invoices provides a solution to this. Simple Invoices is a relatively young project, and working with it requires that you're willing to do some manual configurations and tolerate the occasional problem. To work with and install the application, you need to be familiar with running a web server on OS X, Windows, or Linux. The next section, Web Server Required provides some out of the box server packages that allow you to run a server environment on your personal computer. It's point and click easy and perfect for an individual user. Not up for running a web server, but still need to find a reliable invoicing application? No problem. Visit www.simpleinvoices.com for a list of hosted solutions. Let's get started. Web Server Required Simple Invoices is a web application that requires Apache, PHP, and MySQL to function. Even if you're not a system administrator, you can still run a web server on your computer, regardless of your operating system. Windows users can get the required software by installing WAMP from www.wampserver.com. OS X users can install MAMP from www.mamp.info. Linux users can install Apache, MySql, and PHP5 using their distribution's software repositories. The database administrative tool, phpMyAdmin makes managing the MySQL database intuitive. Both the WAMP and MAMP installers contain phpMyAdmin, and we'll use it to setup our databases. Take a moment to setup your web server before continuing with the Simple Invoices installation. Install Simple Invoices Our first step will be to prepare the MySQL database. Open a web browser and navigate to http://localhost/phpmyadmin. Replace localhost with the actual server address. A login screen will display and will prompt you for a user name and password. Enter the the root login information for your MySQL install. MAMP users might try root for both the user name and password. WAMP users might try root with no password. If you plan on keeping your WAMP or MAMP servers installed, setting new root passwords for your MySQL database is a good idea, even if you do not allow external connections to your server. After you log in to phpMyAdmin, you will see a list of databases on the left sidebar; the main content window displays a set of tabs, including Databases, SQL, and Status. Let's create the database. Click on the Privileges tab to display a list of all users and associated access permissions. Find the Add a New User link and click on it. The Add New User page displays. Complete the following fields: User Name: enter simpleinvoices Host: select Local Password: specify a password for the user; then retype it in the field provided Database for User: select the Create database with same name and grant all privileges option Scroll to the bottom of the page and click the Go button. This procedure creates the database user and the database at the same time. If you wanted to use a database name that was different than the user name, then could have selected None for the Database for user configuration and added the database manually via the Databases tab in phpMyAdmin. If you prefer to work with MySQL directly, the SQL code for the steps we just ran is (the *** in the first line is the password): CREATE USER 'simpleinvoices'@'localhost' IDENTIFIED BY '***'; GRANT USAGE ON *.* TO 'simpleinvoices'@'localhost' IDENTIFIED BY '***' WITH MAX_QUERIES_PER_HOUR 0 MAX_CONNECTIONS_PER_HOUR 0 MAX_UPDATES_PER_HOUR 0 MAX_USER_CONNECTIONS 0;CREATE DATABASE IF NOT EXISTS `simpleinvoices`; GRANT ALL PRIVILEGES ON `simpleinvoices`.* TO 'simpleinvoices'@'localhost'; Now that the database is setup, let's download the stable version of Simple Invoices by visiting www.simpleinvoices.org and following the Download link. The versions are identified by the year and version. At the time of this writing, the stable version is 2010.1. Unzip the Simple Invoices download file into a subdirectory on your web server. Because I like to install a lot of software, I like to keep the application name in my directory structure, so my example installation installs to a directory named simpleinvoices. That makes my installation available at http://localhost/simpleinvoices. Pick a directory path that makes sense for you. Not sure where the root of your web server resides on your server? Here are some of the default locations for the various server environments: WAMP – C:wampwww MAMP – /Applications/MAMP/htdocs Linux – /var/www Linux users will need to set the ownership of the tmp directory to the web user and make the tmp directory writable. For an Ubuntu system, the appropriate commands are: chown -R www-data tmpchmod -R 775 tmp The command syntax assumes we're working from the Simple Invoices installation directory on the web server. The web user on Ubuntu and other Debian-based systems is www-data. The -R option in both commands applies the permissions to all sub-directories and files. With the chmod command, you are granting write access to the web user. If you have problems or feel like being less secure, you can reduce this step down to one command: chmod -R 777 tmp. We're almost ready to open the Simple Invoices installer, but before we go to the web browser, we need to define the database connections in the config/config.ini file. At a minimum, we need to specify the database.params.username and database.params.password with the values we used to setup the database. If you skip this step and try to open Simple Invoices in your web browser, you will receive an error message indicating that your config.ini settings are incorrect. The following screenshot shows the relevant settings in confi.ini. Now, we're ready to start Simple Invoices and step through the graphical installer. Open a web browser and navigate to your installation (for example: http://localhost/simpleinvoices). Step 1: Install Database will display in the browser. Review the database connection information and click the Install Database button. Step 2: Import essential data displays. Click the Install Essential Data button to advance the installation. Step 3: Import sample data displays. We can choose to import sample data or start using the application. The sample data contains a few example billers, customers, and invoices. We're going to set all that up from scratch, so I recommend you click the Start using Simple Invoices button. At this point the Simple Invoices dashboard displays with a yellow note that instructs us to configure a biller, a customer, and a product before we create our first invoice. See the following screenshot. You might notice that the default access to Simple Invoices is not protected by a username and password. We can force authentication by adding a user and password via the People > Users screen. Then set the authentication.enabled field in config.ini equal to true.
Read more
  • 0
  • 0
  • 3024

article-image-data-binding-expression-blend-4-silverlight-4
Packt
13 May 2010
7 min read
Save for later

Data binding from Expression Blend 4 in Silverlight 4

Packt
13 May 2010
7 min read
Using the different modes of data binding to allow persisting data Until now, the data has flowed from the source to the target (the UI controls). However, it can also flow in the opposite direction, that is, from the target towards the source. This way, not only can data binding help us in displaying data, but also in persisting data. The direction of the flow of data in a data binding scenario is controlled by the Mode property of the Binding. In this recipe, we'll look at an example that uses all the Mode options and in one go, we'll push the data that we enter ourselves to the source. Getting ready This recipe builds on the code that was created in the previous recipes, so if you're following along, you can keep using that codebase. You can also follow this recipe from the provided start solution. It can be found in the Chapter02/SilverlightBanking_Binding_ Modes_Starter folder in the code bundle that is available on the Packt website. The Chapter02/SilverlightBanking_Binding_Modes_Completed folder contains the finished application of this recipe. How to do it... In this recipe, we'll build the "edit details" window of the Owner class. On this window, part of the data is editable, while some isn't. The editable data will be bound using a TwoWay binding, whereas the non-editable data is bound using a OneTime binding. The Current balance of the account is also shown—which uses the automatic synchronization—based on the INotifyPropertyChanged interface implementation. This is achieved using OneWay binding. The following is a screenshot of the details screen: Let's go through the required steps to work with the different binding modes: Add a new Silverlight child window called OwnerDetailsEdit.xaml to the Silverlight project. In the code-behind of this window, change the default constructor—so that it accepts an instance of the Owner class—as shown in the following code: private Owner owner; public OwnerDetailsEdit(Owner owner) { InitializeComponent(); this.owner = owner; } In MainPage.xaml, add a Click event on the OwnerDetailsEditButton: <Button x_Name="OwnerDetailsEditButton" Click="OwnerDetailsEditButton_Click" > In the event handler, add the following code, which will create a new instance of the OwnerDetailsEdit window, passing in the created Owner instance: private void OwnerDetailsEditButton_Click(object sender, RoutedEventArgs e) { OwnerDetailsEdit ownerDetailsEdit = new OwnerDetailsEdit(owner); ownerDetailsEdit.Show(); } The XAML of the OwnerDetailsEdit is pretty simple. Take a look at the completed solution (Chapter02/SilverlightBanking_Binding_Modes_Completed)for a complete listing. Don't forget to set the passed Owner instance as the DataContext for the OwnerDetailsGrid. This is shown in the following code: OwnerDetailsGrid.DataContext = owner; For the OneWay and TwoWay bindings to work, the object to which we are binding should be an instance of a class that implements the INotifyPropertyChanged interface. In our case, we are binding an Owner instance. This instance implements the interface correctly. The following code illustrates this: public class Owner : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; ... } Some of the data may not be updated on this screen and it will never change. For this type of binding, the Mode can be set to OneTime. This is the case for the OwnerId field. The users should neither be able to change their ID nor should the value of this field change in the background, thereby requiring an update in the UI. The following is the XAML code for this binding: <TextBlock x_Name="OwnerIdValueTextBlock" Text="{Binding OwnerId, Mode=OneTime}" > </TextBlock> The CurrentBalance TextBlock at the bottom does not need to be editable by the user (allowing a user to change his or her account balance might not be benefi cial for the bank), but it does need to change when the source changes. This is the automatic synchronization working for us and it is achieved by setting the Binding to Mode=OneWay. This is shown in the following code: <TextBlock x_Name="CurrentBalanceValueTextBlock" Text="{Binding CurrentBalance, Mode=OneWay}" > </TextBlock> The final option for the Mode property is TwoWay. TwoWay bindings allow us to persist data by pushing data from the UI control to the source object. In this case, all other fields can be updated by the user. When we enter a new value, the bound Owner instance is changed. TwoWay bindings are illustrated using the following code: <TextBox x_Name="FirstNameValueTextBlock" Text="{Binding FirstName, Mode=TwoWay}" > </TextBox> We've applied all the different binding modes at this point. Notice that when you change the values in the pop-up window, the details on the left of the screen are also updated. This is because all controls are in the background bound to the same source object as shown in the following screenshot: How it works... When we looked at the basics of data binding, we saw that a binding always occurs between a source and a target. The first one is normally an in-memory object, but it can also be a UI control. The second one will always be a UI control. Normally, data flows from source to target. However, using the Mode property, we have the option to control this. A OneTime binding should be the default for data that does not change when displayed to the user. When using this mode, the data flows from source to target. The target receives the value initially during loading and the data displayed in the target will never change. Quite logically, even if a OneTime binding is used for a TextBox, changes done to the data by the user will not flow back to the source. IDs are a good example of using OneTime bindings. Also, when building a catalogue application, OneTime bindings can be used, as we won't change the price of the items that are displayed to the user (or should we...?). We should use a OneWay binding for binding scenarios in which we want an up-to-date display of data. Data will flow from source to target here also, but every change in the values of the source properties will propagate to a change of the displayed values. Think of a stock market application where updates are happening every second. We need to push the updates to the UI of the application. The TwoWay bindings can help in persisting data. The data can now flow from source to target, and vice versa. Initially, the values of the source properties will be loaded in the properties of the controls. When we interact with these values (type in a textbox, drag a slider, and so on), these updates are pushed back to the source object. If needed, conversions can be done in both directions. There is one important requirement for the OneWay and TwoWay bindings. If we want to display up-to-date values, then the INotifyPropertyChanged interface should be implemented. The OneTime and OneWay bindings would have the same effect, even if this interface is not implemented on the source. The TwoWay bindings would still send the updated values if the interface was not implemented; however, they wouldn't notify about the changed values. It can be considered as a good practice to implement the interface, unless there is no chance that the updates of the data would be displayed somewhere in the application. The overhead created by the implementation is minimal. There's more... Another option in the binding is the UpdateSourceTrigger. It allows us to specify when a TwoWay binding will push the data to the source. By default, this is determined by the control. For a TextBox, this is done on the LostFocus event; and for most other controls, it's done on the PropertyChanged event. The value can also be set to Explicit. This means that we can manually trigger the update of the source. BindingExpression expression = this.FirstNameValueTextBlock. GetBindingExpression(TextBox.TextProperty); expression.UpdateSource(); See also Changing the values that flow between source and target can be done using converters. Data binding from Expression Blend 4 While creating data bindings is probably a task mainly reserved for the developer(s) in the team, Blend 4—the design tool for Silverlight applications—also has strong support for creating and using bindings. In this recipe, we'll build a small data-driven application that uses data binding. We won't manually create the data binding expressions; we'll use Blend 4 for this task.
Read more
  • 0
  • 0
  • 5475

article-image-creating-customizing-and-assigning-portlets-automatically-plone-33
Packt
12 May 2010
5 min read
Save for later

Creating, Customizing, and Assigning Portlets Automatically for Plone 3.3

Packt
12 May 2010
5 min read
(For more resources on Plone, see here.) Introduction One of the major changes from Plone 2.5 to Plone 3.0 was the complete refactoring of its portlets engine. In Plone 2.5, left and right side portlets were managed from Zope Management Interface (ZMI) by setting two special properties: left_slots and right_slots. This was not so hard but was cumbersome. Any TALES path expression—ZPT macros typically—could be manually inserted in the above two properties and would be displayed as a bit of HTML to the final user. The major drawback for site managers with the old-style approach was not just the uncomfortable way of setting which portlets to display, but the lack of any configuration options for them. For example, if we wanted to present the latest published news items, we wouldn't have any means of telling how many of them to show, unless the portlet itself were intelligent enough to get that value from some other contextual property. But again, we were still stuck in the ZMI. Fortunately, this has changed enormously in Plone 3.x: Portlets can now be configured and their settings are mantained in ZODB. Portlets are now managed via a user-friendly, Plone-like interface. Just click on the Manage portlets link below each of the portlets columns (see the following screenshot). In the above screen, if we click on the News portlet link, we are presented with a special configuration form to choose the Number of items to display and the Workflow state(s) we want to consider when showing news items. If you have read previous chapters, you might be correctly guessing that Zope 3 components (zope.formlib mainly) are behind the portlets configuration forms. In the next sections, we'll look again at the customer's requirements: Advertisement banners will be located in several areas of every page Advertisement banners may vary according to the section of the website Creating a portlet package Once again, paster comes to the rescue. As in Creating a product package structure and Creating an Archetypes product with paster, we will use the paster command here to create all the necessary boilerplate (and even more) to get a fully working portlet. Getting ready As we are still at the development stage, we should run the following commands in our buildout's src folder: cd ./src How to do it... Run the paster command: We are going to create a new egg called pox.portlet.banner. The pox prefix is from PloneOpenX (the website we are working on) and it will be the namespace of our product. Portlets eggs usually have a nested portlet namespace as in plone.portlet.collection or plone.portlet.static. We have chosen the pox main namespace as in the previous eggs we have created, and the banner suffix corresponds to the portlet name. For more information about eggs and packages names read http://www.martinaspeli.net/articles/the-naming-ofthings-package-names-and-namespaces.If you want to add portlets to an existing package instead of creating a new one, the steps covered in this chapter should tell you all you need to know (the use of paster addcontent portlet local command will be of great help). In your src folder, run the following command: paster create -t plone3_portlet This paster command creates a product using the plone3_portlet template. When run, it will output some informative text, and then a short wizard will be started to select options for the package: Option Value Enter project name pox.portlet.banner Expert Mode? easy Version 1.0 Description Portlet to show banners Portlet Name Banner portlet Portlet Type BannerPortlet After selecting the last option, you'll get an output like this (a little longer actually): Creating directory ./pox.portlet.banner ... Recursing into +namespace_package+ Recursing into +namespace_package2+ Recursing into +package+ Recursing into profiles Creating ./pox.portlet.banner/pox/portlet/banner/profiles/ Recursing into default Creating ./pox.portlet.banner/pox/portlet/banner/profiles/default/ Copying metadata.xml_tmpl to ./pox.portlet.banner/pox/portlet/banner/profiles/default/metadata.xml Copying portlets.xml_tmpl to ./pox.portlet.banner/pox/portlet/banner/profiles/default/portlets.xml This tells us that even the GenericSetup extension profile has also been created by paster. This means that we can install the new Banner portlet product (as entered in the portlet_name option above). Install the product: To tell our Zope instance about the new product, we must update the buildout.cfg file as follows: [buildout]...eggs =... pox.portlet.banner...develop = src/pox.portlet.banner We can automatically install the product during buildout. Add a pox.portlet.banner line inside the products parameter of the [plonesite] part: [plonesite]recipe = collective.recipe.plonesite...products =... pox.portlet.banner Build your instance and, if you want to, launch it to see the new empty Banner portlet: ./bin/buildout./bin/instance fg Check the new portlet: After implementing the changes above, if you click on the Manage portlets link in the site's home page (or anywhere in the Plone site), you will see a new Banner portlet option in the drop-down menu. A new box in the portlet column will then be shown. The Header/Body text/Footer box shown above matches the template definition in the bannerportlet.pt file the way paster created it.
Read more
  • 0
  • 0
  • 4652
article-image-creating-your-first-complete-moodle-theme
Packt
11 May 2010
11 min read
Save for later

Creating your First Complete Moodle Theme

Packt
11 May 2010
11 min read
So let's crack on... Creating a new theme Finding a base theme to create your Moodle theme on is the first thing that you need to do.There are, however, various ways to do this; you can make a copy of the standard themeand rename it as you did in part of this article, or you can use a parent theme that isalso based on the standard theme. The important point here is that the standard theme that comes with Moodle is the cornerstone of the Moodle theming process. Every other Moodle theme should be based upon this theme, and would normally describe the differences from the standard theme. Although this method does work and is a simple way to get started with Moodle theming, it does cause problems as new features could get added to Moodle that might cause your theme to display or function incorrectly. The standard theme will always be updated before a new release of Moodle is launched. So if you do choose to make a copy of the standard theme and change its styles, it would be best to make sure that you use a parent theme as well. In this way, the parent theme will be your base theme along with the changes that you make to your copy of the standard theme. However, there is another way of creating your first theme, and that is to create a copy of a theme that is very close to the standard theme, such as standardwhite, and use this as your theme. Moodle will then use the standard theme as its base theme and apply any changes that you make to the standardwhite theme on the top (parent). All we are doing is describing the differences between the standard and the standardwhite themes. This is better because Moodle developers will sometimes make changes to the standard theme to be up-to-date with new Moodle features. This means that on each Moodle update, your standard theme, folder will be updated automatically, thus avoiding any nasty display problems being caused by Moodle updates. The way by which you configure Moodle themes is completely up to you. If you see a theme that is nearly what you want and there aren't really many changes needed, then using a parent theme makes sense, as most of the styles that you require have already been written. However, if you want to create a theme that is completely different from any other theme or wish to really get into Moodle theming, then using a copy of one of the standard sheets would be best. So let's get on and see what the differences are when using different theme setups, and see what effect these different methods have on the theming process. Time for action – copying the standard theme Browse to your theme folder in C:Program FilesApache Software FoundationApache 2.2htdocstheme. Copy the standard theme by right-clicking on the theme's folder and choosing Copy. Paste the copied theme into the theme directory (the same directory that you arecurrently in). Rename the copy of standard folder to blackandblue or any other name that you wish to choose (remember not to use capitals or spaces). Open your Moodle site and navigate to Site Administration Appearance | Themes | Theme Selector|, and choose the blackandblue theme that you have just created. Y ou might have noticed that the theme shown in the preceding screenshot has a header that says Black and Blue theme. This is because I have added this to the Full site name in the Front Page settings page. Time for action – setting a parent theme Open your web browser and navigate to your Moodle site and log in as the administrator. Go to Site Administration | Appearance | Themes | Theme Selector and choose your blackandblue theme if it is not already selected. Browse to the root of your blackandblue folder, right-click on the config.php file, and choose Open with | WordPad. You need to make four changes to this file so that you can use this theme and a parent theme while ensuring that you still use the default standard theme as your base. Here are the changes: $THEME->sheets = array('user_styles');$THEME->standardsheets = true;$THEME->parent = 'autumn';$THEME->parentsheets = array('styles'); Let's look at each of these statements, in turn. $THEME->sheets = array('user_styles'); This contains the names of all of the stylesheet files that you want to include in this for your blackandblue theme, namely user_styles. $THEME->standardsheets = true; This parameter is used to include the standard theme's stylesheets. If it is set to True, it will use all of the stylesheets in the standard theme. Alternatively, it can be set as an array in order to load individual stylesheets in whatever order is required. We have set this to True, so we will use all of the stylesheets of the standard theme. $THEME->parent = 'autumn'; This variable can be set to use a theme as the parent theme, which is included before the current theme. This will make it easier to make changes to another theme without having to change the actual files. $THEME->parentsheets = array('styles'); This variable can be used to choose either all of the parent theme's stylesheets or individual files. It has been set to include the styles.css file from the parent theme, namely autumn. Because there is only one stylesheet in the Autumn theme, you can set this variable to True. Either way, you will have the same outcome. Save themeblackandblueconfig.php, and refresh your web browser window. You should see something similar to the following screenshot. Note that your blocks may be different to the ones below, but you can ignore this. What just happened? Okay, so now you have a copy of the standard theme that uses the Autumn theme (by Patrick Malley) as its parent. You might have noticed that the header isn't correct and that the proper Autumn theme header isn't showing. Well, this is because you are essentially using the copy of the standard theme and that the header from this theme is the one that you see above. It's only the CSS files that are included in this hierarchy, so any HTML changes will not be seen until you edit your standard theme's header.html file. Have a go hero – choose another parent theme Go back and have a look through some of the themes on Moodle.org and download one that you like. Add this theme as a parent theme to your blackandblue theme's config.php file, but this time choose which stylesheets you want to use from that theme. The Back to School theme is a good one for this exercise, as its stylesheets are clearly labeled. So you Copying the header and footer files To show that you are using the Autumn theme's CSS files and the standard theme's HTML files, you can just go and copy the header.html and footer.html files from Patrick Malley's Autumn theme and paste them into your blackandblue theme's folder. Don't worry about overwriting your header and footer files, as you can always just copy them again from the ac tual standard theme folder. Time for action – copying the header.html and footer.html files Browse to the Autumn theme's folder and highlight both the header.html and footer.html files by holding down the Ctrl key and clicking on them both. Right-click on the selected files and choose Copy. Browse to your blackandblue theme's folder and right-click and choose Paste. Go back to your browser window and press the F5 button to refresh the page. You will now see the full Autumn theme. What just happened? You have copied the autumn theme's header.html and footer.html files into your blackandblue theme, so you can see the full autumn theme working. You probably will not actually use the header.html and footer.html files that you just copied, as this was just an example of how the Moodle theming process works. So you now have an unmodified copy of the standard theme called blackandblue, which is using the autumn theme as its parent theme. All you need to do now to make changes to this theme is to edit your CSS file in the blackandblue theme folder. Theme folder housework However, there are a couple of things that you need to do first, as you have an exact copy of the standard theme apart from the header.html and footer.html files. This copied folder has files that you do not need, as the only file that you set for your theme to use was the user_styles.css file in the config.php file earlier. This was the first change that you made: $THEME->sheets = array('user_styles'); The user_style.css file does not exist in your blackandblue theme's folder, so you will need to create it. You will also need to delete any other CSS files that are present, as your new blackandblue theme will use only one stylesheet, namely the user_styles.css file that you will be creating in the following sections. Time for action – creating our stylesheet Right-click anywhere in your blackandblue folder and choose New Text Document|. Rename this text document to user_styles.css by right-clicking again and choosing Rename. Time for action – deleting CSS files that we don't need Delete the following CSS files by selecting them and then right-clicking on the selected files and choosing Delete. styles_color.css styles_ie6.css styles_ie6.css styles_ie7.css styles_layout.css styles_moz.css {background: #000000;} What just happened? In the last two tasks, you created an empty CSS file called user_style.css in your blackandblue theme's folder. You then deleted all of the CSS files in your blackandblue theme's folder, as you will no longer need them. Remember, these are just copies of the CSS files in the standard theme folder and you have set your theme to use the standard theme as its base in the blackandblue theme's config.php file. Let's make some changes Now you have set up your theme the way that you want it, that is, you are using your own blackandblue theme by using the standard theme as a base and the Autumn theme as the parent. Move on and make a few changes to your user_style.css file so that you can see what effect this has on your theme, and check that all of your config.php file's settings are correct. Remember that all of the current styles are being inherited from the Autumn theme. Time for action – checking our setup Open up your Moodle site with the current theme (which should be blackandblue but looks like the Autumn theme). Navigate to your blackandblue theme's folder, right-click on the user_style.css file, and choose Open. This file should be completely blank. Type in the following line of CSS for the body element, and then save the fi le: body {background: #000000;} Now refresh your browser window. You will see that the background is now black. Note: When using Firebug to identify styles that are being used, it might not always be obvious where they are or which style is controlling that element of the page. An example of this is the body {background: #000000;}code that we just pasted in our user_style.css file. If we had used Firebug to indentify that style, we would not have found it. Instead, I just took a look at the CSS file from the autumn theme. What I am trying to say here is that there will always be an element of poking around and trial and error. What just happened? All seems fine there, doesn't it? You have added one style declaration to your empty user_style.css file to change the background color, and have checked the changes in your browser. You now know how the parent themes work and know that you only need to copy the styles from Firebug into your user_style.css file and edit the style declarations that need to be changed.
Read more
  • 0
  • 1
  • 8476

article-image-introduction-data-binding
Packt
10 May 2010
10 min read
Save for later

Introduction to Data Binding

Packt
10 May 2010
10 min read
Introduction Data binding allows us to build data-driven applications in Silverlight in a much easier and much faster way compared to old-school methods of displaying and editing data. This article and the following one take a look at how data binding works. We'll start by looking at the general concepts of data binding in Silverlight 4 in this article. Analyzing the term data binding immediately reveals its intentions. It is a technique that allows us to bind properties of controls to objects or collections thereof. The concept is, in fact, not new. Technologies such as ASP.NET, Windows Forms, and even older technologies such as MFC (Microsoft Foundation Classes) include data binding features. However, WPF's data binding platform has changed the way we perform data binding; it allows loosely coupled bindings. The BindingsSource control in Windows Forms has to know of the type we are binding to, at design time. WPF's built-in data binding mechanism does not. We simply defi ne to which property of the source the target should bind. And at runtime, the actual data—the object to which we are binding—is linked. Luckily for us, Silverlight inherits almost all data binding features from WPF and thus has a rich way of displaying data. A binding is defined by four items: The source or source object: This is the data we are binding to. The data that is used in data binding scenarios is in-memory data, that is, objects. Data binding itself has nothing to do with the actual data access. It works with the objects that are a result of reading from a database or communicating with a service. A typical example is a Customer object. A property on the source object: This can, for example, be the Name property of the Customer object. The target control: This is normally a visual control such as a TextBox or a ListBox control. In general, the target can be a DependencyObject. In Silverlight 2 and Silverlight 3, the target had to derive from FrameworkElement; this left out some important types such as transformations. A property on the target control: This will, in some way—directly or after a conversion—display the data from the property on the source. The data binding process can be summarized in the following image: In the previous image, we can see that the data binding engine is also capable of synchronization. This means that data binding is capable of updating the display of data automatically. If the value of the source changes, Silverlight will change the value of the target as well without us having to write a single line of code. Data binding isn't a complete black box either. There are hooks in the process, so we can perform custom actions on the data fl owing from source to target, and vice versa. These hooks are the converters. Our applications can still be created without data binding. However, the manual process—that is getting data and setting all values manually on controls from code-behind—is error prone and tedious to write. Using the data-binding features in Silverlight, we will be able to write more maintainable code faster. In this article, we'll explore how data binding works. We'll start by building a small data-driven application, which contains the most important data binding features, to get a grasp of the general concepts. We'll also see that data binding isn't tied to just binding single objects to an interface; binding an entire collection of objects is supported as well. We'll also be looking at the binding modes. They allow us to specify how the data will flow (from source to target, target to source, or both). We'll finish this article series by looking at the support that Blend 4 provides to build applications that use data binding features. In the recipes of this article and the following one, we'll assume that we are building a simple banking application using Silverlight. Each of the recipes in this article will highlight a part of this application where the specific feature comes into play. The following screenshot shows the resulting Silverlight banking application: Displaying data in Silverlight applications When building Silverlight applications, we often need to display data to the end user. Applications such as an online store with a catalogue and a shopping cart, an online banking application and so on, need to display data of some sort. Silverlight contains a rich data binding platform that will help us to write data-driven applications faster and using less code. In this recipe, we'll build a form that displays the data of the owner of a bank account using data binding. Getting ready To follow along with this recipe, you can use the starter solution located in the Chapter02/ SilverlightBanking_Displaying_Data_Starter folder in the code bundle available on the Packt website. The finished application for this recipe can be found in the Chapter02/SilverlightBanking_Displaying_Data_Completed folder. How to do it... Let's assume that we are building a form, part of an online banking application, in which we can view the details of the owner of the account. Instead of wiring up the fields of the owner manually, we'll use data binding. To get data binding up and running, carry out the following steps: Open the starter solution, as outlined in the Getting Ready section. The form we are building will bind to data. Data in data binding is in-memory data, not the data that lives in a database (it can originate from a database though). The data to which we are binding is an instance of the Owner class. The following is the code for the class. Add this code in a new class fi le called Owner in the Silverlight project. public class Owner{public int OwnerId { get; set; }public string FirstName { get; set; }public string LastName { get; set; }public string Address { get; set; }public string ZipCode { get; set; }public string City { get; set; }public string State { get; set; }public string Country { get; set; }public DateTime BirthDate { get; set; }public DateTime CustomerSince { get; set; }public string ImageName { get; set; }public DateTime LastActivityDate { get; set; }public double CurrentBalance { get; set; }public double LastActivityAmount { get; set; }} Now that we've created the class, we are able to create an instance of it in the MainPage.xaml.cs file, the code-behind class of MainPage.xaml. In the constructor, we call the InitializeOwner method, which creates an instance of the Owner class and populates its properties. private Owner owner;public MainPage(){InitializeComponent();//initialize owner dataInitializeOwner();}private void InitializeOwner(){owner = new Owner();owner.OwnerId = 1234567;owner.FirstName = "John";owner.LastName = "Smith";owner.Address = "Oxford Street 24";owner.ZipCode = "W1A";owner.City = "London";owner.Country = "United Kingdom";owner.State = "NA";owner.ImageName = "man.jpg";owner.LastActivityAmount = 100;owner.LastActivityDate = DateTime.Today;owner.CurrentBalance = 1234.56;owner.BirthDate = new DateTime(1953, 6, 9);owner.CustomerSince = new DateTime(1999, 12, 20);} Let's now focus on the form itself and build its UI. For this sample, we're not making the data editable. So for every field of the Owner class, we'll use a TextBlock. To arrange the controls on the screen, we'll use a Grid called OwnerDetailsGrid. This Grid can be placed inside the LayoutRoot Grid.We will want the Text property of each TextBlock to be bound to a specifi c property of the Owner instance. This can be done by specifying this binding using the Binding "markup extension" on this property. <Grid x_Name="OwnerDetailsGrid"VerticalAlignment="Stretch"HorizontalAlignment="Left"Background="LightGray"Margin="3 5 0 0"Width="300" ><Grid.RowDefinitions><RowDefinition Height="100"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="30"></RowDefinition><RowDefinition Height="*"></RowDefinition></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition></ColumnDefinition><ColumnDefinition></ColumnDefinition></Grid.ColumnDefinitions><Image x_Name="OwnerImage"Grid.Row="0"Width="100"Height="100"Stretch="Uniform"HorizontalAlignment="Left"Margin="3"Source="/CustomerImages/man.jpg"Grid.ColumnSpan="2"></Image><TextBlock x_Name="OwnerIdTextBlock"Grid.Row="1"FontWeight="Bold"Margin="2"Text="Owner ID:"></TextBlock><TextBlock x_Name="FirstNameTextBlock"Grid.Row="2"FontWeight="Bold"Margin="2"Text="First name:"></TextBlock><TextBlock x_Name="LastNameTextBlock"Grid.Row="3"FontWeight="Bold"Margin="2"Text="Last name:"></TextBlock><TextBlock x_Name="AddressTextBlock"Grid.Row="4"FontWeight="Bold"Margin="2"Text="Adress:"></TextBlock><TextBlock x_Name="ZipCodeTextBlock"Grid.Row="5"FontWeight="Bold"Margin="2"Text="Zip code:"></TextBlock><TextBlock x_Name="CityTextBlock"Grid.Row="6"FontWeight="Bold"Margin="2"Text="City:"></TextBlock><TextBlock x_Name="StateTextBlock"Grid.Row="7"FontWeight="Bold"Margin="2"Text="State:"></TextBlock><TextBlock x_Name="CountryTextBlock"Grid.Row="8"FontWeight="Bold"Margin="2"Text="Country:"></TextBlock><TextBlock x_Name="BirthDateTextBlock"Grid.Row="9"FontWeight="Bold"Margin="2"Text="Birthdate:"></TextBlock><TextBlock x_Name="CustomerSinceTextBlock"Grid.Row="10"FontWeight="Bold"Margin="2"Text="Customer since:"></TextBlock><TextBlock x_Name="OwnerIdValueTextBlock"Grid.Row="1"Grid.Column="1"Margin="2"Text="{Binding OwnerId}"></TextBlock><TextBlock x_Name="FirstNameValueTextBlock"Grid.Row="2"Grid.Column="1"Margin="2"Text="{Binding FirstName}"></TextBlock><TextBlock x_Name="LastNameValueTextBlock"Grid.Row="3"Grid.Column="1"Margin="2"Text="{Binding LastName}"></TextBlock><TextBlock x_Name="AddressValueTextBlock"Grid.Row="4"Grid.Column="1"Margin="2"Text="{Binding Address}"></TextBlock><TextBlock x_Name="ZipCodeValueTextBlock"Grid.Row="5"Grid.Column="1"Margin="2"Text="{Binding ZipCode}"></TextBlock><TextBlock x_Name="CityValueTextBlock"Grid.Row="6"Grid.Column="1"Margin="2"Text="{Binding City}"></TextBlock><TextBlock x_Name="StateValueTextBlock"Grid.Row="7"Grid.Column="1"Margin="2"Text="{Binding State}"></TextBlock><TextBlock x_Name="CountryValueTextBlock"Grid.Row="8"Grid.Column="1"Margin="2"Text="{Binding Country}"></TextBlock><TextBlock x_Name="BirthDateValueTextBlock"Grid.Row="9"Grid.Column="1"Margin="2"Text="{Binding BirthDate}"></TextBlock><TextBlock x_Name="CustomerSinceValueTextBlock"Grid.Row="10"Grid.Column="1"Margin="2"Text="{Binding CustomerSince}"></TextBlock><Button x_Name="OwnerDetailsEditButton"Grid.Row="11"Grid.ColumnSpan="2"Margin="3"Content="Edit details..."HorizontalAlignment="Right"VerticalAlignment="Top"></Button><TextBlock x_Name="CurrentBalanceValueTextBlock"Grid.Row="12"Grid.Column="1"Margin="2"Text="{Binding CurrentBalance}" ></TextBlock><TextBlock x_Name="LastActivityDateValueTextBlock"Grid.Row="13"Grid.Column="1"Margin="2"Text="{Binding LastActivityDate}" ></TextBlock><TextBlock x_Name="LastActivityAmountValueTextBlock"Grid.Row="14"Grid.Column="1"Margin="2"Text="{Binding LastActivityAmount}" ></TextBlock></Grid> At this point, all the controls know what property they need to bind to. However, we haven't specifi ed the actual link. The controls don't know about the Owner instance we want them to bind to. Therefore, we can use DataContext. We specify the DataContext of the OwnerDetailsGrid to be the Owner instance. Each control within that container can then access the object and bind to its properties . Setting the DataContext in done using the following code: public MainPage(){InitializeComponent();//initialize owner dataInitializeOwner();OwnerDetailsGrid.DataContext = owner;} The result can be seen in the following screenshot:
Read more
  • 0
  • 0
  • 7072
Modal Close icon
Modal Close icon