Changeset 20323 for extensions
- Timestamp:
- Jan 22, 2013, 1:43:58 PM (11 years ago)
- Location:
- extensions/oAuth
- Files:
-
- 38 added
- 22 deleted
- 12 edited
Legend:
- Unmodified
- Added
- Removed
-
extensions/oAuth/admin.php
r20293 r20323 11 11 12 12 // get current tab 13 $page['tab'] = (isset($_GET['tab'])) ? $_GET['tab'] : $page['tab'] = ' config';13 $page['tab'] = (isset($_GET['tab'])) ? $_GET['tab'] : $page['tab'] = 'providers'; 14 14 15 15 // tabsheet … … 18 18 $tabsheet->set_id('oauth'); 19 19 20 $tabsheet->add('providers', l10n('Providers'), OAUTH_ADMIN . '-providers'); 20 21 $tabsheet->add('config', l10n('Configuration'), OAUTH_ADMIN . '-config'); 21 22 $tabsheet->select($page['tab']); -
extensions/oAuth/admin/config.php
r20301 r20323 2 2 defined('OAUTH_PATH') or die('Hacking attempt!'); 3 3 4 $PROVIDERS_CONFIG = include(OAUTH_PATH . 'include/providers_stats.inc.php');5 4 6 5 if (isset($_POST['save_config'])) 7 6 { 8 // providers config 9 array_walk_recursive($_POST, 'trim'); 10 11 $providers = array(); 12 foreach ($_POST['providers'] as $id => $data) 13 { 14 $error = false; 15 $data['enabled'] = $data['enabled']=='true'; 7 // plugin config 8 $conf['oauth'] = array( 9 'display_register' => isset($_POST['display_register']), 10 'display_menubar' => isset($_POST['display_menubar']), 11 'identification_icon' => $_POST['identification_icon'], 12 'menubar_icon' => $_POST['menubar_icon'], 13 ); 16 14 17 if ($PROVIDERS_CONFIG[$id]['new_app_link'] and $data['enabled']) 18 { 19 if (empty($data['keys']['secret']) or 20 (@$PROVIDERS_CONFIG[$id]['require_client_id'] and empty($data['keys']['id'])) or 21 (!@$PROVIDERS_CONFIG[$id]['require_client_id'] and empty($data['keys']['key'])) 22 ) { 23 array_push($page['errors'], sprintf(l10n('%s: invalid keys'), $PROVIDERS_CONFIG[$id]['provider_name'])); 24 $error = true; 25 } 26 } 27 else 28 { 29 unset($data['keys']); 30 } 31 32 if ( ($id=='Wordpress' or $id=='Flickr') and $data['enabled'] and !@$providers['OpenID']['enabled'] ) 33 { 34 array_push($page['errors'], sprintf(l10n('OpenID must be enabled in order to use %s authentication'), $id)); 35 $error = true; 36 } 37 38 if (isset($PROVIDERS_CONFIG[$id]['scope'])) 39 { 40 $data['scope'] = $PROVIDERS_CONFIG[$id]['scope']; 41 } 42 43 if (!$error) 44 { 45 $providers[$id] = $data; 46 } 47 } 48 49 if (!count($page['errors'])) 50 { 51 // generate config file 52 $hybridauth_conf['providers'] = $providers; 53 $content = "<?php\ndefined('PHPWG_ROOT_PATH') or die('Hacking attempt!');\n\nreturn "; 54 $content.= var_export(array('providers'=>$providers), true); 55 $content.= ";\n?>"; 56 file_put_contents(OAUTH_CONFIG, $content); 57 58 // plugin config 59 $conf['oauth'] = array( 60 'display_register' => isset($_POST['display_register']), 61 'display_menubar' => isset($_POST['display_menubar']), 62 ); 63 64 conf_update_param('oauth', serialize($conf['oauth'])); 65 array_push($page['infos'], l10n('Information data registered in database')); 66 } 15 conf_update_param('oauth', serialize($conf['oauth'])); 16 array_push($page['infos'], l10n('Information data registered in database')); 67 17 } 68 18 69 70 $template->assign(array(71 'PROVIDERS' => $PROVIDERS_CONFIG,72 'CONFIG' => $hybridauth_conf['providers'],73 'SERVERNAME' => get_absolute_root_url(),74 'OAUTH_CALLBACK' => OAUTH_PUBLIC . '?hauth.done=',75 ));76 19 $template->assign($conf['oauth']); 77 20 -
extensions/oAuth/admin/template/config.tpl
r20301 r20323 1 1 {combine_css path=$OAUTH_PATH|@cat:"admin/template/style.css"} 2 3 {footer_script}{literal}4 jQuery("select.enable").change(function() {5 var $top = $(this).closest("div.provider");6 var p = $top.data('p');7 8 if ($(this).val()=='true') {9 $top.find("td.keys").show();10 $top.removeClass('disabled');11 $top.addClass('enabled');12 }13 else {14 $top.find("td.keys").hide();15 $top.removeClass('enabled');16 $top.addClass('disabled');17 }18 });19 20 jQuery("#close_help").click(function() {21 jQuery("#help_container").animate({"margin-right": "-550px"}, 'fast');22 return false;23 });24 25 jQuery(".open-help").click(function() {26 var $top = $(this).closest("div.provider");27 var p = $top.data('p');28 29 $("#help_container h5").html($top.find("h4").html());30 $("#help_container div").html($top.find("div.help").html());31 $("#help_container").animate({"margin-right": "0px"}, 'fast');32 return false;33 });34 {/literal}{/footer_script}35 36 2 37 3 <div class="titrePage"> … … 41 7 <form method="post" action="" class="properties"> 42 8 <fieldset id="commentsConf"> 43 <legend></legend>44 9 <ul> 45 10 <li> 46 11 <label> 47 <input type="checkbox" name="display_menubar" {if ($display_menubar)}checked="checked"{/if}>12 <input type="checkbox" name="display_menubar" {if $display_menubar}checked="checked"{/if}> 48 13 <b>{'Display sign in buttons in the menubar'|@translate}</b> 49 14 </label> … … 52 17 <li> 53 18 <label> 54 <input type="checkbox" name="display_register" {if ($display_register)}checked="checked"{/if}>19 <input type="checkbox" name="display_register" {if $display_register}checked="checked"{/if}> 55 20 <b>{'Display sign in buttons on the register page'|@translate}</b> 56 21 </label> … … 59 24 </fieldset> 60 25 61 <p style="text-align:left;"><input type="submit" name="save_config" value="{'Save Settings'|@translate}"></p> 62 63 <fieldset id="providers"> 64 <div id="help_container"> 65 <a href="#" id="close_help" title="{'Close'|@translate}">×</a> 66 <h5></h5> 67 <div></div> 68 </div> 69 <legend>{'Providers'|@translate}</legend> 70 71 {foreach from=$PROVIDERS item=provider key=p} 72 <div data-p="{$p}" class="provider {if $CONFIG[$p].enabled}enabled{else}disabled{/if}"> 73 <h4>{$provider.provider_name}</h4> 74 75 <table><tr> 76 <td> 77 <img src="{$OAUTH_PATH}template/icons/{$p|strtolower}_big.png"> 78 </td> 26 <fieldset id="commentsConf"> 27 <ul> 28 <li> 29 <b>{'Icon size on the identification page'|@translate} :</b><br> 30 <label> 31 <input type="radio" name="identification_icon" value="16px" {if $identification_icon=='16px'}checked="checked"{/if}> 32 16px 33 <img src="{$OAUTH_PATH}template/icons/16px/facebook.png"> 34 <img src="{$OAUTH_PATH}template/icons/16px/google.png"> 35 <img src="{$OAUTH_PATH}template/icons/16px/twitter.png"> 36 </label> 37 <label> 38 <input type="radio" name="identification_icon" value="26px" {if $identification_icon=='26px'}checked="checked"{/if}> 39 26px 40 <img src="{$OAUTH_PATH}template/icons/26px/facebook.png"> 41 <img src="{$OAUTH_PATH}template/icons/26px/google.png"> 42 <img src="{$OAUTH_PATH}template/icons/26px/twitter.png"> 43 </label> 44 <label> 45 <input type="radio" name="identification_icon" value="38px" {if $identification_icon=='38px'}checked="checked"{/if}> 46 38px 47 <img src="{$OAUTH_PATH}template/icons/38px/facebook.png"> 48 <img src="{$OAUTH_PATH}template/icons/38px/google.png"> 49 <img src="{$OAUTH_PATH}template/icons/38px/twitter.png"> 50 </label> 51 </li> 79 52 80 <td> 81 <select name="providers[{$p}][enabled]" class="enable"> 82 <option value="true" {if $CONFIG[$p].enabled}selected="selected"{/if}>{'Enabled'|@translate}</option> 83 <option value="false" {if not $CONFIG[$p].enabled}selected="selected"{/if}>{'Disabled'|@translate}</option> 84 </select> 85 <br><a href="#" class="open-help">{'Help'|@translate}</a> 86 </td> 87 88 {if $provider.new_app_link} 89 <td class="keys" {if not $CONFIG[$p].enabled}style="display:none;"{/if}> 90 {if $provider.require_client_id} 91 <label for="{$p}_app_id">Application ID</label> 92 <input type="text" id="{$p}_app_id" name="providers[{$p}][keys][id]" value="{$CONFIG[$p].keys.id}"> 93 {else} 94 <label for="{$p}_key">Application Key</label> 95 <input type="text" id="{$p}_key" name="providers[{$p}][keys][key]" value="{$CONFIG[$p].keys.key}"> 96 {/if} 97 <label for="{$p}_secret">Application Secret</label> 98 <input type="text" id="{$p}_secret" name="providers[{$p}][keys][secret]" value="{$CONFIG[$p].keys.secret}"> 99 <br> 100 </td> 101 {/if} 102 </tr></table> 103 104 <div class="help"> 105 {if $provider.new_app_link} 106 <ol> 107 <li>{'Go to <a href="%s" target="_blank">%s</a> and <b>create a new application</b>'|@translate|sprintf:$provider.new_app_link:$provider.new_app_link}</li> 108 109 {if $p=='Google'} 110 <li>{'On the <b>API Access</b> tab, <b>create an OAuth 2.0 Client ID</b>'|@translate}</li> 111 <li>{'Fill out any required fields such as the application name and description'|@translate}</li> 112 <li>{'On the <b>Create Client ID</b> popup switch to advanced settings by clicking on <b>(more options)</b>'|@translate}</li> 113 {else} 114 <li>{'Fill out any required fields such as the application name and description'|@translate}</li> 115 {/if} 116 117 {if $provider.callback} 118 <li> 119 {assign var=callback value=$OAUTH_CALLBACK|cat:$p} 120 {'Provide this URL as the Callback/Redirect URL for your application: <em>%s</em>'|@translate|sprintf:$callback} 121 </li> 122 {/if} 123 124 {if $p=='Live'} 125 <li>{'Put your website domain in the %s fields. It should match with the current hostname: <em>%s</em>'|@translate|sprintf:'<b>Redirect Domain</b>':$SERVERNAME}</li> 126 {elseif $p=='Facebook'} 127 <li>{'Select <em>Website with facebook authentication</em> as application type'|@translate}</li> 128 <li>{'Put your website domain in the %s fields. It should match with the current hostname: <em>%s</em>'|@translate|sprintf:'<b>Site Url</b>, <b>App Domains</b>':$SERVERNAME}</li> 129 {elseif $p=='LinkedIn'} 130 <li>{'Put your website domain in the %s fields. It should match with the current hostname: <em>%s</em>'|@translate|sprintf:'<b>Website URL</b>':$SERVERNAME}</li> 131 <li>{'Set the <b>Application Type</b> to <em>Web Application</em>'|@translate}</li> 132 {elseif $p=='Yahoo'} 133 <li>{'Put your website domain in the %s fields. It should match with the current hostname: <em>%s</em>'|@translate|sprintf:'<b>Application URL</b>, <b>Application Domain</b>':$SERVERNAME}</li> 134 <li>{'Set the <b>Kind of Application</b> to <em>Web-based</em>'|@translate}</li> 135 <li>{'Set the <b>Access Scopes</b> to <em>This app will only access public...</em>'|@translate}</li> 136 {elseif $p=='Twitter'} 137 <li>{'Put your website domain in the %s fields. It should match with the current hostname: <em>%s</em>'|@translate|sprintf:'<b>Application Website</b>, <b>Application Callback URL</b>':$SERVERNAME}</li> 138 <li>{'Set the <b>Default Access Type</b> to <em>Read only</em>'|@translate}</li> 139 {elseif $p=='Tumblr'} 140 <li>{'Put your website domain in the %s fields. It should match with the current hostname: <em>%s</em>'|@translate|sprintf:'<b>Application Website</b>, <b>Default Callback URL</b>':$SERVERNAME}</li> 141 {/if} 142 143 <li>{'Once you have registered, copy and past the created application credentials into this setup page'|@translate}</li> 144 </ol> 145 {else} 146 <p>{'No registration required for OpenID based providers'|@translate}</p> 147 {/if} 148 </div> 149 </div> 150 {/foreach} 151 53 <li> 54 <b>{'Icon size in the menubar'|@translate} :</b><br> 55 <label> 56 <input type="radio" name="menubar_icon" value="16px" {if $menubar_icon=='16px'}checked="checked"{/if}> 57 16px 58 <img src="{$OAUTH_PATH}template/icons/16px/facebook.png"> 59 <img src="{$OAUTH_PATH}template/icons/16px/google.png"> 60 <img src="{$OAUTH_PATH}template/icons/16px/twitter.png"> 61 </label> 62 <label> 63 <input type="radio" name="menubar_icon" value="26px" {if $menubar_icon=='26px'}checked="checked"{/if}> 64 26px 65 <img src="{$OAUTH_PATH}template/icons/26px/facebook.png"> 66 <img src="{$OAUTH_PATH}template/icons/26px/google.png"> 67 <img src="{$OAUTH_PATH}template/icons/26px/twitter.png"> 68 </label> 69 <label> 70 <input type="radio" name="menubar_icon" value="38px" {if $menubar_icon=='38px'}checked="checked"{/if}> 71 38px 72 <img src="{$OAUTH_PATH}template/icons/38px/facebook.png"> 73 <img src="{$OAUTH_PATH}template/icons/38px/google.png"> 74 <img src="{$OAUTH_PATH}template/icons/38px/twitter.png"> 75 </label> 76 </li> 77 </ul> 152 78 </fieldset> 153 79 154 80 <p style="text-align:left;"><input type="submit" name="save_config" value="{'Save Settings'|@translate}"></p> 155 156 81 </form> 82 83 <p>Icons from http://www.wpzoom.com - Library from http://hybridauth.sourceforge.net/</p> -
extensions/oAuth/admin/template/style.css
r20293 r20323 51 51 border-radius:2px; 52 52 } 53 .provider img { 54 margin:5px; 55 } 56 .Wordpress, .OpenID, .Flickr { 57 display:inline-block; 58 } 53 59 54 60 #help_container { -
extensions/oAuth/auth.php
r20301 r20323 88 88 4 : Missing provider application credentials 89 89 5 : Authentication aborded 90 6 : User profile request failed 90 91 other errors : 91 92 1002 : Invalid provider -
extensions/oAuth/include/install.inc.php
r20293 r20323 11 11 'display_menubar' => true, 12 12 'display_register' => true, 13 'identification_icon' => '38px', 14 'menubar_icon' => '26px', 13 15 )); 14 16 -
extensions/oAuth/include/public_events.inc.php
r20301 r20323 7 7 function oauth_begin_identification() 8 8 { 9 global $template; 10 11 // template icons 12 if ($template->get_template_vars('OAUTH_URL') == null) 13 { 14 $template->assign(array( 15 'OAUTH_URL' => get_root_url() . OAUTH_PATH . 'auth.php?provider=', 16 'OAUTH_PATH' => OAUTH_PATH, 17 'OAUTH_ABS_PATH' => realpath(OAUTH_PATH) . '/', 18 'PROVIDERS' => get_activated_providers(), 19 )); 20 } 21 9 global $template, $conf; 10 11 oauth_assign_template_vars(); 22 12 $template->assign('REDIRECT_TO', !empty($_GET['redirect']) ? urldecode($_GET['redirect']) : get_gallery_home_url()); 23 13 … … 58 48 function oauth_begin_register() 59 49 { 60 global $conf, $template, $hybridauth_conf ;50 global $conf, $template, $hybridauth_conf, $page; 61 51 62 52 // comming from identification page … … 71 61 $adapter = $hybridauth->authenticate($provider); 72 62 $remote_user = $adapter->getUserProfile(); 63 64 $template->assign(array( 65 'OAUTH_PROVIDER' => $provider, 66 'OAUTH_USERNAME' => $remote_user->displayName, 67 'OAUTH_PROFILE_URL' => $remote_user->profileURL, 68 'OAUTH_AVATAR' => $remote_user->photoURL, 69 'OAUTH_PATH' => OAUTH_PATH, 70 )); 71 72 array_push($page['infos'], l10n('Your registration is almost done, please complete the registration form.')); 73 73 74 74 $oauth_id = $provider.'---'.$remote_user->identifier; … … 119 119 120 120 // template 121 $template->set_prefilter('register', 'oauth_add_profile_prefilter'); 121 122 $template->set_prefilter('register', 'oauth_remove_password_fields_prefilter'); 122 123 } … … 128 129 else if ($conf['oauth']['display_register']) 129 130 { 130 // template icons 131 if ($template->get_template_vars('OAUTH_URL') == null) 132 { 133 $template->assign(array( 134 'OAUTH_URL' => get_root_url() . OAUTH_PATH . 'auth.php?provider=', 135 'OAUTH_PATH' => OAUTH_PATH, 136 'OAUTH_ABS_PATH' => realpath(OAUTH_PATH) . '/', 137 'PROVIDERS' => get_activated_providers(), 138 )); 139 } 140 131 oauth_assign_template_vars(); 141 132 $template->assign('REDIRECT_TO', get_gallery_home_url()); 142 133 … … 178 169 'OAUTH_PROVIDER' => $provider, 179 170 'OAUTH_USERNAME' => $remote_user->displayName, 180 'OAUTH_ URL' => $remote_user->profileURL,171 'OAUTH_PROFILE_URL' => $remote_user->profileURL, 181 172 'OAUTH_AVATAR' => $remote_user->photoURL, 182 173 'OAUTH_PATH' => OAUTH_PATH, … … 241 232 } 242 233 243 if ($template->get_template_vars('OAUTH_URL') == null) 244 { 245 $template->assign(array( 246 'OAUTH_URL' => get_root_url() . OAUTH_PATH . 'auth.php?provider=', 247 'OAUTH_PATH' => OAUTH_PATH, 248 'OAUTH_ABS_PATH' => realpath(OAUTH_PATH) . '/', 249 'PROVIDERS' => get_activated_providers(), 250 )); 251 } 252 234 oauth_assign_template_vars(); 253 235 $template->assign('REDIRECT_TO', get_gallery_home_url()); 254 236 … … 282 264 function oauth_add_profile_prefilter($content) 283 265 { 284 $search = ' <legend>{\'Registration\'|@translate}</legend>';266 $search = '#</legend>#'; 285 267 $add = file_get_contents(OAUTH_PATH . 'template/profile.tpl'); 286 return str_replace($search, $search.$add, $content);268 return preg_replace($search, '</legend> '.$add, $content, 1); 287 269 } 288 270 … … 294 276 } 295 277 278 279 function oauth_assign_template_vars() 280 { 281 global $template, $conf; 282 283 if ($template->get_template_vars('OAUTH_URL') == null) 284 { 285 $template->assign(array( 286 'oauth' => $conf['oauth'], 287 'OAUTH_URL' => get_root_url() . OAUTH_PATH . 'auth.php?provider=', 288 'OAUTH_PATH' => OAUTH_PATH, 289 'OAUTH_ABS_PATH' => realpath(OAUTH_PATH) . '/', 290 'PROVIDERS' => get_activated_providers(), 291 'ABS_ROOT_URL' => get_gallery_home_url(), 292 )); 293 } 294 } 296 295 ?> -
extensions/oAuth/main.inc.php
r20301 r20323 115 115 } 116 116 } 117 118 // in case of registration aborded 119 if (script_basename() != 'register') 120 { 121 pwg_unset_session_var('oauth_new_user'); 122 } 117 123 } 118 124 -
extensions/oAuth/template/identification_common.tpl
r20301 r20323 24 24 url = "{$REDIRECT_TO}"; 25 25 if (typeof type != 'undefined' && type != 'default') {ldelim} 26 url = "{$ ROOT_URL}"+ type +".php";26 url = "{$ABS_ROOT_URL}"+ type +".php"; 27 27 } 28 28 window.location.href = url; … … 34 34 url+ "&t=" + (new Date()).getTime(), 35 35 "hybridauth_social_sing_on", 36 "location=0,status=0,scrollbars=0,width= 600,height=350"36 "location=0,status=0,scrollbars=0,width=800,height=500" 37 37 ); 38 38 } 39 39 40 40 // click on a button 41 $("a [class^='oauth_']").click(function() {ldelim}41 $("a.oauth").click(function() {ldelim} 42 42 var idp = $(this).attr('title'); 43 43 … … 52 52 53 53 $("#openid_form").css('background-color', $("#the_page #content").css('background-color')); 54 $("#openid_form img").attr('src', '{$ROOT_URL}{$OAUTH_PATH}template/icons/ '+ idp.toLowerCase() +'_big.png');54 $("#openid_form img").attr('src', '{$ROOT_URL}{$OAUTH_PATH}template/icons/38px/'+ idp.toLowerCase() +'.png'); 55 55 $("#openid_form h3").html(idp); 56 56 $("#openid_form").data('idp', idp); … … 105 105 </div> 106 106 <div> 107 <br> 107 108 <label id="openid_label" for="openid_identifier">Open ID URL</label> 108 109 <br> -
extensions/oAuth/template/identification_menubar.tpl
r20293 r20323 4 4 {include file=$OAUTH_ABS_PATH|@cat:'template/identification_common.tpl'} 5 5 {/if} 6 7 6 {html_head}{literal} 8 7 <style type="text/css"> 9 #menubardl#mbIdentification dd:first-of-type {8 dl#mbIdentification dd:first-of-type { 10 9 padding-bottom:0 !important; 10 } 11 #mbIdentification .oauth { 12 margin:0 1px; 11 13 } 12 14 </style> 13 15 {/literal}{/html_head} 16 14 17 <dd> 15 18 <form id="quickconnect"> … … 17 20 <legend>{'Or sign in with'|@translate}</legend> 18 21 19 {foreach from=$PROVIDERS item=provider key=p} 20 <a href="#" class="oauth _{$p}" title="{$p}"><img src="{$ROOT_URL}{$OAUTH_PATH}template/icons/{$p|strtolower}_small.png"></a>21 {/ foreach}22 {foreach from=$PROVIDERS item=provider key=p}{strip} 23 <a href="#" class="oauth" title="{$p}"><img src="{$ROOT_URL}{$OAUTH_PATH}template/icons/{$oauth.menubar_icon}/{$p|strtolower}.png"></a> 24 {/strip}{/foreach} 22 25 </fieldset> 23 26 </form> -
extensions/oAuth/template/identification_page.tpl
r20293 r20323 3 3 {include file=$OAUTH_ABS_PATH|@cat:'template/identification_common.tpl'} 4 4 {/if} 5 {html_head}{literal} 6 <style type="text/css"> 7 #oauth_wrap .oauth { 8 margin:0 2px; 9 } 10 </style> 11 {/literal}{/html_head} 5 12 6 <fieldset style="text-align:center;" >13 <fieldset style="text-align:center;" id="oauth_wrap"> 7 14 <legend>{'Or sign in with'|@translate}</legend> 8 15 9 {foreach from=$PROVIDERS item=provider key=p} 10 <a href="#" class="oauth _{$p}" title="{$p}"><img src="{$ROOT_URL}{$OAUTH_PATH}template/icons/{$p|strtolower}_big.png"></a>11 {/ foreach}16 {foreach from=$PROVIDERS item=provider key=p}{strip} 17 <a href="#" class="oauth" title="{$p}"><img src="{$ROOT_URL}{$OAUTH_PATH}template/icons/{$oauth.identification_icon}/{$p|strtolower}.png"></a> 18 {/strip}{/foreach} 12 19 </fieldset> -
extensions/oAuth/template/profile.tpl
r20301 r20323 29 29 {'Logged with'|@translate} : <b>{$OAUTH_PROVIDER}</b><br> 30 30 <b>{'Username'|@translate}</b> : {$OAUTH_USERNAME}<br> 31 <b>{'Profile URL'|@translate}</b> : <a href="{$OAUTH_ URL}">{$OAUTH_URL|truncate:40:' ... ':true:true}</a>31 <b>{'Profile URL'|@translate}</b> : <a href="{$OAUTH_PROFILE_URL}">{$OAUTH_PROFILE_URL|truncate:40:' ... ':true:true}</a> 32 32 </div>
Note: See TracChangeset
for help on using the changeset viewer.