Skip to content. | Skip to navigation

Personal tools
Log in
Sections
You are here: Home

Latest Plone Posts

Plone Conference 2014, Bristol: Schedule of Events

From Planet Plone. Published on Oct 01, 2014.

Everything you need to know to plan your trip from talks, to training, keynote speakers, events, and sponsors.

Is a Web Consolidation Strategy Right For You?

By Gabrielle Hendryx-Parker from Planet Plone. Published on Oct 01, 2014.

Isawebconsolidationstrategyrightforyou484.png

Thanks to open source CMSs like WordPress, Drupal and Plone, spawning new websites has never been so easy. Each organization's department, unit, center and group of people has been empowered to create their own web presence, sometimes out of frustration in the face of a slow corporate response.

This extraordinary and organic proliferation of website has helped organizations reach out to and engage their audiences better. But it has also come with ever growing tech headaches as IT departments are now faced with litters of unmaintained, out-of-date and disorganized standalone websites.

Fortunately, most enterprise CMSs already offer some level of multi-site management features thus there is no need to reinvent the wheel. So, is a web consolidation in order for your organization? Here are 7 key factors to review before making a decision:

1 - How many websites should you really be maintaining?

Is there still a valid need behind keeping all those websites, or is a major spring cleaning necessary? A detailed strategic inventory will need to be completed to assess the situation as you may not have as many sites to maintain as you think.

2 - How many different CMSs are you running?

And more importantly: is there a good reason why you should be running that many CMSs? Would it be possible to reduce the number of systems you maintain and standardize on a few, if not just one?

3 - How large are the websites you manage?

Most organizations have a plethora of tiny sites with a few large outliers. How many objects do those big sites contain? Assuming consolidation is an option, the benefits of a streamlined infrastructure will need to be carefully balanced with the reality of your hosting limitations.

4 - Are there groups of websites that have the same feature set?

Do they all need an event calendar? Do they all want to feature specific staff members? Do they all need the ability to manage online forms? Identifying clusters of websites based on functionalities is a first essential step toward any consolidation effort.

5 - How old are the various add-ons installed?

Have sites been patched regularly or are you sitting on a ticking bomb? This investigation will provide valuable information as to where to start with any consolidation effort and how soon it should be addressed.

6 - Would some websites benefit from sharing content?

For instance do many sites already cross link with one another? Do they point to the same information? Or worse, do they duplicate information? This is often true for event calendars, people directories and legal policies. When content synergies are identified, a consolidation will not only simplify web content management activities, but it will also greatly improve the effectiveness of marketing efforts.

7 - Finally, what is the hosting situation?

Now is the time to have an honest look at your operations: are the servers running perfectly or are you plagued with performance issues? Get the facts: you'll need to audit your hosting infrastructure and review server load, disk space, memory usage, traffic pattern, number of restarts, uptime statistics, etc. This will help you design the new and improved consolidated system.

In our experience, web consolidation projects provide an incredible opportunity for cutting expenses, improving performance, and simplifying content management across websites. For more real life examples, review our case studies...

College of the Liberal Arts at Penn State

College of Engineering at Notre Dame

Jury for Paragon announced, last week of nominations!

From Planet Plone. Published on Oct 01, 2014.

Meet the jury; and get your nominations in before monday!

A minimalist view for collections

By nguyen from Planet Plone. Published on Sep 30, 2014.

Plone 4.3

Using Custom Script Adapters with PloneFormGen forms to do a lot behind the scenes

By nguyen from Planet Plone. Published on Sep 30, 2014.

One nice thing you can do with PloneFormGen is present the user with a simple form that does a bunch more in the back end, using custom script adapters.

In these cases we wanted to give users one form to add a story to a web site, including a headline, text, photo, etc., and have the form's custom script adapter create the necessary folder, then the news item, the page, etc., and apply keywords/tags/categories as needed.

Here are the scripts we used with the forms.

Script 1

This script creates a News Item in a particular folder.

# Available parameters:
#  fields  = HTTP request form fields as key value pairs
#  request = The current HTTP request. 
#            Access fields by request.form["myfieldname"]
#  ploneformgen = PloneFormGen object
# 
# Return value is not processed -- unless you
# return a dictionary with contents. That's regarded
# as an error and will stop processing of actions
# and return the user to the form. Error dictionaries
# should be of the form {'field_id':'Error message'}

from Products.CMFPlone.utils import normalizeString


def suggestId(parent, title):
    return normalizeString(title)


title = request.form['entry-title']
image = request.form['entry-image_file']
imageDescription = request.form['entry-image-description']
storyDescription = request.form['entry-description']
story = request.form['story']
category = request.form['entry-category']

parent = context.aq_inner.aq_parent
grandparent = parent.aq_inner.aq_parent
entries = grandparent.get('all-entries')

id = suggestId(entries, title)
while hasattr(entries, id):
  id += "_1" # make it unique

newNewsItemId = entries.invokeFactory(id=id, type_name='News Item')
newNewsItem = entries[newNewsItemId]
newNewsItem.setTitle(title)
newNewsItem.setDescription(storyDescription)
newNewsItem.setImage(image)
newNewsItem.setImageCaption(imageDescription)
newNewsItem.setText(story)
newNewsItem.setSubject(category)
newNewsItem.reindexObject()

context.plone_utils.addPortalMessage("Created a news item with ID %s" % newNewsItemId, 'info')

portal_state = ploneformgen.restrictedTraverse('@@plone_portal_state')
url = portal_state.portal_url() + '/search'
request.response.redirect('%s?Creator=%s&sort_on=created&sort_order=reverse' % (url, portal_state.member().getId()))
 

 

Script 2

This one is a bit more complicated. It creates a folder that holds a News Item and a Document (Page).

# Available parameters:
#  fields  = HTTP request form fields as key value pairs
#  request = The current HTTP request. 
#            Access fields by request.form["myfieldname"]
#  ploneformgen = PloneFormGen object
# 
# Return value is not processed -- unless you
# return a dictionary with contents. That's regarded
# as an error and will stop processing of actions
# and return the user to the form. Error dictionaries
# should be of the form {'field_id':'Error message'}

from Products.CMFPlone.utils import normalizeString

def suggestId(parent, title):
   return normalizeString(title)

title = request.form['story-title']
image = request.form['main-image_file']
imageDescription = request.form['image-description']
storyDescription = request.form['story-description']
category = request.form['category']

stories = context.stories
defaultPageContent = stories['default-page-content'].getText()

id = suggestId(stories, title)
while hasattr(stories, id):
  id += "_1" # make it unique

newStoryFolderId = stories.invokeFactory(id=id, type_name='Folder')
newStoryFolder = stories[newStoryFolderId]
newStoryFolder.setSubject(category)
newStoryFolder.setTitle(title)
newStoryFolder.reindexObject()

id = suggestId(newStoryFolder, title)
id = "%s-1" % id
newStoryPageId = newStoryFolder.invokeFactory(id=id, type_name='Document')
newStoryPage = newStoryFolder[newStoryPageId]
newStoryPage.setTitle(title)
newStoryPage.setDescription(storyDescription)
newStoryPage.setSubject(category)
newStoryPage.setText(defaultPageContent)
newStoryPage.reindexObject()

id = suggestId(newStoryFolder, title)
id = "%s-2" % id
newNewsItemId = newStoryFolder.invokeFactory(id=id, type_name='News Item')
newNewsItem = newStoryFolder[newNewsItemId]
newNewsItem.setTitle(title)
newNewsItem.setDescription(storyDescription)
newNewsItem.setImage(image)
newNewsItem.setImageCaption(imageDescription)
newNewsItem.setSubject(category)
newNewsItem.reindexObject()

id = suggestId(newStoryFolder, title)
id = "%s-3" % id
newStoryImageId = newStoryFolder.invokeFactory(id=id, type_name='Image')
newStoryImage = newStoryFolder[newStoryImageId]
newStoryImage.setTitle(title)
newStoryImage.setImage(image)
newStoryImage.setDescription(imageDescription)
newStoryImage.setSubject(category)
newStoryImage.reindexObject()
 

 

 

How to export a collection of Dexterity objects to CSV

By nguyen from Planet Plone. Published on Sep 30, 2014.

oh External Methods, how I love thee

eGenix PyCon UK 2014 Talks & Videos

From Planet Plone. Published on Sep 29, 2014.

The PyCon UK Conference is the premier conference for Python users and developers in the UK. This year it was held from September 19-22 in Coventry, UK.

eGenix Talks at PyCon UK 2014

At this year's PyCon UK, Marc-André Lemburg, CEO of eGenix, gave the following talks at the conference. The presentations are available for viewing and download from our Presentations and Talks section.

When performance matters ...

Simple idioms you can use to make your Python code run faster and use less memory.

Python applications sometimes need all the performance they can get. Think of e.g. web, REST or RPC servers. There are several ways to address this: scale up by using more processes, use Cython, use PyPy, rewrite parts in C, etc.

However, there are also quite a few things that can be done directly in Python. This talk goes through a number of examples and show cases how sticking to a few idioms can easily enhance the performance of your existing applications without having to revert to more complex optimization strategies.

The talk was complemented with a lightning talk titled "Pythons and Flies", which addresses a memory performance idiom and answers one of the audience questions raised in the above talk.

Click to proceed to the PyCon UK 2014 talk and lightning talk videos and slides ...

Python Web Installer

Installing Python packages is usually done with one of the available package installation systems, e.g. pip, easy_install, zc.buildout, or manually by running "python setup.py install" in a package distribution directory.

These systems work fine as long as you have Python-only packages. For packages that contain binaries, such as Python C extensions or other platform dependent code, the situation is a lot less bright.

In this talk, we present a new web installer system that we're currently developing to overcome these limitations.

The system combines the dynamic Python installation interface supported by all installers ("python setup.py install"), with a web installer which automatically selects, downloads, verifies and installs the binary package for your platform.

Click to proceed to the PyCon 2014 talk video and slides ...

If you are interested in learning more about these idioms and techniques, eGenix now offers Python project coaching and consulting services to give your project teams advice on how to achieve best performance and efficiency with Python. Please contact our eGenix Sales Team for information.

Enjoy !

Charlie Clark, eGenix.com Sales & Marketing

Verslag Silicon Alley LIghtning Talks 26 september

By Kees Hink from Planet Plone. Published on Sep 29, 2014.

Afgelopen vrijdag vonden er Lightning Talks plaats, in samenwerking met onze buren van WebIQ.

Presenting Buildout at PySS 14

From Planet Plone. Published on Sep 27, 2014.

Buildout is a tool we use in all of the development and deployments of our applications, and we have given a talk about it at PySS 14.

Life at the Boundaries: Conversion and Validation

From Planet Plone. Published on Sep 26, 2014.

In software development we deal with boundaries between systems.

Examples of boundaries are:

  • Your application code and a database.
  • Your application code and the file system.
  • A web server and your server-side application code.
  • A client-side application and the browser DOM.
  • A client-side application in JavaScript and the web server.

It's important to recognize these boundaries. You want to do things at the boundaries of our application, just after input has arrived into your application across an outer boundary, and just before you send output across an inner boundary.

If you read a file and what's in that file is a string representing a number, you want to convert the string to a number as soon as possible after reading it, so that the rest of your codebase can forget about the file and the string in it, and just deal with the number.

Because if you don't and pass a filename around, you may have to open that file multiple times throughout your codebase. Or if you read from the file and leave the value as a string, you may have to convert it to a number each time you need it. This means duplicated code, and multiple places where things can go wrong. All that is more work, more error prone, and less fun.

Boundaries are our friends. So much so that programming languages give us tools like functions and classes to create new boundaries in software. With a solid, clear boundary in place in the middle of our software, both halves can be easier to understand and easier to manage.

One of the most interesting things that happen on the boundaries in software is conversion and validation of values. I find it very useful to have a clear understanding of these concepts during software development. To understand each other better it's useful to share this understanding out loud. So here is how I define these concepts and how I use them.

I hope this helps some of you see the boundaries more clearly.

Following a HTML form submit through boundaries

Let's look at an example of a value going across multiple boundaries in software. In this example, we have a web form with an input field that lets the user fill in their date of birth as a string in the format 'DD-MM-YYYY'.

I'm going to give examples based on web development. I also give a few tiny examples in Python. The web examples and Python used here only exist to illustrate concepts; similar ideas apply in other contexts. You shouldn't need to understand the details of the web or Python to understand this, so don't go away if you don't.

Serializing a web form to a request string

In a traditional non-HTML 5 HTTP web form, the input type for dates is text`. This means that the dates are in fact not interpreted by the browser as dates at all. It's just a string to the browser, just like adfdafd. The browser does not know anything about the value otherwise, unless it has loaded JavaScript code that checks whether it the input is really a date and shows an error message if it's not.

In HTML 5 there is a new input type called date, but for the sake of this discussion we will ignore it, as it doesn't change all that much in this example.

So when the user submits a form with the birth date field, the inputs in the form are serialized to a longer string that is then sent to the server as the body of a POST request. This serialization happens according to what's specified in the form tag's enctype attribute. When the enctype is multipart/form-data, the request to the server will be a string that looks a lot like this:

POST /some/path HTTP/1.1
Content-type: multipart/form-data, boundary=AaB03x

--AaB03x
content-disposition: form-data; name="birthdate"

21-10-1985
--AaB03x--

Note that this serialization of form input to the multipart/form-data format cannot fail; serialization always succeeds, no matter what form data was entered.

Converting the request string to a Request object

So now this request arrives at the web server. Let's imagine our web server is in Python, and that there's a web framework like Django or Flask or Pyramid or Morepath in place. This web framework takes the serialized HTTP request, that is, the string, and then converts it into a request object.

This request object is much more convenient to work with in Python than the HTTP request string. Instead of having one blob of a string, you can easily check indidivual aspects of the request -- what request method was used (POST), what path the request is for, what the body of the request was. The web framework also recognizes multipart/form-data and automatically converts the request body with the form data into a convenient Python dictionary-like data structure.

Note that the conversion of HTTP request text to request object may fail. This can happen when the client did not actually format the request correctly. The server should then return a HTTP error, in this case 400 Bad Request, so that the client software (or the developer working on the client software) knows something went wrong.

The potential that something goes wrong is one difference between conversion and serialization; both transform the data, but conversion can fail and serialization cannot. Or perhaps better said: if serialization fails it is a bug in the software, whereas conversion can fail due to bad input. This is because serialization goes from known-good data to some other format, whereas conversion deals with input data from an external source that may be wrong in some way.

Thanks to the web framework's parsing of web form into a Python data structure, we can easily get the field birthdate from our form. If the request object was implemented by the Webob library (like for Pyramid and Morepath), we can get it like this:

 >>> request.POST['birthdate']
'21-10-1985'

Converting the string to a date

But the birthdate at this point is still a string 21-10-1985. We now want to convert it into something more convenient to Python. Python has a datetime library with a date type, so we'd like to get one of those.

This conversion could be done automatically by a form framework -- these are very handy as you can declaratively describe what types of values you expect and the framework can then automatically convert incoming strings to convenient Python values accordingly. I've written a few web form frameworks in my time. But in this example we'll do it it manually, using functionality from the Python datetime library to parse the date:

>>> from datetime import datetime
>>> birthdate = datetime.strptime(request.POST['birthdate'], '%d-%m-%Y').date()
datetime.date(1985, 10, 21)

Since this is a conversion operation, it can fail if the user gave input that is not in the right format or is not a proper date Python will raise a ValueError exception in this case. We need to write code that detects this and then signal the HTTP client that there was a conversion error. The client needs to update its UI to inform the user of this problem. All this can get quite complicated, and here again a form framework can help you with this.

It's important to note that we should isolate this conversion to one place in our application: the boundary where the value comes in. We don't want to pass the birth date string around in our code and only convert it into a date when we need to do something with it that requires a date object. Doing conversion "just in time" like that has a lot of problems: code duplication is one of them, but even worse is that we would need worry about conversion errors everywhere instead of in one place.

Validating the date

So now that we have the birth date our web application may want to do some basic checking to see whether it makes sense. For example, we probably don't expect time travellers to fill in the form, so we can safely reject any birth dates set in the future as invalid.

We've already converted the birth date from a string into a convenient Python date object, so validating that the date is not in the future is now easy:

>>> from datetime import date
>>> birthdate <= date.today()
True

Validation needs the value to be in a convenient form, so validation happens after conversion. Validation does not transform the value; it only checks whether the value is valid according to additional criteria.

There are a lot of possible validations:

  • validate that required values are indeed present.
  • check that a value is in a certain range.
  • relate the value to another value elsewhere in the input or in the database. Perhaps the birth date is not supposed to be earlier than some database-defined value, for instance.
  • etc.

If the input passes validation, the code just continues on its merry way. Only when the validation fails do we want to take special action. The minimum action that should be taken is to reject the data and do nothing, but it could also involve sending information about the cause of the validation failure back to the user interface, just like for conversion errors.

Validation should be done just after conversion, at the boundary of the application, so that after that we can stop worrying about all this and just trust the values we have as valid. Our life is easier if we do validation early on like this.

Serialize the date into a database

Now the web application wants to store the birth date in a database. The database sits behind a boundary. This boundary may be clever and allow you to pass in straight Python date objects and do a conversion to its internal format afterward. That would be best.

But imagine our database is dumb and expects our dates to be in a string format. Now the task is up to our application: we need transform the date to a string before the database boundary.

Let's say the database layer expects date strings in the format 'YYYY-MM-DD'. We then have to serialize our Python date object to that format before we pass it into the database:

>>> birthdate.strftime('%Y-%m-%d')
'1985-10-21'

This is serialization and not conversion because this transformation always succeeds.

Concepts

So we have:

Transformation:
Transform data from one type to another. Transformation by itself cannot fail, as it is assumed to always get correct input. It is a bug in the software if it does not. Conversion and serialization both do transformation.
Conversion:
Transform input across a boundary into a more convenient form inside that boundary. Fails if the input cannot be transformed.
Serialization
Transform valid data as output across a boundary into a form convenient to outside. Cannot fail if there are no bugs in the software.
Validation:
Check whether input across a boundary that is already converted to convenient form is valid inside that boundary. Can fail. Does not transform.

Reuse

Conversion just deals with converting one value to another and does not interact with the rest of the universe. The implementation of a converter is therefore often reusable between applications.

The behavior of a converter typically does not depend on state or configuration. If conversion behavior does depend on application state, for instance because you want to parse dates as 'MM-DD-YYYY' instead of 'DD-MM-YYYY', it is often a better approach to just swap in a different converter based on the locale than to have the converter itself to be aware of the locale.

Validation is different. While some validations are reusable across applications, a lot of them will be application specific. Validation success may depend on the state of other values in the input or on application state. Reusable frameworks that help with validation are still useful, but they do need additional information from the application to do their work.

Serialization and parsing

Serialization is transformation of data to a particular type, such as a string or a memory buffer. These types are convenient for communicating across the boundary: storing on the file system, storing data in a database, or passing data through the network.

The opposite of serialization is deserialization and this is done by parsing: this takes data in its serialized form and transforms it into a more convenient form. Parsing can fail if its input is not correct. Parsing is therefore conversion, but not all conversion is parsing.

Parsing extracts information and checks whether the input conforms to a grammar in one step, though if you treat the parser as a black box you can view these as two separate phases: input validation and transformation.

There are transformation operations in an application that do not serialize but can also not fail. I don't have a separate word for these besides "transformation", but they are quite common. Take for instance an operation that takes a Python object and transforms it into a dictionary convenient for serialization to JSON: it can only consist of dicts, lists, strings, ints, floats, bools and None.

Some developers argue that data should always be kept in such a format instead of in objects, as it can encourage a looser coupling between subsystems. This idea is especially prevalent in Lisp-style homoiconic language communities, where even code is treated as data. It is interesting to note that JSON has made web development go in the direction of more explicit data structures as well. Perhaps it is as they say:

Whoever does not understand LISP is doomed to reinvent it.

Input validation

We can pick apart conversion and find input validation inside. Conversion does input validation before transformation, and serialization (and plain transformation) does not.

Input validation is very different from application-level validation. Input validation is conceptually done just before the convenient form is created, and is an inherent part of the conversion. In practice, a converter typically parses data, doing both in a single step.

I prefer to reserve the term "validation" for application-level validation and discuss input validation only when we talk about implementing a converter.

But sometimes conversion from one perspective is validation from another.

Take the example above where we want to store a Python date in a database. What if this operation does not work for all Python date objects? The database layer could accept dates in a different range than the one supported by the Python date object. The database may therefore may therefore be offered a date that is outside of its range and reject it with an error.

We can view this as conversion: the database converts a date value that comes in, and this conversion may fail. But we can also view this in another way: the database transforms the date value that comes in, and then there is an additional validation that may fail. The database is a black box and both perspectives work. That comes in handy a little bit later.

Validation and layers

Consider a web application with an application-level validation layer, and another layer of validation in the database.

Maybe the database also has a rule to make sure that the birth date is not in the future. It gives an error when we give a date in the future. Since validation errors can now occur at the database layer, we need to worry about properly handling them.

But transporting such a validation failure back to the user interface can be tricky: we are on the boundary between application code and database at this point, far from the boundary between application and user interface. And often database-level validation failure messages are in a form that is not very informative to a user; they speak in terms of the database instead of the user.

We can make our life easier. What we can do is duplicate any validation the database layer does at the outer boundary of our application, the one facing the web. Validation failures there are relatively simple to propagate back to the user interface. Since any validation errors that can be given by the database have already been detected at an earlier boundary before the database is ever reached, we don't need to worry about handling database-level validation messages anymore. We can act as if they don't exist, as we've now guaranteed they cannot occur.

We treat the database-level validation as an extra sanity check guarding against bugs in our application-level code. If validation errors occur on the database boundary, we have a bug, and this should not happen, and we can just report a general error: on the web this is a 500 internal server error. That's a lot easier to do.

The general principle is: if we do all validations that the boundary to a deeper layer already needs at a higher layer, we can effectively the inner boundary as not having any validations. The validations in the deeper layer then only exist as extra checks that guard against bugs in the validations at the outer boundary.

We can also apply this to conversion errors: if we already make sure we clean up the data with validations at an outer boundary before it reaches an inner boundary that needs to do conversions, the conversions cannot fail. We can treat them as transformations again. We can do this as in a black box we can treat any conversion as a combination of transformation and validation.

Validation in the browser

In the end, let's return to the web browser.

We've seen that doing validation at an outer boundary can let us ignore validation done deeper down in our code. We do validation once when values come into the web server, and we can forget about doing them in the rest of our server code.

We can go one step further. We can lift our validation out of the server, into the client. If we do our validation in JavaScript when the user inputs values into the web form, we are in the right place to give really accurate user interface feedback in easiest way possible. Validation failure information has to cross from JavaScript to the browser DOM and that's it. The server is not involved.

We cannot always do this. If our validation code needs information on the server that cannot be shared securily or efficiently with the client, the server is still involved in validation, but at least we can still do all the user interface work in the client.

Even if we do not need server-side validation for the user interface, we cannot ignore doing server-side validation altogether, as we cannot guarantee that our JavaScript program is the only program that sends information to the server. Through that route, or because of bugs in our JavaScript code, we can still get input that is potentially invalid. But now if the server detects invalid information, it does not need do anything complicated to report validation errors to the client. Instead it can just generate an internal server error.

If we could somehow guarantee that only our JavaScript program is the one that sends information to the server, we could forgo doing validation on the server altogether. Someone more experienced in the arts of encryption may be able to say whether this is possible. I suspect the answer will be "no", as it usually is with JavaScript in web browsers and encryption.

In any case, we may in fact want to encourage other programs to use the same web server; that's the whole idea behind offering HTTP APIs. If this is our aim, we need to handle validation on the server as well, and give decent error messages.

collective.angularstarter (Plone + AngularJS + Yeoman kickstarter project)

By davide moro (noreply@blogger.com) from Planet Plone. Published on Sep 26, 2014.

Get started with Plone + AngularJS without any of the normal headaches associated with a manual setup of useful tools that let you improve your development experience and the deploy of your application.

Since I have been using AngularJS on Plone, I decided to create a reusable starter scaffold (or something or similar) based on Yeoman that let me save precious time.

That's why I created:
This is a plugin that let you bootstrap single page web applications (or heavy Javascript logics) based on Plone+AngularJS+Yeoman.

Yeoman workflow benefits

collective.angularstarter is powered by the Yeoman workflow. If you want to see what are the Yeoman benefits due to an integration with a framework you might have a look at:
Next sections will talk about what you can build with Plone if you are not familiar with it, in particular heavily dynamic Javascript based verticalizations built with collective.angularstarter or similar techniques.

Plone

Plone is not only a CMS but a framework built with the Python programming language that let you build complex web applications, intranet, websites with strong focus on:
  • contents
  • security and sharing
  • workflow
  • searchability
You can extend the features provided by default by Plone thanks to a considerable number third party plugins.

You can see a couple of examples about you can build with Plone verticalizations with collective.angularstarter.

Coworking application

You can create custom content types for meeting rooms, private desks or common desks seats.

Basically the main object you are sharing is a folderish with metadata that let you configure the resource, for example:
  • image, title, description and rich text widget. You can describe the resource you are sharing
  • configure time slots configuration and hourly costs
  • configure time slots for group of hours with costs (ex: morning, afternoon)
  • daily cost or month cost, depending on the type of resource
  • number of available seats available in parallel
Once the resource has been published, users can buy the suitable time slots depending on the type of resource and availability.

For private or common desks you can choose to search for multiple seats for multiple days or months. For meeting rooms you can buy just one unit time slot, multiple hours, group of hours (ex: morning) or the full day.

For example if you are searching for a meeting room you can choose partial days mode and select the available day you want to select:



The infinite scrolling shows you only the days that fits what you are searching for (2 private desks)
One selected the day, you can choose one or more available slots (for example morning):
And then: buy it!
The reservation objects are based on event types, since the reservation has a start and end datetime. So you can easily perform non-expensive catalog queries in order to search for slots available.

The project itself it is more complex because Plone it is integrated with an external invoice management software, a Paypal interface that let you buy more credits and a personal user box that let the users to see invoice PDF files and other notifications.

Advanced search forms

You can also use plone as a backend for a highly dynamic single page web application.
 
You can mix Plone data with external resources provided by third party server in order to build a complex search form.

For example the main search prompt to the user a master-slave AJAX widget
where the vocabulary of slave selects depend on the value of the previous one): 

or search for different criteria:
In this particular case results will appear after you fill all the needed information, but it is quite easy to implement a live search.

If you need more info about how to create a master-select widget component with Angular you may have a look at this article http://davidemoro.blogspot.com/2014/09/angularjs-master-slave-select-with.html.

What collective.angularstarter is

The collective.angularstarter plugin is:
  1. a Plone + AngularJS kickstarter project. You can use this package when you want to develop a single page web applications powered by Angular using Plone as backend. With all the benefits of the Yeoman workflow
  2. scaffolding tool that let you extend this package, add more features and then clone it creating a more sophisticated application. You can redistribute it with another name. Or you can develop a rapid prototype of your reusable application and after create a new zopeskel or yo package generator with one or more options. The clone hack might fail in some corner case but it should help you to convert an existing package to another. Anyway if something goes wrong you can easily correct the problems by hand. I get used to apply a similar script when me or other colleagues chose a very ugly package name and then you have to rename it. Maurizio, remember?! How many days we saved with this script? 
Anyway when you install collective.angularstarter and visit the @@angularstarter browser view it shows an example of AngularJS app with enabled by default:
Here you can see how the @@angularstarter view looks like:
collective.angularstarter screenshot. Fill the input text and you'll see the page instantly updated
After that it's up to you coding with AngularJS and Plone!

Results

The following screenshot show you what happens if you analyze the network section of Firebug when you are in development mode:
or in production more:

Wait a moment! The resulting resultim bootstrap.css weights in at only 3,2 KB?! That's the power of minification and uncss tasks
A you can see you'll get (see the part 1 article of Pyramid starter seed project for further details about uncss and other tips explained):
  • html minified (experimental, disable for real project)
  • lighter images (no asset images in collective.angularstarter)
  • most popular Javascript resources automatically cdn-ified
  • css files concatenated, uncssed and minified
  • javascript concatenated and uglified
  • [update 20140926] assets automatically revved (avoid nasty caching problems)
  • ... you can do more installing additional grunt tasks
How did I did it? Basically I played with Plone's resource registrations and layers.  See https://github.com/collective/collective.angularstarter/blob/master/collective/angularstarter/browser/configure.zcml

collective.angularstarter wraps a modified Yeoman AngularJS project (browser/angular): asset paths modified, bower_components folder renamed and a couple and other local changes to the Gruntfile.js file.

Hope you'll find collective.angularstarter useful. Feedback will be very appreciated!

Pyramid starter seed template powered by Yeoman (part 1)

By davide moro (noreply@blogger.com) from Planet Plone. Published on Sep 26, 2014.

Book of the month I'm reading this summer: Pylons/Pyramid (http://docs.pylonsproject.org/en/latest).


Pyramid (http://www.pylonsproject.org) is a minimal Python-based web development framework that let you "start small and finish big".

It stole a lot of (good) ideas and concepts from other mature Python web frameworks and it is build with the pluggable and extensible concepts in mind. Read: no need to fork applications.

Furthermore Pyramid is database and template engine agnostic: you are free.

From the very beginning Pyramid allows you to become productive quickly. So why not start with something of useful?

Pyramid + Yeoman

The goal of this experiment is integrate yeoman with Pyramid (or other frameworks like NodeJs/Express with AngularJS or Plone as already did), preserving the yeoman's workflow.

UPDATE 20140926: here you can see a Plone + AngularJS + Yeoman article (collective.angularstarter)

In this article I'll talk about what are the benefits you get integrating your Pyramid app with Yeoman, in future posts I'll discuss how they work under the hood with additional technical details omitted here (each used component deserves an entire blog post).

Yeoman
You might wonder why? Because of the importance of tooling. Since it is very important build an effective developer tooling ecosystem, I want to integrate the simple starter demo app with commonly used tools to help you stay productive. So this simple application prototype it is just an experiment that should help you to integrate with modern web development tools provided by the yeoman workflow stack (http://yeoman.io).

Choosing the right tools is very important for the best develop experience and I cannot work anymore without Yeoman, especially when coding with Javascript.

Grunt
Yeoman it is internally based on three important components (nodejs powered):
  • yo, scaffolding tool like pcreate, paster or zopeskel. It is widely adopted by a large and trasversal community
  • grunt, system used for build, preview and test your software. Gulp is another popular option
  • bower, used for dependency management, so that you no longer have to manually download and manage your scripts
Bower

So with the yeoman's tools you can just code, avoid annoying repetitive tasks and don't worry about:
  • javascript testing setup
  • javascript code linting
  • javascript/css minification and merging
  • image minification
  • html minification
  • switch to CDN versions of you vendor plugins in production mode
  • auto-reload browser
  • much much more
So let's see together what happened to our pyramid starter demo template created with pcreate -t starter integrated with a yeoman's generator-webapp project.

The result will be a Pyramid starter seed project integrated with modern non Python-based web development tools.

Goals

Management of third party assets

You no longer have to manually download and manage your scripts with the Bower package manager.

From http://bower.io:
"""Bower works by fetching and installing packages from all over, taking care of hunting, finding, downloading, and saving the stuff you’re looking for."""
So just type something like: bower install angular-translate --save and you'll get the rigth resource with pinning support.

Tasks automation

Automation, automation, automation.

From http://gruntjs.com:
"""Why use a task runner? In one word: automation. The less work you have to do when performing repetitive tasks like minification, compilation, unit testing, linting, etc, the easier your job becomes. After you've configured it, a task runner can do most of that mundane work for you—and your team—with basically zero effort."""
Examples:
  • grunt serve
  • grunt test
  • grunt build
  • grunt YOUR TASK 
  • etc

Jslint

No more deploy Javascript code with bad indentation, syntax errors or bad code practices.

All syntax errors or bad practise will be found.

Image minification

The build process will detect and minify automatically all your asset images.

Uncss task

Modern (and heavy) UI frameworks like Twitter Bootstrap provide an excellent solution for prototyping your initial project, but most of the times you are using a very minimal subset of their functionalities.

https://twitter.com/davidemoroThis inspiring Addy Osmani's blog post helps you to remove unused css in your pages with a grunt task named grunt-uncss (https://github.com/addyosmani/grunt-uncss):
The original not-minified bootstrap.css weights in at 120 kB before removing unused rule.

Css concat and minification

You can split your css code into different files and then the build process will concat and minify them creating a unique app.css file. This way you write modular and better readable css files, reducing the number of browser requests.

The theme.css file is quite small but in real projects you can save more. In this case:
The configured build pipeline is concat, uncss and cssmin. 122.85 kB (original bootstrap.css) -> 4.64 kB (uncss) -> 3.45 kB (minification)

Automatic CDN-ification

It is handy using unminified versions of third party javascript libraries during development and switch to CDN versions in production mode with well known benefits for your website.

Don't worry: the cdnify task will take care about this boring issue. Automatically.

You save a boring manual and error-prone configuration.

Composable bootstrap.js version

The Pyramid starter project is based on Twitter Bootstrap.

Twitter Bootstrap
Depending on your project you can load the whole Twitter Bootstrap Javascript code at once or including individual plugins.

As you can see the Javascript component of Twitter Bootstrap is very modular: http://getbootstrap.com/javascript. So if you don't use a particular feature, just don't include it.

This way in development mode you will have all individual plugins splitted in different files, in production it will served a unique concatenated and minified Javascript file built automatically.

So if you just need alert.js and dropdown.js you can get a 2.79 kB plugins.js:

The concatenation of alert.js and dropdown.js produces a 7.06 kB, that weight in at 2.79 kB after minification instead of the 8.9 kB (gzipped) bootstrap-min.js corresponding to not gzipped 27.2 kB.

Html (template) minification

Since the ZPT/Chameleon templating language is an extension of HTML with xml syntax,

Brower are able to display unrendered ZPT/Chameleon templates
theorically it can play well with html minificators.

I know, template minification can lead to potential unexpected problems due to minification issues on template files... but this is my personal playground, so let me play please!

So... why not switch to a pre-compiled minified template of your ZPT/Chameleon files when you are in "production mode"?

Obviously during development you will use the original template files.

The interesting side of this approach is that there is no overhead at response time, since the minification task runs just one time before deploying your application. It might be an option if you want just your html minified and you cannot feasibly add to your site or project additional optimization tools at web server level.

Anyway I have tried this mad experiment and... if you don't use too aggressive minification params, it seems to work fine with good results. Try it at your own risk or just disable it. Here you can the effects on the generated index.html used in production:
Template minified (7.62 kB -> 4.16 kB)

Result: a lighter Pyramid

Same results but a lighter Pyramid app:

Let's see how it behave the standard Pyramid starter project:
Standard Pyramid starter project (production.ini)
And the Pyramid starter seed:
Pyramid starter seed (production.ini)
As you can see the seed version is ~38 Kb smaller and more performant.

Useful links

That's all?

No, you can do more, for example:
  • reduce the  number or requests (for example you can merge vendor.css and app.css)
  • create and keep updated css sprites with grunt (https://github.com/Ensighten/grunt-spritesmith)
  • manage and upload all your assets to professional services like Amazon AWS (for example you can serve up all your images, styles and scripts from a S3 bucket + CloudFront). This way Pyramid will be able to handle more requests. Pyramid let you put static media on a separate webserver during production with static_url() in conjunction with add_static_view(), without having to change your templates code
  • generate static gzipped assets with Grunt and let your webserver serve them
  • install and configure dedicated performance modules at webserver level (Apache's mod_pagespeed)
Let me know what you think about that, please. Hope soon I will manage to write the second part of this blog post explaining how I did it. In the mean time you can:

Links

    Avoiding load average spike alerts on Munin monitoring

    By Mikko Ohtamaa from Planet Plone. Published on Sep 24, 2014.

    Munin is a server monitoring tool written in Perl. In this post I’ll introduce some monitoring basics and how to avoid unnecessary monitoring alerts on temporary server conditions. Munin has vibrant plugin community. You can easily write your own plugins, … Continue reading

    Pyramid starter seed template powered by Yeoman (part 2)

    By davide moro (noreply@blogger.com) from Planet Plone. Published on Sep 24, 2014.

    In the previous blog post we have seen what are the benefits of using the Yeoman workflow fully integrated with a web development framework like Pyramid. See:

    Now we'll add more technical details about:
    • how to install pyramid_starter_seed and its prerequisites

    How to install pyramid_starter_seed

      Prerequisites

      As you can imagine, nodejs tools are required.

      I strongly suggest to:
      • avoid system packages because they are too old
      • install nodejs with nvm (Node Version Manager)
      I won't cover the nodejs installation but it is quite simple if you follow these instructions provided by this useful link:
      The nvm achronym stands for NodeJS Version Manager. Once installed nvm, installing nodejs it is as simple as typing nvm install VERSION (at this time of writing 0.10.32).

      Nodejs is shipped with the command line utility named npm (Nodejs Package Manager) and we will use npm for installing what we need.

      We need to install our global (-g option) dev dependencies, so just type:
      $ npm install -g bower
      $ npm install -g grunt-cli
      $ npm install -g karma

      Starter seed project installation

      Create an isolated Python environment as explained in the official Pyramid documentation and instal Pyramid.

      Once installed you can clone pyramid_starter_seed from github:
      $ git clone git@github.com:davidemoro/pyramid_starter_seed.git
      $ cd pyramid_starter_seed
      $ YOUR_VIRTUALENV_PYTHON_PATH/bin/python setup.py develop
      Not finished yet, continue.

      Yeoman initialization

      Go to the folder where it lives our Yeoman project and initialize it.

      These are the standard commands (but, wait a moment, see the "Notes and known issues" subsection):
      $ cd pyramid_starter_seed/webapp
      $ bower install
      $ npm install --loglevel verbose
      Known issues:
      • if you are behind a proxy you'll have to configure properly npm
      • if you have a slow internet connection you might experience timeout problems.

      Build phase

      Just type:
      $ grunt
      and... probably it will fail because of a couple of known issues shipped with the latest version of generator-webapp or its dependencies.

      Probably these issues will be fixed in newer generator-webapp releases. However here it is how to solve these problems, so don't worry:
      1. grunt-contrib-imagemin fix
        Problem with grunt:
        Warning: Running "imagemin:dist" (imagemin) task
        Warning: Bad argument Use --force to continue.
        Solution:
        $ npm cache clean
        $ rm -fR node_modules       # not sure it is needed, don't remember
        $ npm install grunt-contrib-imagemin
      2. Mocha/PhantomJS issue
      Problem with Mocha/PhantomJS launching grunt
      Warning: PhantomJS timed out, possibly due to a missing Mocha run() call. Use --force to continue.
      Solution:
      $ cd test
      $ bower install
      Run bower install in the test directory of webapp (pyramid_starter_seed/webapp/test). This is a known issue, see https://github.com/yeoman/generator-webapp/issues/446.

      Run you Pyramid app

      Now can choose to run Pyramid in development or production mode.
      Just type:
      $ YOUR_VIRTUALENV_PYTHON_PATH/bin/pserve development.ini
      or:
      $ YOUR_VIRTUALENV_PYTHON_PATH/bin/pserve production.ini
      Done!


      In the next blog post with topic Pyramid + Yeoman (coming soon) I'm going to talk about:
      • how to manage things with grunt and personalize pyramid_starter_seed registering other assets
      • how to clone pyramid_starter_seed. Yes, you can easily customize it creating something of more sophisticated and create your own starter seed with another name. Without having to write a package generator
       So stay tuned..

      Links