From: Benoît Pin <benoit.pin@gmail.com>
Date: Sun, 14 Nov 2010 21:28:02 +0000 (+0100)
Subject: Le thème créé pour realis est reconverti en thème « photo » pour Plinn core.
X-Git-Url: https://scm.cri.minesparis.psl.eu/git/Plinn.git/commitdiff_plain/de068d5b8a1fd46a70f75f4e20e581ee846f866d?ds=sidebyside

Le thème créé pour realis est reconverti en thème « photo » pour Plinn core.
---

diff --git a/directories.zcml b/directories.zcml
index 714ce40..1c12729 100644
--- a/directories.zcml
+++ b/directories.zcml
@@ -1,6 +1,7 @@
 <configure
     xmlns="http://namespaces.zope.org/zope"
     xmlns:cmf="http://namespaces.zope.org/cmf">
+  <cmf:registerDirectory name="photo_theme" recursive="True"/>
   <cmf:registerDirectory name="content" recursive="True"/>
   <cmf:registerDirectory name="control" recursive="True"/>
   <cmf:registerDirectory name="generic" recursive="True"/>
diff --git a/profiles/photo/skins.xml b/profiles/photo/skins.xml
index 1ca266e..4ab03bc 100644
--- a/profiles/photo/skins.xml
+++ b/profiles/photo/skins.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
 <object name="portal_skins" meta_type="CMF Skins Tool" allow_any="False"
-   cookie_persistence="False" default_skin="Realis"
+   cookie_persistence="False" default_skin="Plinn photo"
    request_varname="portal_skin">
  <object name="Images" meta_type="Filesystem Directory View"
     directory="Products.CMFDefault:skins/Images"/>
@@ -45,6 +45,8 @@
     directory="Products.Plinn:skins/generic"/>
  <object name="plinn_images" meta_type="Filesystem Directory View"
     directory="Products.Plinn:skins/images"/>
+ <object name="plinn_photo_theme" meta_type="Filesystem Directory View"
+    directory="Products.Plinn:skins/photo_theme"/>
  <object name="portfolio" meta_type="Filesystem Directory View"
     directory="Products.Portfolio:skins"/>
  <object name="realis" meta_type="Filesystem Directory View"
@@ -59,9 +61,9 @@
     directory="Products.CMFDefault:skins/zpt_generic"/>
  <object name="zpt_topic" meta_type="Filesystem Directory View"
     directory="Products.CMFTopic:skins/zpt_topic"/>
- <skin-path name="Realis">
+ <skin-path name="Plinn photo">
   <layer name="custom"/>
-  <layer name="realis"/>
+  <layer name="plinn_photo_theme"/>
   <layer name="photoprint"/>
   <layer name="plinn_content"/>
   <layer name="plinn_control"/>
diff --git a/skins/photo_theme/base_properties.props b/skins/photo_theme/base_properties.props
new file mode 100755
index 0000000..a32b6ea
--- /dev/null
+++ b/skins/photo_theme/base_properties.props
@@ -0,0 +1,65 @@
+title:string=Color, font, logo and border defaults
+
+backgroundColor:string=#484848
+fontColor:string=#9da097
+fontFamily:string=Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif
+fontBaseSize:string=90%
+headingFontFamily:string=Arial, Helvetica, Geneva, Swiss, SunSans-Regular, sans-serif
+
+linkColor:string=#e6eadd
+linkHoverColor:string=#f28c18
+
+fontSmallSize:string=90%
+
+
+topbarBackgroundColor:string=#000
+breadcrumbsBackground:string=#484848
+
+tabsBorderColor:string=#202020
+tabsBorderWidth:string=2px
+tabsBottomBorderSelectedWidth:string=3px
+tabsFontColor:string=#e6eadd
+tabsBackgroundColor:string=#484848
+selectedTabBackgroundColor:string=#484848
+
+descriptionColor:string=#9da097
+
+inputFontColor:string=#000
+
+evenRowBackgroundColor:string=#202020
+oddRowBackgroundColor:string=#484848
+highLightColor:string=#f28c18
+
+messageBackgroundColor:string=#484848
+messageBorderColor:string=#484848
+
+weekTableFontHeight:int=10
+weekTableGridStep:int=10
+todayBackgroundColor:string=#f0f0f0
+calGridBorderColor:string=#ccc
+calHalfGridBorderColor:string=#e5e5e5
+
+contentBackgroundColor:string=#484848
+
+configFontColor:string=#fff
+configBackgroundColor:string=#3e3e3e
+
+portfolioSlideBackgroundColor:string=#f2f2f2
+
+memberActionsBoxBackground:string=#484848
+footerBackgroundColor:string=#707070
+outlineColor:string=#F28C18
+bylineBackgroundColor:string=#202020
+messageFontColor:string=#F28C18
+listingHeaderBackgroundColor:string=#000
+listingHeaderFontColor:string=#ddd
+listingBarBorderColor:string=#202020
+newsTitleBorderColor:string=#ddd
+boxesBorderColor:string=#ddd
+specialLinkColor:string=#e6eadd
+specialLinkHoverColor:string=#f28c18
+bulletColor:string=#ef8e32
+boxeHeaderBackgroundColor:string=#484848
+
+twoColumnFormFontColor:string=#fff
+twoColumnFormBackground:string=#3A3A3A
diff --git a/skins/photo_theme/collapsePalette.gif b/skins/photo_theme/collapsePalette.gif
new file mode 100644
index 0000000..2777170
Binary files /dev/null and b/skins/photo_theme/collapsePalette.gif differ
diff --git a/skins/photo_theme/collapsedPalette.gif b/skins/photo_theme/collapsedPalette.gif
new file mode 100644
index 0000000..17caf53
Binary files /dev/null and b/skins/photo_theme/collapsedPalette.gif differ
diff --git a/skins/photo_theme/createMemberContent.py b/skins/photo_theme/createMemberContent.py
new file mode 100755
index 0000000..0256a30
--- /dev/null
+++ b/skins/photo_theme/createMemberContent.py
@@ -0,0 +1,2 @@
+##parameters=member,member_id,member_folder
+return None
\ No newline at end of file
diff --git a/skins/photo_theme/customer_join_form.py b/skins/photo_theme/customer_join_form.py
new file mode 100755
index 0000000..a1e3111
--- /dev/null
+++ b/skins/photo_theme/customer_join_form.py
@@ -0,0 +1,61 @@
+##parameters=add=''
+from Products.CMFCore.utils import getToolByName
+from Products.realis.utils import translate
+from ZTUtils import make_query as mq
+_ = lambda msg : translate(msg, context)
+ptool = getToolByName(script, 'portal_properties')
+atool = getToolByName(script, 'portal_actions')
+validate_email = ptool.getProperty('validate_email')
+options = {}
+options['validate_email'] = validate_email
+
+req = context.REQUEST
+resp = req.RESPONSE
+form = req.form
+fg = lambda name : form.get(name,'').strip()
+
+
+if add and \
+	context.validatePassword(**form) and \
+	context.customer_add_control(**form) and \
+	context.validatePrivateAccess(**form) :
+	came_from = fg('came_from')
+	if came_from :
+		return context.setRedirect(	atool, 'user/logged_in'
+								  , came_from = came_from
+								  , __ac_name=fg('member_id')
+								  , __ac_password=fg('password'))
+		#return resp.redirect('%s?%s' % ( came_from, mq(__ac_name=fg('member_id'), __ac_password=fg('password'), noajax='1')) )
+	else:
+		options['member_id'] = fg('member_id')
+		options['password'] = fg('password')
+		options['form_action'] = target = atool.getActionInfo('user/logged_in')['url']
+		return context.confirm_join_template(**options)
+
+continuationFields = [
+	  'given_name'
+	, 'name'
+	, 'member_email'
+	, 'member_id'
+	, 'password'
+	, 'confirm'
+	, 'send_password'
+	, 'wedding_id'
+	, 'wedding_password'
+	, 'wedding_password_confirm'
+	, 'billing_address'
+	, 'billing_city'
+	, 'billing_zipcode'
+	, 'country'
+	, 'phone'
+	, 'accept_gcs']
+
+
+for name in continuationFields :
+	options[name] = fg(name)
+
+# TODO try to be more clever...
+if not options['country']:
+	options['country'] = 'FR'
+options['came_from'] = fg('came_from')
+return context.customer_join_template(**options)
diff --git a/skins/photo_theme/customer_join_template.pt b/skins/photo_theme/customer_join_template.pt
new file mode 100644
index 0000000..5c2a95c
--- /dev/null
+++ b/skins/photo_theme/customer_join_template.pt
@@ -0,0 +1,159 @@
+<html metal:use-macro="context/main_template/macros/master">
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
+  </head>
+  <body metal:fill-slot="main_no_tabs" i18n:domain="realis"
+        tal:omit-tag="">
+    <div tal:condition="python:options.get('came_from', '').endswith('/my_cart')"
+         tal:define="step_authentication python:True" tal:omit-tag="">
+      <div metal:use-macro="here/sell_macros/macros/sell_steps"></div>
+    </div>
+    <h1 i18n:translate="">New customer account</h1>
+    
+    <form tal:attributes="action string:portal_url/customer_join_form" method="post">
+      <table class="TwoColumnForm">
+        <tr>
+          <td colspan="2" style="text-align:center">
+            <h3 i18n:translate="">Customer informations</h3>
+          </td>
+        </tr>
+        <tr>
+          <th i18n:translate="">First name</th>
+          <td>
+            <input type="text" name="given_name" size="30" value="" tal:attributes="value options/given_name" />
+          </td>
+        </tr>
+        <tr>
+          <th i18n:translate="">Last name</th>
+          <td>
+            <input type="text" name="name" size="30" value="" tal:attributes="value options/name" />
+          </td>
+        </tr>
+        <tr>
+          <th i18n:translate="">Email Address</th>
+          <td>
+            <input type="text" name="member_email" size="30"
+                   tal:attributes="value options/member_email" />
+          </td>
+        </tr>
+        <tr>
+         <th i18n:translate="">Member ID</th>
+         <td>
+          <input type="text" name="member_id" size="30" value=""
+                 tal:attributes="value options/member_id" />
+         </td>
+        </tr>
+        <tal:case tal:condition="not: options/validate_email">
+          <tr>
+            <th i18n:translate="">Password</th>
+            <td>
+              <input type="password" name="password" size="30" tal:attributes="value options/password" />
+            </td>
+          </tr>
+          <tr>
+            <th i18n:translate="">Password (confirm)</th>
+            <td>
+              <input type="password" name="confirm" size="30" tal:attributes="value options/confirm" />
+            </td>
+          </tr>
+          <tr>
+            <th i18n:translate="">Mail Password?</th>
+            <td>
+              <input type="checkbox" name="send_password" id="cb_send_password"
+                     tal:attributes="checked options/send_password" />
+              <em><label for="cb_send_password" i18n:translate="">Check this box to
+                have the password mailed.</label></em>
+            </td>
+          </tr>
+        </tal:case>
+        <tr>
+          <td colspan="2"><hr/></td>
+        </tr>
+        <tr>
+          <td colspan="2" style="text-align:center">
+            <h3>Accès à des images privées</h3>
+            veuillez entrer le code d'accès pour accéder / acheter les images<br/>
+            d'un événement privé couvert par les photographes de notre agence.
+          </td>
+        </tr>
+        <tr>
+          <th>Identifiant collection privée</th>
+          <td>
+            <input type="text" name="wedding_id" tal:attributes="value options/wedding_id"/>
+          </td>
+        </tr>
+        <tr>
+          <th>Mot de passe associé</th>
+          <td>
+            <input type="password" name="wedding_password" tal:attributes="value options/wedding_password" />
+          </td>
+        </tr>
+        <tr>
+          <th>Confirmation du mot de passe</th>
+          <td>
+            <input type="password" name="wedding_password_confirm" tal:attributes="value options/wedding_password_confirm" />
+          </td>
+        </tr>
+        <tr>
+          <td colspan="2"><hr/></td>
+        </tr>
+        <tr>
+          <td colspan="2" style="text-align:center">
+            <h3 i18n:translate="">Billing informations</h3>
+          </td>
+        </tr>
+        <tr>
+          <th i18n:translate="">Address</th>
+          <td>
+            <textarea name="billing_address" tal:content="options/billing_address"
+                      cols="30" rows="1" style="width:auto"></textarea>
+          </td>
+        </tr>
+        <tr>
+          <th i18n:translate="">City</th>
+          <td>
+            <input type="text" name="billing_city" size="35" tal:attributes="value options/billing_city"/>
+          </td>
+        </tr>
+        <tr>
+          <th i18n:translate="">Zip code</th>
+          <td>
+            <input type="text" name="billing_zipcode" size="5" tal:attributes="value options/billing_zipcode"/>
+          </td>
+        </tr>
+        <tr>
+          <th i18n:translate="">Country</th>
+          <td>
+            <select name="country"
+                    tal:define="countries python:modules['Products.iso_3166_1'].fr.countries"
+                    i18n:domain="iso_3166_1">
+              <option tal:repeat="c countries" tal:attributes="value python:c[0]; selected python:c[0]==options['country']" tal:content="python:c[0]" i18n:translate=""></option>
+            </select>
+          </td>
+        </tr>
+        <tr>
+          <th i18n:translate="">Phone</th>
+          <td>
+            <input type="text" name="phone" tal:attributes="value options/phone"/>
+          </td>
+        </tr>
+        <tr>
+          <th><br/></th>
+          <td>
+            <input type="checkbox" name="accept_gcs" tal:attributes="checked python:options['accept_gcs']" />
+            <a tal:attributes="href string:$portal_url/cgv" target="_blank" i18n:translate="">I accept general conditions of sales</a>
+          </td>
+        </tr>
+        <tr>
+          <td>&nbsp;</td>
+          <td>
+            <input type="hidden" name="noAjax" value="1"/>
+            <input type="hidden" name="came_from" tal:condition="options/came_from" tal:attributes="value options/came_from" />
+            <input type="submit" name="add" value="Register" i18n:attributes="value"/>
+            <input type="submit" name="cancle" value="Cancel" i18n:attributes="value" style="margin-left:15em"/>
+          </td>
+        </tr>
+      </table>
+    </form>
+  </body>
+</html>
diff --git a/skins/photo_theme/doFormSearch.py b/skins/photo_theme/doFormSearch.py
new file mode 100755
index 0000000..d917835
--- /dev/null
+++ b/skins/photo_theme/doFormSearch.py
@@ -0,0 +1,45 @@
+## Script (Python) "doFormSearch"
+##parameters=REQUEST
+##title=Pre-process form variables, then return catalog query results.
+##
+vars = REQUEST.form
+form_vars = {}
+skip_vars = ['strCreator']
+select_vars = ( 'review_state'
+			  , 'Subject'
+			  , 'portal_type'
+			  )
+date_vars = ('created',
+			 )
+epoch = DateTime("1970/01/01 00:00:00 GMT")
+
+for k, v in vars.items():
+
+	if k in select_vars:
+		if same_type( v, [] ):
+			v = filter( None, v )
+		if not v:
+			continue
+
+	if k in date_vars:
+		if v == epoch and vars.get(k+'_usage') == 'range:min':
+			skip_vars.append(k+'_usage')
+			continue
+
+	form_vars[ k ] = v
+
+if form_vars.has_key('portal_type') :
+	try : form_vars['portal_type'].remove('Member Data')
+	except ValueError : pass
+else :
+	form_vars['portal_type'] = context.portal_types.objectIds()
+
+for k in skip_vars:
+	if form_vars.has_key(k) :
+		del form_vars[k]
+
+if vars.has_key('strCreator') :
+	form_vars['Creator'] = [ m.id for m in context.portal_membership.looseSearchMembers(vars['strCreator']) ]
+
+results = context.portal_catalog( form_vars )
+return {'results':results, 'searchArgs':form_vars}
diff --git a/skins/photo_theme/expandPalette.gif b/skins/photo_theme/expandPalette.gif
new file mode 100644
index 0000000..cb52874
Binary files /dev/null and b/skins/photo_theme/expandPalette.gif differ
diff --git a/skins/photo_theme/expandedPalette.gif b/skins/photo_theme/expandedPalette.gif
new file mode 100644
index 0000000..9b4f5a0
Binary files /dev/null and b/skins/photo_theme/expandedPalette.gif differ
diff --git a/skins/photo_theme/folder_contents.py b/skins/photo_theme/folder_contents.py
new file mode 100755
index 0000000..e0bc4d8
--- /dev/null
+++ b/skins/photo_theme/folder_contents.py
@@ -0,0 +1,232 @@
+##parameters=b_start=0, key='', reverse=0, ids=(), items_copy='', items_cut='', items_delete='', items_new='', items_paste='', items_rename='', items_up='', items_down='', items_top='', items_bottom='', items_sort='', template='', macro='', ajax=''
+##
+from Products.Plinn.PloneMisc import Batch
+from DateTime import DateTime
+locale_date_fmt = context.locale_date_fmt()
+from ZTUtils import make_query
+from Products.CMFCore.utils import getToolByName
+from Products.CMFDefault.permissions import AddPortalContent
+from Products.CMFDefault.permissions import DeleteObjects
+from Products.CMFDefault.permissions import ListFolderContents
+from Products.CMFDefault.permissions import ManageProperties
+from Products.CMFDefault.permissions import ViewManagementScreens
+from Products.CMFDefault.permissions import ModifyPortalContent
+from Products.CMFDefault.utils import html_marshal
+
+mtool = getToolByName(script, 'portal_membership')
+checkPermission = mtool.checkPermission
+utool = getToolByName(script, 'portal_url')
+portal_url = utool()
+
+
+form = context.REQUEST.form
+default_target = 'object/folderContents'
+default_kw = {'b_start': b_start, 'key': key, 'reverse': reverse, 'ajax' : ajax}
+if items_copy :
+	if ajax : default_kw['syncFragments']=['rightCell']
+	if context.validateItemIds(**form) and \
+			context.folder_copy_control(**form) and \
+			context.setRedirect(context, default_target, **default_kw):
+		return
+elif items_cut :
+	if ajax : default_kw['syncFragments']=['rightCell']
+	if context.validateItemIds(**form) and \
+			context.folder_cut_control(**form) and \
+			context.setRedirect(context, default_target, **default_kw):
+			return
+elif items_delete and \
+		context.validateItemIds(**form) and \
+		context.folder_delete_control(**form) and \
+		context.setRedirect(context, default_target, **default_kw):
+	return
+elif items_new and \
+		context.setRedirect(context, 'object/new', **default_kw):
+	return
+elif items_paste :
+	if ajax : default_kw['syncFragments']=['rightCell']
+	if context.folder_paste_control(**form) and \
+			context.setRedirect(context, default_target, **default_kw):
+			return
+elif items_rename and \
+		context.validateItemIds(**form) and \
+		context.setRedirect(context, 'object/rename_items', ids=ids,
+							**default_kw):
+	return
+elif items_sort and \
+		context.folder_sort_control(**form) and \
+		context.setRedirect(context, default_target, b_start=b_start):
+	return
+elif items_up and \
+		context.validateItemIds(**form) and \
+		context.folder_up_control(**form) and \
+		context.setRedirect(context, default_target, **default_kw):
+	return
+elif items_down and \
+		context.validateItemIds(**form) and \
+		context.folder_down_control(**form) and \
+		context.setRedirect(context, default_target, **default_kw):
+	return
+elif items_top and \
+		context.validateItemIds(**form) and \
+		context.folder_top_control(**form) and \
+		context.setRedirect(context, default_target, **default_kw):
+	return
+elif items_bottom and \
+		context.validateItemIds(**form) and \
+		context.folder_bottom_control(**form) and \
+		context.setRedirect(context, default_target, **default_kw):
+	return
+	
+
+options = {}
+
+items_add_allowed = checkPermission(AddPortalContent, context)
+upitems_list_allowed = checkPermission(ListFolderContents, context, 'aq_parent')
+manage_props_allowed = checkPermission(ManageProperties, context)
+
+target = context.getActionInfo(default_target)['url']
+
+if not key:
+	(key, reverse) = context.getDefaultSorting()
+	is_default = 1
+elif (key, reverse) == context.getDefaultSorting():
+	is_default = 1
+else:
+	is_default = 0
+
+columns = ( {'key': 'Lock',
+			 'title': '',
+			 'width': '16',
+			 'colspan': None}
+			, {'key': 'Type',
+			 'title': 'Type',
+			 'width': None,
+			 'colspan': '2'}
+			, {'key': 'id',
+			 'title': 'Name',
+			 'width': None,
+			 'colspan': None}
+			, {'key': 'modified',
+			 'title': 'Last Modified',
+			 'width': None,
+			 'colspan': None}
+			, {'key': 'position',
+			 'title': 'Position',
+			 'width': None,
+			 'colspan': None }
+			)
+
+for column in columns:	
+	images = []
+	if key == column['key'] :
+		if not is_default and manage_props_allowed :
+			images.append( {'src' : getattr(context, 'set_default_sorting.gif').absolute_url(),
+							'alt' : 'Set Sorting as Default',
+							'id'	 : 'SetSortingAsDefault',
+							'href': '%s?%s' % (target, make_query(items_sort=True,
+																  key=key,
+																  reverse= (key != 'position' and [reverse] or [False])[0] )
+												  )
+							 }
+							 )
+
+		if key != 'position' :
+			if reverse :
+				toggleImg = getattr(context, 'arrowDown.gif')
+				alt = 'descending sort'
+			else :
+				toggleImg = getattr(context, 'arrowUp.gif')
+				alt = 'ascending sort'
+			query = make_query(key=column['key'], reverse = not reverse)
+		else :	
+			toggleImg = getattr(context, 'arrowUp.gif')
+			alt = 'ascending sort'
+			query = make_query(key=column['key'])
+		images.append( {'src' : toggleImg.absolute_url(), 'alt' : alt} )
+	else :
+		if key != 'position' :
+			query = make_query(key=column['key'], reverse = reverse)
+		else :
+			query = make_query(key=column['key'])
+	
+	column['url'] = '%s?%s' % (target, query)
+	column['images'] = images
+
+context.filterCookie()
+folderfilter = context.REQUEST.get('folderfilter', '')
+filter = context.decodeFolderFilter(folderfilter)
+items = context.listCatalogedContents(contentFilter=filter)
+sort_dir = reverse and 'desc' or 'asc'
+sortFunc = key in ['Type'] and 'nocase' or 'cmp'
+items = sequence.sort( items, ((key, sortFunc, sort_dir),) )
+batch_obj = Batch(items, context.default_batch_size, b_start, orphan=0, quantumleap=1)
+items = []
+i = 1
+display_delete_button = True # TODO : à revoirs
+for item in batch_obj:
+	item_icon = item.getIcon
+	item_id = item.getId
+	item_position = key == 'position' and str(b_start + i) or '...'
+	i += 1
+	item_url = item.getURL()
+	#try : item_delete_allowed = context.objectIdCanBeDeleted(item_id)
+	#except : item_delete_allowed = checkPermission(DeleteObjects, context) # std zope perm
+	#if not display_delete_button :
+	#	display_delete_button = item_delete_allowed
+	items.append(
+		{'lock' : False,
+		 'checkbox': True,
+		 'icon': item_icon and ( '%s/%s' % (portal_url, item_icon) ) or '',
+		 'id': item_id,
+		 'modified': item.modified.strftime(locale_date_fmt),
+		 'position': item_position,
+		 'title_or_id': item.Title or item_id,
+		 'type': item.Type or None,
+		 'url': item_url } )
+
+options['batch'] = { 'listColumnInfos': tuple(columns),
+					 'listItemInfos': tuple(items),
+					 'sort_key' : key,
+					 'sort_dir' : sort_dir,
+					 'batch_obj': batch_obj }
+
+hidden_vars = []
+for name, value in html_marshal(**default_kw):
+	hidden_vars.append( {'name': name, 'value': value} )
+	
+# buttons
+buttons = []
+if items_add_allowed and context.allowedContentTypes():
+	buttons.append( {'name': 'items_new', 'value': 'New...'} )
+	if items:
+		buttons.append( {'name': 'items_rename', 'value': 'Rename'} )
+		
+if checkPermission(ViewManagementScreens, context) and items:
+	buttons.append( {'name': 'items_cut', 'value': 'Cut'} )
+	buttons.append( {'name': 'items_copy', 'value': 'Copy'} )
+	
+if items_add_allowed and context.cb_dataValid():
+	buttons.append( {'name': 'items_paste', 'value': 'Paste'} )
+	
+if display_delete_button and items:
+	buttons.append( {'name': 'items_delete', 'value': 'Delete'} )
+
+length = batch_obj.sequence_length
+is_orderable = manage_props_allowed and (key == 'position') and length > 1
+is_sortable = manage_props_allowed and not is_default
+options['form'] = { 'action': target,
+					'listHiddenVarInfos': tuple(hidden_vars),
+					'listButtonInfos': tuple(buttons),
+					'is_orderable': is_orderable,
+					'is_sortable': is_sortable }
+if not ajax and is_orderable :
+	deltas = range( 1, min(5, length) ) + range(5, length, 5)
+	options['form']['listDeltas'] = tuple(deltas)
+
+if template and macro :
+	options['template'] = template
+	options['macro'] = macro
+	return context.use_macro(**options)
+else :
+	return context.folder_contents_template(**options)
+
diff --git a/skins/photo_theme/folder_contents_macros.pt b/skins/photo_theme/folder_contents_macros.pt
new file mode 100644
index 0000000..00d2ee9
--- /dev/null
+++ b/skins/photo_theme/folder_contents_macros.pt
@@ -0,0 +1,118 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+    <title>folder contents macros</title>
+  </head>
+
+  <body i18n:domain="cmf_default" metal:define-macro="FolderListing"
+	 			tal:define="batch_dict options/batch;
+				 						batch batch_dict/batch_obj;
+				 						listColumnInfos batch_dict/listColumnInfos;
+				 						listItemInfos batch_dict/listItemInfos;"
+				tal:omit-tag="">
+    <div style="display:none">
+      <span id="FolderUrl" tal:content="here/absolute_url" >Folder url for script</span>
+      <span tal:condition="options/form/is_orderable" tal:omit-tag="">
+	      <span id="FirstItemPos" tal:content="python:listItemInfos[0]['position']">first batch item position</span>
+	      <span id="BatchNavigationSize" tal:content="batch/size"></span>
+	    </span>
+    </div>
+   	<!-- batch navigation -->
+    <div tal:define="navBatchBarId string:topNavBatchBar" tal:omit-tag="">
+      <span metal:use-macro="here/batch_macros/macros/navigation">batch navigation</span>
+    </div>
+    <table class="listing" cellspacing="0">
+			<thead id="FolderListingHeader">
+				<tr>
+					<th tal:attributes="width column_info/width; colspan column_info/colspan" tal:repeat="column_info listColumnInfos" nowrap="nowrap" >
+						<a href="." tal:attributes="href column_info/url" tal:content="column_info/title" i18n:translate="">Type</a>
+						<span  tal:repeat="img column_info/images" tal:omit-tag="">
+							<a tal:omit-tag="python:not img.has_key('href')"
+								 tal:attributes="href img/href|nothing ; title img/alt" i18n:attributes="title"
+								 ><img tal:attributes="src img/src ; alt img/alt ; id img/id|nothing" border="0" i18n:attributes="alt" /></a>
+						</span>
+					</th>
+					<td colspan="5" tal:replace="nothing"></td>
+				</tr>
+			</thead>
+			<tbody id="FolderListingBody">
+<tbody tal:repeat="item_info listItemInfos" tal:omit-tag="">
+		    <tr class="" tal:attributes="class python: (even and 'even') or 'odd'" tal:define="even repeat/item_info/even">
+		      <td><img src="." alt="lock" height="16" width="16" border="0"
+		               tal:condition="python:not isAnon and item_info['lock']"
+		               tal:attributes="src here/lock.gif/absolute_url"
+		               i18n:attributes="alt" /></td>
+		      <td>
+		        <input id="" type="checkbox" name="ids:list" value=""
+		               tal:attributes="value item_info/id; id item_info/checkbox" tal:condition="item_info/checkbox" />
+		      </td>
+		      <td>
+		        <a href="." tal:attributes="href item_info/url" tal:condition="item_info/icon">
+		          <img src="." alt="" tal:attributes="src item_info/icon; alt item_info/type" border="0" i18n:attributes="alt" />
+		        </a>
+		      </td>
+		      <td>
+		        <a href="." tal:attributes="href item_info/url" tal:define="tod item_info/title_or_id; id item_info/id">
+		          <span tal:replace="id">id</span>
+		          <span tal:condition="python:tod != id">
+		            • <span tal:replace="tod">title</span>
+		          </span>
+		        </a>
+		      </td>
+		      <td>
+		        <span tal:replace="item_info/modified">2001</span>
+		      </td>
+		      <td>
+		        <span tal:replace="item_info/position">1</span>
+		      </td>
+		    </tr>
+</tbody>
+	   	</tbody>
+    </table>
+    <!-- batch navigation -->
+    <div tal:define="navBatchBarId string:bottomNavBatchBar" tal:omit-tag="">
+      <span metal:use-macro="here/batch_macros/macros/navigation">batch navigation</span>
+    </div>
+		<script type="text/javascript">
+		//<!--
+			// variables for drag-and-drop ordered support
+			var ddEnabled = false
+			try {
+				var firstItemPos = parseInt(document.getElementById("FirstItemPos").innerHTML);
+				ddEnabled = true;
+			}
+			catch (e) {}
+
+			var topNavBatchBar		= document.getElementById("topNavBatchBar");
+			var bottomNavBatchBar	= document.getElementById("bottomNavBatchBar");
+			var listing = document.getElementById("FolderListingBody");
+
+			if (ddEnabled) {
+				var rows = listing.getElementsByTagName("TR")
+				for ( var i = 0 ; i < rows.length ; i++ )
+					rows[i].pos = i + firstItemPos;
+
+				var fDDcontroler = new FolderDDropControler(listing);
+				var topNavBatchBar		= document.getElementById("topNavBatchBar");
+				var bottomNavBatchBar	= document.getElementById("bottomNavBatchBar");
+				if (topNavBatchBar && bottomNavBatchBar) {
+					new DropTarget(topNavBatchBar, fDDcontroler);
+					new DropTarget(bottomNavBatchBar, fDDcontroler);
+				}
+			}
+
+
+			addListener(document.getElementById("FolderListingHeader"), "click", loadListing);
+			if (topNavBatchBar && bottomNavBatchBar) {
+				addListener(topNavBatchBar, "click", loadListing);
+				addListener(bottomNavBatchBar, "click", loadListing);
+			}
+
+		//-->
+		</script>
+  </body>
+
+</html>
\ No newline at end of file
diff --git a/skins/photo_theme/global_plinn_style.css.dtml b/skins/photo_theme/global_plinn_style.css.dtml
new file mode 100755
index 0000000..763ec4c
--- /dev/null
+++ b/skins/photo_theme/global_plinn_style.css.dtml
@@ -0,0 +1,16 @@
+/*
+
+
+*/
+
+/* zpt_stylesheet.css */
+<dtml-var zpt_stylesheet.css>
+
+/* plinn_style.css */
+<dtml-var plinn_style.css>
+
+/* Portfolio */
+<dtml-var portfolio_style.css>
+
+/* realis_style.css */
+<dtml-var realis_style.css>
\ No newline at end of file
diff --git a/skins/photo_theme/global_plinn_style.css.dtml.metadata b/skins/photo_theme/global_plinn_style.css.dtml.metadata
new file mode 100644
index 0000000..855fecc
--- /dev/null
+++ b/skins/photo_theme/global_plinn_style.css.dtml.metadata
@@ -0,0 +1,2 @@
+[default]
+cache=HTTPCache
diff --git a/skins/photo_theme/homepage_dispatcher.py b/skins/photo_theme/homepage_dispatcher.py
new file mode 100755
index 0000000..57262be
--- /dev/null
+++ b/skins/photo_theme/homepage_dispatcher.py
@@ -0,0 +1,28 @@
+##parameters=
+from Products.CMFCore.utils import getToolByName
+from ZTUtils import make_query as mq
+from Products.CMFDefault.utils import Message as _
+
+req = context.REQUEST
+resp = req.RESPONSE
+came_from = req.form.get('came_from')
+
+if came_from :
+	urlQs = came_from.split('?', 1)
+	if len(urlQs) == 1 :
+		came_from = '%s?%s' % (urlQs[0], mq(portal_status_message=_('Login success')))
+	else :
+		url, qs=  urlQs
+		came_from = '%s?%s&%s' % (url, qs, mq(portal_status_message=_('Login success')))
+	return resp.redirect(came_from)
+
+else :
+	from Products.realis.utils import searchContentsWithLocalRolesForAuthenticatedUser as search
+	results = search(context, portal_type='Portfolio')
+	if results :
+		atool = getToolByName(context, 'portal_actions')
+		return context.setRedirect(atool, 'user/my_albums', portal_status_message=_('Login success'))
+	else :
+		utool = getToolByName(context, 'portal_url')
+		return resp.redirect('%s?%s' % (utool(), mq(portal_status_message=_('Login success'))))
+	
\ No newline at end of file
diff --git a/skins/photo_theme/imagerotator.swf b/skins/photo_theme/imagerotator.swf
new file mode 100644
index 0000000..0156f0e
Binary files /dev/null and b/skins/photo_theme/imagerotator.swf differ
diff --git a/skins/photo_theme/info.gif b/skins/photo_theme/info.gif
new file mode 100644
index 0000000..472921c
Binary files /dev/null and b/skins/photo_theme/info.gif differ
diff --git a/skins/photo_theme/loading_progress.gif b/skins/photo_theme/loading_progress.gif
new file mode 100755
index 0000000..c2f0e7c
Binary files /dev/null and b/skins/photo_theme/loading_progress.gif differ
diff --git a/skins/photo_theme/logo.gif b/skins/photo_theme/logo.gif
new file mode 100755
index 0000000..75e0264
Binary files /dev/null and b/skins/photo_theme/logo.gif differ
diff --git a/skins/photo_theme/logout.py b/skins/photo_theme/logout.py
new file mode 100755
index 0000000..f3ec6fe
--- /dev/null
+++ b/skins/photo_theme/logout.py
@@ -0,0 +1,17 @@
+## Script (Python) "logout"
+##title=Logout handler
+##parameters=ajax=''
+from Products.CMFCore.utils import getToolByName
+from Products.CMFDefault.utils import translate
+def _(msg) : return translate(msg, context)
+utool = getToolByName(context, 'portal_url')
+REQUEST = context.REQUEST
+if REQUEST.has_key('portal_skin'):
+	context.portal_skins.clearSkinCookie()
+REQUEST.RESPONSE.expireCookie('__ac', path='/')
+session = REQUEST.SESSION
+for k in session.keys() :
+	del session[k]
+context.setStatus(True, _('You have been logged out.'))
+portal = utool.getPortalObject()
+return context.setRedirect(portal, 'object/view')
\ No newline at end of file
diff --git a/skins/photo_theme/main_template_macros.pt b/skins/photo_theme/main_template_macros.pt
new file mode 100644
index 0000000..71c04a0
--- /dev/null
+++ b/skins/photo_theme/main_template_macros.pt
@@ -0,0 +1,123 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+    <title></title>
+  </head>
+
+  <body>
+
+<div metal:define-macro="global_defines" tal:omit-tag=""
+tal:define="
+global utool nocall:here/portal_url;
+global mtool here/portal_membership;
+global atool here/portal_actions;
+global wtool here/portal_workflow;
+global portal_object utool/getPortalObject;
+global portal_title portal_object/Title;
+global object_title here/title_or_id;
+global portal_url utool;
+global member mtool/getAuthenticatedMember;
+global membersfolder mtool/getMembersFolder;
+global isAnon mtool/isAnonymousUser;
+global actions python: atool.listFilteredActionsFor( here );
+global user_actions actions/user;
+global object_actions actions/object;
+global workflow_actions actions/workflow;
+global folder_actions actions/folder;
+global global_actions actions/global;
+global wf_state python:wtool.getInfoFor(here,'review_state','');
+global locale_date_fmt here/locale_date_fmt;
+global dummy python:request.RESPONSE.setHeader('Content-Type', 'text/html;;charset=utf-8');
+Iterator python:modules['Products.Plinn'].IndexIterator ;
+global tabindex python:Iterator();
+global specialCtxHeader options/specialCtxHeader|nothing"
+		/>
+
+
+<div metal:define-macro="top_bar">
+	<table id="Masthead" cellspacing="0" border="0">
+		<tr>
+			<td id="PortalLogo" width="215"><a href="#" tal:attributes="href string:${portal_url}?noajax=1; title portal_object/title_or_id"><img src="logo.gif" tal:define="logo here/logo.gif" tal:attributes="src logo/absolute_url; alt portal_object/title_or_id; height logo/height; width logo/width" /></a></td>
+			<td>
+				<div id="site_menu"
+				     tal:define="menu actions/site_menu|nothing"
+				     tal:condition="menu">
+          <span tal:repeat="a menu" tal:omit-tag="">
+            <a tal:content="a/title" tal:attributes="href a/url">Site Menu</a>
+            <span tal:condition="not:repeat/a/end" tal:omit-tag=""> |</span>
+          </span>
+			  </div>
+			</td>
+			<td>
+			  <img tal:attributes="src here/loading_progress.gif/absolute_url" width="32" height="32" id="loading_progress" alt="Loading progress..." i18n:attributes="alt" style="display:none" />
+			</td>
+			<td style="vertical-align:top">
+				<div id="site_related"
+				     tal:define="menu actions/site_related|nothing"
+				     tal:condition="menu">
+          <span tal:repeat="a menu" tal:omit-tag="">
+           <a tal:content="a/title" tal:attributes="href a/url">Site Menu</a>
+           <span tal:condition="not:repeat/a/end" tal:omit-tag=""> |</span>
+          </span>
+				</div>
+			</td>
+		</tr>
+	</table>
+</div>
+
+  
+<div tal:define="uname python: (isAnon and [''] or [member.getMemberFullName(nameBefore=0)])[0]" metal:define-macro="user_menu_bar">
+	<table id="MemberActionsBox" border="0" cellspacing="0" i18n:domain="cmf_default">
+		<tr>
+			<td class="ActionTitle" tal:content="uname">Guest</td>
+			<td><img tal:attributes="src here/loading_progress.gif/absolute_url" width="82" height="7" id="loading_progress" alt="Loading progress..." i18n:attributes="alt" style="display:none" /></td>
+			<td class="ActionLink" align="right">
+				<span tal:repeat="action user_actions">
+					<a href="#" tal:attributes="href action/url" tal:content="action/name" i18n:translate="">Login</a>
+					<span tal:condition="not: repeat/action/end"> | </span> 
+				</span>
+				<span tal:condition="nothing"><a href="join_form" i18n:translate="">Join</a></span>
+			</td>
+		</tr>
+	</table>
+</div>
+
+
+<div metal:define-macro="Breadcrumbs" tal:omit-tag="">
+	<span tal:content="here/absolute_url" id="BC_Object_URL" style="display:none"></span>
+  <span i18n:translate="">You are here:</span> <span tal:omit-tag="" tal:repeat="bc options/breadcrumbs|here/breadcrumbs"><a href="." tal:attributes="href bc/url" tal:content="python:bc['title'] or bc['id']">ID</a><span tal:condition="not: repeat/bc/end" tal:omit-tag=""> / </span> </span>
+</div>
+
+
+<div id="leftCell" class="boxes_container" width="15%" metal:define-macro="leftCell"
+		 tal:define="block python:getattr(here.global_settings, 'left_boxes', None) ;
+								 displayAction python:request.SESSION.get('editBoxes') and 'edit' or 'view'"
+		 tal:omit-tag="">
+		<div tal:condition="python:displayAction == 'edit' and here.cb_dataValid() and block.haveRules()">
+			<a title="Paste" href="#" tal:attributes="href string:${block/absolute_url}/pasteBlocks" i18n:attributes="title"><img src="paste.png" alt="Paste" height="14" width="16" tal:attributes="src string:paste.png" border="0" i18n:attributes="alt" /></a>
+		</div>
+		<div metal:use-macro="here/block_canvas/macros/canvas">left boxes</div>
+</div>
+
+<div metal:define-macro="rightCell" 
+		 tal:define="block python:getattr(here.global_settings, 'right_boxes', None) ;
+								 displayAction python:request.SESSION.get('editBoxes') and 'edit' or 'view'"
+		 tal:condition="python:block is not None">
+	<div tal:condition="python:displayAction=='edit'" style="text-align:right">
+		<a tal:attributes="href string:$portal_url/manage_boxes?exitMode:boolean=True" i18n:domain="plinn" i18n:translate="">Exit boxes editing</a><br/>
+	</div>
+	<div tal:condition="python:displayAction == 'edit' and here.cb_dataValid() and block.haveRules()">
+		<a title="Paste" href="#" tal:attributes="href string:${block/absolute_url}/pasteBlocks" i18n:attributes="title"><img src="paste.png" alt="Paste" height="14" width="16" tal:attributes="src string:paste.png" border="0" i18n:attributes="alt" /></a>
+	</div>
+	
+	<div metal:use-macro="here/block_canvas/macros/canvas">right boxes</div>
+
+	<div metal:use-macro="here/widgets/macros/clipboard">Clipboard</div>
+</div>
+
+  </body>
+
+</html>
\ No newline at end of file
diff --git a/skins/photo_theme/main_template_standard.pt b/skins/photo_theme/main_template_standard.pt
new file mode 100755
index 0000000..724b156
--- /dev/null
+++ b/skins/photo_theme/main_template_standard.pt
@@ -0,0 +1,168 @@
+<metal:block define-macro="master">
+  <tal:block define="global main_macros here/main_template_macros/macros"/>
+  <metal:block use-macro="main_macros/global_defines"/>
+  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+  <html xmlns="http://www.w3.org/1999/xhtml">
+
+    <head>
+      <title tal:content="here/expanded_title">Title goes here</title>
+      <meta http-equiv="content-type" content="text/html;charset=utf-8" />
+      <meta name="description"
+            tal:attributes="content python:here.Description().strip() or portal_object.description" />
+      <meta name="keywords"
+            tal:define="keywords python:here.Subject() or portal_object.keywords or []"
+            tal:attributes="content python:', '.join(keywords)" />
+      <base tal:omit-tag="" metal:define-slot="base" />
+
+      <link rel="Shortcut Icon" type="image/x-icon" href="favicon.ico" tal:attributes="href string:$portal_url/favicon.ico" />
+      <link rel="alternate" type="application/rss+xml"
+            tal:define="stool nocall:here/portal_syndication"
+            tal:attributes="title here/title_or_id ; href string:${here/absolute_url}/RSS"
+            tal:condition="python:stool.isSyndicationAllowed(here)" />
+      
+      <link rel="stylesheet" media="screen" tal:attributes="href here/global_plinn_style.css/absolute_url" />
+      <style type="text/css" media="all" tal:content="string:@import url(${here/calendar-system.css/absolute_url});"></style>
+
+      <link metal:define-slot="css_slot" tal:replace="nothing" />
+      <script type="text/javascript" tal:attributes="src here/epoz_script_detect.js/absolute_url"></script>
+      <script type="text/javascript" tal:attributes="src here/javascript_events_api.js/absolute_url"></script>
+      <script type="text/javascript" tal:attributes="src here/sarissa.js/absolute_url"></script>      
+      <script type="text/javascript">
+        //<!--
+        function absolute_url() {
+          var e = document.getElementById("Object_URL");
+          if (e)
+            return e.innerHTML;
+          else {
+            e = document.getElementById("BC_Object_URL");
+            if (e)
+              return e.innerHTML;
+            else
+              return document.location.href;
+          }
+        }
+
+        function portal_url() {
+          var e = document.getElementById("Portal_URL");
+          return e.innerHTML;
+        }
+
+        var startupFunctions = new Array();
+
+        function registerStartupFunction(fct) {
+          startupFunctions.push(fct);
+        }
+
+        function runAjaxStartup() {
+          for (var i = 0 ; i < startupFunctions.length ; i++)
+            startupFunctions[i]();
+        }
+        
+        addListener(window, 'load', runAjaxStartup);
+        
+        var progressImage;
+        function _initLoadingStatus() {
+          progressImage = document.getElementById('loading_progress');
+        }
+        registerStartupFunction(_initLoadingStatus);
+        
+        function showProgressImage() { progressImage.style.display = 'inline'; }
+        function hideProgressImage() { progressImage.style.display = 'none'; }
+
+
+        // -->
+      </script>
+      <script type="text/javascript" tal:attributes="src here/base_properties.js/absolute_url"></script>
+      <script type="text/javascript" tal:attributes="src here/fragment_importer.js/absolute_url"></script>
+      <script type="text/javascript" tal:attributes="src here/ajax_form_manager.js/absolute_url"></script>
+      <script type="text/javascript" tal:attributes="src here/root_handlers.js/absolute_url"></script>
+      <script type="text/javascript" tal:attributes="src here/plinn_script_loader.js/absolute_url"></script>
+      
+      <script type="text/javascript" metal:define-slot="javascript_head_slot" tal:replace="nothing"></script>
+    </head>
+
+    <body>
+      <span style="display:none">
+        <span id="Portal_URL" tal:content="portal_url"></span>
+        <span id="ajax_config"  tal:content="python:request.get('ajaxConfig', request.SESSION.get('ajaxConfig', portal_object.getProperty('ajax_config')))"></span>
+        <script type="text/javascript">
+        // <!--
+          AJAX_CONFIG = document.getElementById('ajax_config');
+          AJAX_CONFIG = parseInt(AJAX_CONFIG.innerHTML);
+        // -->
+        </script>
+      </span>
+      <a href="#documentContent" i18n:translate="" i18n:domain="plinn" class="hidden">Skip to content</a>
+      <div metal:use-macro="main_macros/top_bar">Top bar</div>
+      <div id="search_bar">
+        <div metal:use-macro="here/realis_main_macros/macros/search_bar">Barre de recherche réalis</div>
+      </div>
+      <div id="Breadcrumbs"><div metal:use-macro="main_macros/Breadcrumbs">Breadcrumbs</div></div>
+
+      <div id="main_body">
+        <table id="Forematter" width="100%" cellspacing="0">
+          <tr>
+            <td class="boxes_container" width="15%" tal:condition="python:member.has_role(['photographe', 'Manager'])">
+              <div id="leftCell">
+                <span metal:use-macro="main_macros/leftCell">Left cell</span>
+              </div>
+              <div id="rightCell">
+                <span metal:use-macro="main_macros/rightCell">Right cell</span>
+              </div>
+            </td>
+
+            <td id="mainCell" width="85%">
+
+              <div class="DesktopStatusBar" tal:condition="request/portal_status_message|nothing">
+                <img src="info.gif" alt="Information" tal:attributes="src here/info.gif/absolute_url" i18n:attributes="alt" />
+                <span tal:content="request/portal_status_message" i18n:translate="">Status message.</span>
+              </div>
+
+              <a name="documentContent"></a>
+              <div metal:define-slot="main_no_tabs" tal:omit-tag="">
+                <div tal:condition="specialCtxHeader" tal:content="structure specialCtxHeader">special context header</div>
+                <div id="ContentTabs" tal:condition="not:specialCtxHeader">
+                  <div class="tabs" metal:use-macro="here/tabs/macros/tabs">Content tabs</div>
+                  <div metal:use-macro="here/tabs/macros/additional_tabs">Additional tabs</div>
+                </div>
+                <div id="ForematterCell">
+                  <div class="header">
+                    <div tal:omit-tag="" metal:define-slot="header">
+                      <h1 id="DesktopTitle">
+                        <span tal:replace="here/title_or_id">Page Title</span>
+                      </h1>
+                      <div id="DesktopDescription" tal:content="structure here/Description">
+                        Description of the resource goes here, perhaps even wrapping lines; this is to make it long enough to test.
+                      </div>
+                    </div>
+                  </div>
+
+                  <div id="Desktop">
+                    <div metal:define-slot="main">
+                      <h2>Section Header</h2>
+                      <p>The content of the object is <a href=".">rendered</a>
+                      hre. Lorem ipsum dolorem. Nihil obstat imprimatur. Semper ubi sub ubi.
+                      Non illegitimi carborundum. In vino veritas. E pluribus unam.
+                      </p>
+                    </div>
+                  </div>
+                  <div tal:condition="not:isAnon" tal:omit-tag="">
+                    <div id="ContentByLine" metal:use-macro="here/content_byline/macros/byline">
+                      Content by me
+                    </div>
+                  </div>
+                  <div metal:define-slot="footer" tal:replace="nothing">footer slot</div>
+                </div>
+              </div>
+
+            </td>
+            <!-- end_main_column -->
+          </tr>
+        </table>
+      </div>
+    </body>
+
+  </html>
+
+</metal:block>
\ No newline at end of file
diff --git a/skins/photo_theme/member_albums.py b/skins/photo_theme/member_albums.py
new file mode 100755
index 0000000..9425f2b
--- /dev/null
+++ b/skins/photo_theme/member_albums.py
@@ -0,0 +1,6 @@
+##parameters=
+
+from Products.realis.utils import searchContentsWithLocalRolesForAuthenticatedUser as search
+
+results = search(context, portal_type='Portfolio')
+return context.member_albums_template(results = results)
\ No newline at end of file
diff --git a/skins/photo_theme/member_albums_template.pt b/skins/photo_theme/member_albums_template.pt
new file mode 100644
index 0000000..eceee09
--- /dev/null
+++ b/skins/photo_theme/member_albums_template.pt
@@ -0,0 +1,42 @@
+<html metal:use-macro="here/main_template/macros/master"
+      xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal">
+  <head>
+    <title>Member albums' template</title>
+    <metal:block metal:fill-slot="javascript_head_slot">
+      
+    </metal:block>
+  </head>
+  <body>
+    <div metal:fill-slot="main_no_tabs" tal:omit-tag=""
+         tal:define="Batch python:modules['Products.Plinn'].Batch;
+                     b_start python:request.get('b_start',0);
+                     batch python:Batch(options['results'], 16, b_start, orphan=1, quantumleap=1);"
+         i18n:domain="portfolio">
+      <h2 i18n:translate="">My albums</h2>
+      <div tal:condition="not:batch" i18n:translate="">You don't have any album yet.</div>
+      <div tal:condition="batch">
+        <div metal:use-macro="here/batch_macros/macros/navigation">batch navigation</div>
+        <div class="portfolio_lightbox">
+          <a tal:repeat="brain batch"
+             tal:attributes="href brain/getURL">
+            <span tal:define="portfolio brain/getObject;
+                              sphoto portfolio/samplePhoto" tal:omit-tag="">
+              <span tal:condition="sphoto" tal:omit-tag="">
+                <img tal:attributes="src sphoto/src;
+                                     alt sphoto/alt;
+                                     height sphoto/height;
+                                     width sphoto/width;"/>
+              </span>
+              <span tal:condition="not:sphoto" tal:omit-tag="">
+                <img tal:attributes="src here/empty_portfolio.gif/absolute_url;
+                                     alt portfolio/title_or_id"/>
+              </span>
+              <h3 tal:content="portfolio/title_or_id">Portfolio title or id</h3>
+              <div tal:content="portfolio/Description"></div>
+            </span>
+          </a>
+        </div>
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/skins/photo_theme/mi.gif b/skins/photo_theme/mi.gif
new file mode 100644
index 0000000..cb71f7e
Binary files /dev/null and b/skins/photo_theme/mi.gif differ
diff --git a/skins/photo_theme/mi_ani.gif b/skins/photo_theme/mi_ani.gif
new file mode 100644
index 0000000..10c759a
Binary files /dev/null and b/skins/photo_theme/mi_ani.gif differ
diff --git a/skins/photo_theme/pl.gif b/skins/photo_theme/pl.gif
new file mode 100644
index 0000000..26268f6
Binary files /dev/null and b/skins/photo_theme/pl.gif differ
diff --git a/skins/photo_theme/pl_ani.gif b/skins/photo_theme/pl_ani.gif
new file mode 100644
index 0000000..773202c
Binary files /dev/null and b/skins/photo_theme/pl_ani.gif differ
diff --git a/skins/photo_theme/portfolio_properties.props b/skins/photo_theme/portfolio_properties.props
new file mode 100644
index 0000000..44d3744
--- /dev/null
+++ b/skins/photo_theme/portfolio_properties.props
@@ -0,0 +1,19 @@
+medium_image_background:string=#727272
+metadata_bar_background:string=#555
+photographer_font_color:string=#fff
+title_font_color:string=#2b2b2b
+metadata_edit_label_color:string=#9da097
+metadata_edit_panel_background:string=#3e3e3e
+metadata_panel_border_sep_color:string=#484848
+metadata_input_font_color:string=#d9d9d9
+metadata_input_background:string=#3a3a3a
+metadata_input_border_color:string=#3a3a3a
+metadata_input_hover_background:string=#4c4c4c
+metadata_input_focus_font_color:string=#000
+metadata_input_focus_background:string=#fff
+metadata_input_border_focus_color:string=#313131
+portfolio_slide_background:string=#1b1b1b
+portfolio_slide_hover_background:string=#2b2b2b
+slide_background:string=#555
+slide_hover_background:string=#606060
+slide_selected_background:string=#727272
diff --git a/skins/photo_theme/realis_main_macros.pt b/skins/photo_theme/realis_main_macros.pt
new file mode 100644
index 0000000..c5e1bfa
--- /dev/null
+++ b/skins/photo_theme/realis_main_macros.pt
@@ -0,0 +1,33 @@
+<html xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal">
+  <head>
+    <title>Macros realis</title>
+    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
+  </head>
+  <body>
+    <div metal:define-macro="search_bar"
+         style="vertical-align:middle">
+      <form action="search" method="get" tal:attributes="action string:${portal_url}/search">
+        <div id="quicksearch" i18n:domain="cmf_default">
+          <span i18n:translate="" tal:omit-tag="">Search</span>
+          <input name="SearchableText" type="text" size="16" />
+          <input name="portal_type:list" type="hidden" value="Photo" />
+          <input type="image" tal:attributes="src string:${portal_url}/topic_icon.gif"
+                 width="16" height="16" alt="Search" i18n:attributes="alt"/>
+          <!--
+            <a tal:attributes="href string:${portal_url}/search_form" i18n:translate="">Advanced search</a>
+          -->
+        </div>
+      </form>
+      <div id="quickuser">
+          <span tal:define="uname python: (isAnon and [''] or [member.getMemberFullName(nameBefore=0)])[0]" tal:content="uname" style="font-weight:bold">bob</span>
+          &nbsp;&nbsp;&nbsp;&nbsp;
+          <span tal:repeat="action user_actions">
+          <a href="#" tal:attributes="href action/url" tal:content="action/title">Login</a>
+          <span tal:condition="not: repeat/action/end" tal:omit-tag=""> | </span>
+          </span>
+          <span tal:condition="nothing"><a href="join_form" i18n:translate="">Join</a></span>
+      </div>
+      <br clear="all"/>
+    </div>
+  </body>
+</html>
diff --git a/skins/photo_theme/realis_style.css.dtml b/skins/photo_theme/realis_style.css.dtml
new file mode 100644
index 0000000..76a4f20
--- /dev/null
+++ b/skins/photo_theme/realis_style.css.dtml
@@ -0,0 +1,169 @@
+/* <dtml-with base_properties>
+
+
+*/
+
+h1, h2, h3, h4, h5, h6 {
+	color:#fff
+}
+
+.ActionTitle {
+  color: #fff;
+}
+
+.ActionLink a:link,
+.ActionLink a:visited,
+.ActionLink a:active { 
+  color: #fff;
+}
+
+.ActionLink a:hover { color: #ef8e32}
+
+#NavBar a,
+#NavBar a:link,
+#NavBar a:visited,
+#NavBar a:active { 
+  color: #fff;
+}
+
+#NavBar a:hover {
+  color: #ef8e32;
+}
+
+#Footer {
+  color: #e6eadd;
+}
+
+#quicksearch, #quickuser {
+	padding:2px;
+	font-size: 90%;
+}
+
+#quicksearch {
+	float:left;
+}
+
+
+#quickuser {
+	float:right;
+}
+
+#quicksearch input {
+	/*height:16px;*/
+	vertical-align:middle;
+}
+
+#quicksearch span {
+	vertical-align:middle;
+}
+#quickuser span {
+	padding-top:5px;
+	vertical-align: middle;
+}
+
+body {
+	padding: 0 40px;
+}
+
+img {
+	border:0;
+}
+
+#search_bar {
+	padding: 1px 8px ;
+	color:#f0f0f0;
+	background: #707070;
+}
+#search_bar input {
+	font-size:90%;
+	background:#e4e4e4;
+	border: 1px solid #2d2d2d;
+}
+
+
+#Masthead {
+	padding:4px;
+}
+
+#site_menu {
+	float:left;
+	font-size:130%;
+	margin-left:2em;
+}
+
+#site_related {
+	font-size:90%;
+	float:right;
+}
+
+#mainCell {
+	padding:4px 0 0 0;
+}
+
+.contentTabs a,
+.contentTabs a:visited
+{
+  border-left : none;
+  border-top : none;
+  border-right : none;
+}
+
+#DesktopDescription {
+  font-size: &dtml-fontSmallSize;;
+  color: &dtml-descriptionColor;;
+}
+
+#Desktop {
+  padding: 0;
+}
+
+#DesktopTitle {
+  padding: 0;
+}
+
+.header {
+  padding: 0;
+}
+
+.boxes_container li {
+  list-style:square inside;
+}
+
+table.home {
+	background:#111;
+	font-size:95%;
+}
+table.home td {
+	margin:0;
+	padding:0;
+}
+
+.home_menu a {
+	background:#111;
+	color: &dtml-fontColor;;
+	display:block;
+	width: 186px;
+	height: 216px;
+	display:block;
+	text-align:center;
+	overflow:hidden;
+}
+
+.home_menu a:hover {
+	background:#222;
+}
+
+.home_menu a h3 {
+	color:#fff;
+}
+
+.home_menu a:hover h3 {
+	color:&dtml-linkHoverColor;;
+}
+
+.home_menu p {
+	font-weight:bold;
+	font-size:10px;
+}
+
+/* </dtml-with> */
\ No newline at end of file
diff --git a/skins/photo_theme/search.pt b/skins/photo_theme/search.pt
new file mode 100644
index 0000000..8d90e1b
--- /dev/null
+++ b/skins/photo_theme/search.pt
@@ -0,0 +1,52 @@
+<html xmlns:tal="http://xml.zope.org/namespaces/tal"
+      xmlns:metal="http://xml.zope.org/namespaces/metal"
+      metal:use-macro="here/main_template/macros/master">
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+    <metal:block metal:fill-slot="javascript_head_slot">
+      <script type="text/javascript" tal:attributes="src string:${portal_url}/photo_lightbox_viewer.js"></script>
+    </metal:block>
+  </head>
+  <body>
+    <div metal:fill-slot="main_no_tabs" i18n:domain="cmf_default">
+      <div class="Desktop">
+
+      <h2 i18n:translate="">Search Results</h2>
+
+      <div tal:define="b_start python:0;b_start request/b_start | b_start;
+                       results python:here.doFormSearch( REQUEST=request );
+                       searchArgs results/searchArgs;
+                       results results/results;
+                       stext request/SearchableText|nothing;
+                       resultsLength python:len(results);
+                       Batch python:modules['Products.Plinn'].Batch;
+                       global batch python:Batch(results, here.default_batch_size, b_start, orphan=1, quantumleap=1);
+                       macro python:request.form.get('portal_type') == ['Photo'] and 'photo' or 'normal';
+                      ">
+
+      <div tal:condition="resultsLength" tal:omit-tag="">
+      <p i18n:translate=""
+         tal:condition="stext">Found <span tal:replace="python:len(results)"
+         i18n:name="count">100</span> <span i18n:name="item_s_match"
+         tal:content="python:resultsLength > 1 and 'items matching' or 'item matching'"
+         i18n:translate=""
+         tal:omit-tag="">items matching</span> '<span
+         tal:replace="request/SearchableText"
+         i18n:name="text">search term</span>'.
+      </p>
+      <div tal:condition="not:stext">
+        <p tal:condition="python:resultsLength==1" i18n:translate="">Found 1 item.</p>
+        <p tal:condition="python:resultsLength>1" i18n:translate="">Found <span i18n:name="count" tal:content="resultsLength" tal:omit-tag="">2</span> items.</p>
+      </div>
+      </div>
+        <div tal:condition="results" tal:omit-tag="">
+          <div metal:use-macro="python:here.search_results_macros.macros[macro]"></div>
+        </div>
+        <p tal:condition="not:results" i18n:translate="">
+          There are no items matching your specified criteria.
+        </p>
+        </div>
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/skins/photo_theme/search_results_macros.pt b/skins/photo_theme/search_results_macros.pt
new file mode 100644
index 0000000..a45d22b
--- /dev/null
+++ b/skins/photo_theme/search_results_macros.pt
@@ -0,0 +1,62 @@
+<html xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal">
+  <head>
+    <title>Macros utilisées pour l'affichage des résultats</title>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+  </head>
+  <body>
+    <div metal:use-macro="here/batch_macros/macros/navigation">batch navigation</div>
+    <table metal:define-macro="normal" class="listing" cellspacing="0">
+     <thead>
+      <tr>
+       <td width="16">&nbsp;</td>
+       <th i18n:translate="">Title</th>
+                    <th i18n:translate="">Creator</th>
+                    <th i18n:translate="">Type</th>
+       <th i18n:translate="">Date</th>
+      </tr>
+     </thead>
+     <tbody tal:repeat="result batch" tal:omit-tag="">
+      <tr tal:define="objURL result/getURL;
+      								oddrow repeat/result/odd"
+      		tal:attributes="class python:test(oddrow, 'even', 'odd')">
+       <td><a href=""
+         tal:condition="result/getIcon|nothing"
+             tal:attributes="href objURL"><img src="" border="0" alt="" title=""
+                 tal:define="alt result/Type"
+                 tal:attributes="src result/getIcon;
+                                 alt alt;
+                                 title alt" /></a></td>
+       <td><a href=""
+         tal:attributes="href objURL"><span
+             tal:condition="result/Title"
+                 tal:replace="result/Title">Title</span><span
+             tal:condition="not:result/Title"
+                 tal:omit-tag=""
+                 i18n:translate="">(No title)</span></a></td>
+                    <td tal:content="python:mtool.getMembers([result.Creator])[0].getMemberFullName(nameBefore=0)"
+                        tal:on-error="result/Creator"></td>
+                    <td tal:content="result/Type" i18n:translate="">Type</td>
+       <td tal:content="python:modules['DateTime'].DateTime(result.Date).strftime(locale_date_fmt)">Date</td>
+      </tr>
+      <tr>
+       <td>&nbsp;</td>
+       <td colspan="4"><em
+         tal:condition="result/Description"
+             tal:content="structure result/Description">Description</em><em
+         tal:condition="not:result/Description"
+             i18n:translate="">(No description)</em></td>
+      </tr>
+     </tbody>
+    </table>
+    
+    <div metal:define-macro="photo"
+         tal:define="slide_size python:portal_object.slide_size">
+      <div tal:define="contextInfo python:here.getPhotoBrainsInfos(batch, searchArgs);
+                       infos contextInfo/infos;
+                       features contextInfo/features"
+           tal:omit-tag="">
+        <div metal:use-macro="here/photo_layout_macros/macros/grid"></div>
+      </div>
+    </div>
+  </body>
+</html>
diff --git a/skins/photo_theme/tabs.pt b/skins/photo_theme/tabs.pt
new file mode 100644
index 0000000..c7a57cb
--- /dev/null
+++ b/skins/photo_theme/tabs.pt
@@ -0,0 +1,50 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+  <head>
+    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
+    <title>tabs</title>
+    <link href="zpt_stylesheet.css" rel="stylesheet" media="screen" />
+    <link href="plinn_style.css" rel="stylesheet" media="screen" />
+  </head>
+
+  <body i18n:domain="plinn">
+
+    <div metal:define-macro="tabs"
+         tal:define="filteredActionsList python:[];
+                     dummy python:[ filteredActionsList.extend(l) for l in [ actions.get(key, []) for key in ['folder', 'object', 'workflow'] ] ]"
+         tal:condition="python:len(filteredActionsList) > 1" tal:omit-tag="">
+		<div metal:define-macro="standalone_tabs" tal:define="forceTab forceTab|nothing">
+      <ul class="contentTabs">
+        <li  class="selected" tal:replace="nothing">
+          <a href=".">View</a>
+        </li>
+        <li tal:repeat="action filteredActionsList"
+            tal:attributes="class python:( (forceTab == action['id']) or (request['URL0'].strip() == action['url'].strip() or request['URL0'].strip().split('/')[-1] == action['id']) ) and 'selected' or nothing">
+          <a href="#" tal:attributes="href action/url ; name action/id"
+             tal:content="action/title" i18n:translate="">Edit</a>
+        </li>
+      </ul>
+		</div>
+    </div>
+
+    <div metal:define-macro="additional_tabs"
+         tal:define="filteredActionsList python:actions.get('additional_tabs', [])"
+         tal:condition="filteredActionsList" tal:omit-tag="">
+    	<div metal:define-macro="standalone_additional_tabs" tal:define="forceTab forceTab|nothing">
+	      <ul class="additionalTabs">
+	        <li  class="selected" tal:replace="nothing">
+	          <a href=".">View</a>
+	        </li>
+	        <li tal:repeat="action filteredActionsList"
+	            tal:attributes="class python:( (forceTab == action['id']) or (request['URL0'].strip() == action['url'].strip() or request['URL0'].strip().split('/')[-1] == action['id']) ) and 'selected' or nothing">
+	          <a href="#" tal:attributes="href action/url ; name action/id"
+	             tal:content="action/title" i18n:translate="">Edit</a>
+	        </li>
+	      </ul>
+	    </div>
+    </div>
+    
+  </body>
+</html>
diff --git a/skins/photo_theme/validatePrivateAccess.py b/skins/photo_theme/validatePrivateAccess.py
new file mode 100755
index 0000000..19e3a7d
--- /dev/null
+++ b/skins/photo_theme/validatePrivateAccess.py
@@ -0,0 +1,16 @@
+##parameters=**kw
+kg = lambda name : kw.get(name,'').strip()
+
+weddingId = kg('wedding_id')
+if not weddingId :
+	return True
+
+else :
+	password = kg('wedding_password')
+	confirm = kg('wedding_password_confirm')
+	memberId = kg('member_id')
+	msg = context.grantAccess(context, weddingId, password, confirm, memberId)
+	if msg :
+		return context.setStatus(False, msg)
+	else :
+		return True