diff options
Diffstat (limited to 'apps')
51 files changed, 2480 insertions, 3336 deletions
diff --git a/apps/calendar/ajax/import/calendarcheck.php b/apps/calendar/ajax/import/calendarcheck.php new file mode 100644 index 00000000000..a91bab70573 --- /dev/null +++ b/apps/calendar/ajax/import/calendarcheck.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +OCP\JSON::checkLoggedIn(); +OCP\App::checkAppEnabled('calendar'); +$calname = strip_tags($_POST['calname']); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser()); +foreach($calendars as $calendar){ + if($calendar['displayname'] == $calname){ + OCP\JSON::success(array('message'=>'exists')); + exit; + } +} +OCP\JSON::error();
\ No newline at end of file diff --git a/apps/calendar/ajax/import/dialog.php b/apps/calendar/ajax/import/dialog.php index b99c32278c4..18fe226172c 100644 --- a/apps/calendar/ajax/import/dialog.php +++ b/apps/calendar/ajax/import/dialog.php @@ -5,8 +5,6 @@ * later. * See the COPYING-README file. */ - - OCP\JSON::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); $tmpl = new OCP\Template('calendar', 'part.import'); diff --git a/apps/calendar/ajax/import/dropimport.php b/apps/calendar/ajax/import/dropimport.php index 87667d4de68..f46e7314098 100644 --- a/apps/calendar/ajax/import/dropimport.php +++ b/apps/calendar/ajax/import/dropimport.php @@ -1,73 +1,32 @@ <?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ $data = $_POST['data']; $data = explode(',', $data); $data = end($data); $data = base64_decode($data); OCP\JSON::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); -$nl="\r\n"; -$comps = array('VEVENT'=>true, 'VTODO'=>true, 'VJOURNAL'=>true); -$data = str_replace(array("\r","\n\n"), array("\n","\n"), $data); -$lines = explode("\n", $data); -unset($data); -$comp=$uid=$cal=false; -$cals=$uids=array(); -$i = 0; -foreach($lines as $line) { - if(strpos($line, ':')!==false) { - list($attr, $val) = explode(':', strtoupper($line)); - if ($attr == 'BEGIN' && $val == 'VCALENDAR') { - $cal = $i; - $cals[$cal] = array('first'=>$i,'last'=>$i,'end'=>$i); - } elseif ($attr =='BEGIN' && $cal!==false && isset($comps[$val])) { - $comp = $val; - $beginNo = $i; - } elseif ($attr == 'END' && $cal!==false && $val == 'VCALENDAR') { - if($comp!==false) { - unset($cals[$cal]); // corrupt calendar, unset it - } else { - $cals[$cal]['end'] = $i; - } - $comp=$uid=$cal=false; // reset calendar - } elseif ($attr == 'END' && $comp!==false && $val == $comp) { - if(! $uid) { - $uid = OC_Calendar_Object::createUID(); - } - $uids[$uid][$beginNo] = array('end'=>$i, 'cal'=>$cal); - if ($cals[$cal]['first'] == $cal) { - $cals[$cal]['first'] = $beginNo; - } - $cals[$cal]['last'] = $i; - $comp=$uid=false; // reset component - } elseif ($attr =="UID" && $comp!==false) { - list($attr, $uid) = explode(':', $line); - } - } - $i++; +$import = new OC_Calendar_Import($data); +$import->setUserID(OCP\User::getUser()); +$import->setTimeZone(OC_Calendar_App::$tz); +$import->disableProgressCache(); +if(!$import->isValid()){ + OCP\JSON::error(); + exit; } -$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), true); -$id = $calendars[0]['id']; -foreach($uids as $uid) { - $prefix=$suffix=$content=array(); - foreach($uid as $begin=>$details) { - $cal = $details['cal']; - if(!isset($cals[$cal])) { - continue; // from corrupt/incomplete calendar - } - $cdata = $cals[$cal]; - // if we have multiple components from different calendar objects, - // we should really merge their elements (enhancement?) -- 1st one wins for now. - if(! count($prefix)) { - $prefix = array_slice($lines, $cal, $cdata['first'] - $cal); - } - if(! count($suffix)) { - $suffix = array_slice($lines, $cdata['last']+1, $cdata['end'] - $cdata['last']); - } - $content = array_merge($content, array_slice($lines, $begin, $details['end'] - $begin + 1)); - } - if(count($content)) { - $import = join($nl, array_merge($prefix, $content, $suffix)) . $nl; - OC_Calendar_Object::add($id, $import); - } -} -OCP\JSON::success();
\ No newline at end of file +$newcalendarname = strip_tags($import->createCalendarName()); +$newid = OC_Calendar_Calendar::addCalendar(OCP\User::getUser(),$newcalendarname,'VEVENT,VTODO,VJOURNAL',null,0,$import->createCalendarColor()); +$import->setCalendarID($newid); +$import->import(); +$count = $import->getCount(); +if($count == 0){ + OC_Calendar_Calendar::deleteCalendar($newid); + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.'))); +}else{ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' . $newcalendarname, 'eventSource'=>OC_Calendar_Calendar::getEventSourceInfo(OC_Calendar_Calendar::find($newid)))); +}
\ No newline at end of file diff --git a/apps/calendar/ajax/import/import.php b/apps/calendar/ajax/import/import.php index 38008af4a9d..b1dfc464d00 100644 --- a/apps/calendar/ajax/import/import.php +++ b/apps/calendar/ajax/import/import.php @@ -5,42 +5,71 @@ * later. * See the COPYING-README file. */ -//check for calendar rights or create new one -ob_start(); - OCP\JSON::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); OCP\JSON::callCheck(); session_write_close(); - -$nl="\r\n"; -$comps = array('VEVENT'=>true, 'VTODO'=>true, 'VJOURNAL'=>true); - -global $progresskey; -$progresskey = 'calendar.import-' . $_POST['progresskey']; - -if (isset($_POST['progress']) && $_POST['progress']) { - echo OC_Cache::get($progresskey); - die; +if (isset($_POST['progresskey']) && isset($_POST['getprogress'])) { + echo OCP\JSON::success(array('percent'=>OC_Cache::get($_POST['progresskey']))); + exit; } - -function writeProgress($pct) { - global $progresskey; - OC_Cache::set($progresskey, $pct, 300); -} -writeProgress('10'); $file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']); +if(!$file){ + OCP\JSON::error(array('error'=>'404')); +} +$import = new OC_Calendar_Import($file); +$import->setUserID(OCP\User::getUser()); +$import->setTimeZone(OC_Calendar_App::$tz); +$import->enableProgressCache(); +$import->setProgresskey($_POST['progresskey']); +if(!$import->isValid()){ + OCP\JSON::error(array('error'=>'notvalid')); + exit; +} +$newcal = false; if($_POST['method'] == 'new'){ - $id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), $_POST['calname']); - OC_Calendar_Calendar::setCalendarActive($id, 1); + $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser()); + foreach($calendars as $calendar){ + if($calendar['displayname'] == $_POST['calname']){ + $id = $calendar['id']; + $newcal = false; + break; + } + $newcal = true; + } + if($newcal){ + $id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), strip_tags($_POST['calname']),'VEVENT,VTODO,VJOURNAL',null,0,strip_tags($_POST['calcolor'])); + OC_Calendar_Calendar::setCalendarActive($id, 1); + } }else{ $calendar = OC_Calendar_App::getCalendar($_POST['id']); if($calendar['userid'] != OCP\USER::getUser()){ - OCP\JSON::error(); + OCP\JSON::error(array('error'=>'missingcalendarrights')); exit(); } $id = $_POST['id']; } +$import->setCalendarID($id); +try{ + $import->import(); +}catch (Exception $e) { + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('Import failed'), 'debug'=>$e->getMessage())); + //write some log +} +$count = $import->getCount(); +if($count == 0){ + if($newcal){ + OC_Calendar_Calendar::deleteCalendar($id); + } + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.'))); +}else{ + if($newcal){ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' . strip_tags($_POST['calname']))); + }else{ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in your calendar'))); + } +} +/* //////////////////////////// Attention: following code is quite painfull !!! /////////////////////// writeProgress('20'); // normalize the newlines $file = str_replace(array("\r","\n\n"), array("\n","\n"), $file); @@ -92,7 +121,6 @@ foreach($lines as $line) { // import the calendar writeProgress('60'); foreach($uids as $uid) { - $prefix=$suffix=$content=array(); foreach($uid as $begin=>$details) { @@ -120,4 +148,4 @@ foreach($uids as $uid) { writeProgress('100'); sleep(3); OC_Cache::remove($progresskey); -OCP\JSON::success();
\ No newline at end of file +OCP\JSON::success();*/ diff --git a/apps/calendar/appinfo/app.php b/apps/calendar/appinfo/app.php index c8fccc326c3..4fdba291262 100644 --- a/apps/calendar/appinfo/app.php +++ b/apps/calendar/appinfo/app.php @@ -9,6 +9,7 @@ OC::$CLASSPATH['OC_Calendar_Repeat'] = 'apps/calendar/lib/repeat.php'; OC::$CLASSPATH['OC_Calendar_Share'] = 'apps/calendar/lib/share.php'; OC::$CLASSPATH['OC_Search_Provider_Calendar'] = 'apps/calendar/lib/search.php'; OC::$CLASSPATH['OC_Calendar_Export'] = 'apps/calendar/lib/export.php'; +OC::$CLASSPATH['OC_Calendar_Import'] = 'apps/calendar/lib/import.php'; //General Hooks OCP\Util::connectHook('OC_User', 'post_createUser', 'OC_Calendar_Hooks', 'createUser'); OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OC_Calendar_Hooks', 'deleteUser'); @@ -24,6 +25,8 @@ OCP\Util::connectHook('OC_Calendar', 'deleteCalendar', 'OC_Calendar_Share', 'pos OCP\Util::addscript('calendar','loader'); OCP\Util::addscript("3rdparty", "chosen/chosen.jquery.min"); OCP\Util::addStyle("3rdparty", "chosen/chosen"); +OCP\Util::addStyle('3rdparty/miniColors', 'jquery.miniColors'); +OCP\Util::addscript('3rdparty/miniColors', 'jquery.miniColors.min'); OCP\App::addNavigationEntry( array( 'id' => 'calendar_index', 'order' => 10, diff --git a/apps/calendar/css/import.css b/apps/calendar/css/import.css new file mode 100644 index 00000000000..8abdc3aecd1 --- /dev/null +++ b/apps/calendar/css/import.css @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +#calendar_import_newcalform, #calendar_import_mergewarning, #calendar_import_process, #calendar_import_done{display:none;} +#calendar_import_process_message, #calendar_import_status, #calendar_import_form_message, #calendar_import_mergewarning{text-align:center;} +#calendar_import_form_message{font-weight: bold;} +#calendar_import_newcalendar{width:415px;float:right;} +#calendar_import_mergewarning{clear: both;} +#calendar_import_defaultcolors{clear:both;margin: 0 auto;text-align: center;} +.calendar_import_warning{border-color: #fc3333;} +.calendar-colorpicker-color{display:inline-block;width:20px;height:5px;margin: 0 auto;cursor:pointer;border:2px solid transparent;}
\ No newline at end of file diff --git a/apps/calendar/js/calendar.js b/apps/calendar/js/calendar.js index 004b2386fb1..25311fa0df4 100644 --- a/apps/calendar/js/calendar.js +++ b/apps/calendar/js/calendar.js @@ -622,18 +622,11 @@ Calendar={ drop:function(e){ var files = e.dataTransfer.files; for(var i = 0;i < files.length;i++){ - var file = files[i] + var file = files[i]; reader = new FileReader(); reader.onload = function(event){ - if(file.type != 'text/calendar'){ - $('#notification').html('At least one file don\'t seems to be a calendar file. File skipped.'); - $('#notification').slideDown(); - window.setTimeout(function(){$('#notification').slideUp();}, 5000); - return false; - }else{ - Calendar.UI.Drop.import(event.target.result); - $('#calendar_holder').fullCalendar('refetchEvents'); - } + Calendar.UI.Drop.import(event.target.result); + $('#calendar_holder').fullCalendar('refetchEvents'); } reader.readAsDataURL(file); } @@ -641,9 +634,13 @@ Calendar={ import:function(data){ $.post(OC.filePath('calendar', 'ajax/import', 'dropimport.php'), {'data':data},function(result) { if(result.status == 'success'){ + $('#calendar_holder').fullCalendar('addEventSource', result.eventSource); + $('#notification').html(result.message); + $('#notification').slideDown(); + window.setTimeout(function(){$('#notification').slideUp();}, 5000); return true; }else{ - $('#notification').html('ownCloud wasn\'t able to import at least one file. File skipped.'); + $('#notification').html(result.message); $('#notification').slideDown(); window.setTimeout(function(){$('#notification').slideUp();}, 5000); } diff --git a/apps/calendar/js/loader.js b/apps/calendar/js/loader.js index cef95afc3aa..b28d19ab00e 100644 --- a/apps/calendar/js/loader.js +++ b/apps/calendar/js/loader.js @@ -5,77 +5,175 @@ * See the COPYING-README file. */ Calendar_Import={ - importdialog: function(filename){ - var path = $('#dir').val(); - $('body').append('<div id="calendar_import"></div>'); - $('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:filename, path:path}, function(){Calendar_Import.initdialog(filename);}); + Store:{ + file: '', + path: '', + id: 0, + method: '', + calname: '', + calcolor: '', + progresskey: '', + percentage: 0 }, - initdialog: function(filename){ - $('#calendar_import_dialog').dialog({ - width : 500, - close : function() { - $(this).dialog('destroy').remove(); - $('#calendar_import').remove(); + Dialog:{ + open: function(filename){ + OC.addStyle('calendar', 'import'); + Calendar_Import.Store.file = filename; + Calendar_Import.Store.path = $('#dir').val(); + $('body').append('<div id="calendar_import"></div>'); + $('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:Calendar_Import.Store.file, path:Calendar_Import.Store.path},function(){ + Calendar_Import.Dialog.init(); + }); + }, + close: function(){ + Calendar_Import.reset(); + $(this).dialog('destroy').remove(); + $('#calendar_import_dialog').remove(); + }, + init: function(){ + //init dialog + $('#calendar_import_dialog').dialog({ + width : 500, + resizable: false, + close : function() { + Calendar_Import.Dialog.close(); + } + }); + //init buttons + $('#calendar_import_done').click(function(){ + Calendar_Import.Dialog.close(); + }); + $('#calendar_import_submit').click(function(){ + Calendar_Import.Core.process(); + }); + $('#calendar_import_mergewarning').click(function(){ + $('#calendar_import_newcalendar').attr('value', $('#calendar_import_availablename').val()); + Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val()); + }); + $('#calendar_import_calendar').change(function(){ + if($('#calendar_import_calendar option:selected').val() == 'newcal'){ + $('#calendar_import_newcalform').slideDown('slow'); + Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val()); + }else{ + $('#calendar_import_newcalform').slideUp('slow'); + $('#calendar_import_mergewarning').slideUp('slow'); + } + }); + $('#calendar_import_newcalendar').keyup(function(){ + Calendar_Import.Dialog.mergewarning($.trim($('#calendar_import_newcalendar').val())); + }); + $('#calendar_import_newcalendar_color').miniColors({ + letterCase: 'uppercase' + }); + $('.calendar-colorpicker-color').click(function(){ + var str = $(this).attr('rel'); + str = str.substr(1); + $('#calendar_import_newcalendar_color').attr('value', str); + $(".color-picker").miniColors('value', '#' + str); + }); + //init progressbar + $('#calendar_import_progressbar').progressbar({value: Calendar_Import.Store.percentage}); + Calendar_Import.Store.progresskey = $('#calendar_import_progresskey').val(); + }, + mergewarning: function(newcalname){ + $.post(OC.filePath('calendar', 'ajax/import', 'calendarcheck.php'), {calname: newcalname}, function(data){ + if(data.message == 'exists'){ + $('#calendar_import_mergewarning').slideDown('slow'); + }else{ + $('#calendar_import_mergewarning').slideUp('slow'); + } + }); + }, + update: function(){ + if(Calendar_Import.Store.percentage == 100){ + return false; } - }); - $('#import_done_button').click(function(){ - $('#calendar_import_dialog').dialog('destroy').remove(); - $('#calendar_import').remove(); - }); - $('#progressbar').progressbar({value: 0}); - $('#startimport').click(function(){ - var filename = $('#filename').val(); - var path = $('#path').val(); - var calid = $('#calendar option:selected').val(); - if($('#calendar option:selected').val() == 'newcal'){ - var method = 'new'; - var calname = $('#newcalendar').val(); - var calname = $.trim(calname); - if(calname == ''){ - $('#newcalendar').css('background-color', '#FF2626'); - $('#newcalendar').focus(function(){ - $('#newcalendar').css('background-color', '#F8F8F8'); - }); - return false; + $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progresskey: Calendar_Import.Store.progresskey, getprogress: true}, function(data){ + if(data.status == 'success'){ + if(data.percent == null){ + return false; + } + Calendar_Import.Store.percentage = parseInt(data.percent); + $('#calendar_import_progressbar').progressbar('option', 'value', parseInt(data.percent)); + if(data.percent < 100 ){ + window.setTimeout('Calendar_Import.Dialog.update()', 250); + }else{ + $('#calendar_import_done').css('display', 'block'); + } + }else{ + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + $('#calendar_import_progressbar > div').css('background-color', '#FF2626'); + $('#calendar_import_status').html(data.message); } - }else{ - var method = 'old'; + }); + return 0; + }, + warning: function(selector){ + $(selector).addClass('calendar_import_warning'); + $(selector).focus(function(){ + $(selector).removeClass('calendar_import_warning'); + }); + } + }, + Core:{ + process: function(){ + var validation = Calendar_Import.Core.prepare(); + if(validation){ + $('#calendar_import_form').css('display', 'none'); + $('#calendar_import_process').css('display', 'block'); + $('#calendar_import_newcalendar').attr('readonly', 'readonly'); + $('#calendar_import_calendar').attr('disabled', 'disabled'); + Calendar_Import.Core.send(); + window.setTimeout('Calendar_Import.Dialog.update()', 250); } - $('#newcalendar').attr('readonly', 'readonly'); - $('#calendar').attr('disabled', 'disabled'); - var progresskey = $('#progresskey').val(); - $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progresskey: progresskey, method: String (method), calname: String (calname), path: String (path), file: String (filename), id: String (calid)}, function(data){ + }, + send: function(){ + $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), + {progresskey: Calendar_Import.Store.progresskey, method: String (Calendar_Import.Store.method), calname: String (Calendar_Import.Store.calname), path: String (Calendar_Import.Store.path), file: String (Calendar_Import.Store.file), id: String (Calendar_Import.Store.id), calcolor: String (Calendar_Import.Store.calcolor)}, function(data){ if(data.status == 'success'){ - $('#progressbar').progressbar('option', 'value', 100); - $('#import_done').css('display', 'block'); + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + Calendar_Import.Store.percentage = 100; + $('#calendar_import_done').css('display', 'block'); + $('#calendar_import_status').html(data.message); + }else{ + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + $('#calendar_import_progressbar > div').css('background-color', '#FF2626'); + $('#calendar_import_status').html(data.message); } }); - $('#form_container').css('display', 'none'); - $('#progressbar_container').css('display', 'block'); - window.setTimeout('Calendar_Import.getimportstatus(\'' + progresskey + '\')', 500); - }); - $('#calendar').change(function(){ - if($('#calendar option:selected').val() == 'newcal'){ - $('#newcalform').slideDown('slow'); + }, + prepare: function(){ + Calendar_Import.Store.id = $('#calendar_import_calendar option:selected').val(); + if($('#calendar_import_calendar option:selected').val() == 'newcal'){ + Calendar_Import.Store.method = 'new'; + Calendar_Import.Store.calname = $.trim($('#calendar_import_newcalendar').val()); + if(Calendar_Import.Store.calname == ''){ + Calendar_Import.Dialog.warning('#calendar_import_newcalendar'); + return false; + } + Calendar_Import.Store.calcolor = $.trim($('#calendar_import_newcalendar_color').val()); + if(Calendar_Import.Store.calcolor == ''){ + Calendar_Import.Store.calcolor = $('.calendar-colorpicker-color:first').attr('rel'); + } }else{ - $('#newcalform').slideUp('slow'); + Calendar_Import.Store.method = 'old'; } - }); + return true; + } }, - getimportstatus: function(progresskey){ - $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progress:1,progresskey: progresskey}, function(percent){ - $('#progressbar').progressbar('option', 'value', parseInt(percent)); - if(percent < 100){ - window.setTimeout('Calendar_Import.getimportstatus(\'' + progresskey + '\')', 500); - }else{ - $('#import_done').css('display', 'block'); - } - }); + reset: function(){ + Calendar_Import.Store.file = ''; + Calendar_Import.Store.path = ''; + Calendar_Import.Store.id = 0; + Calendar_Import.Store.method = ''; + Calendar_Import.Store.calname = ''; + Calendar_Import.Store.progresskey = ''; + Calendar_Import.Store.percentage = 0; } } $(document).ready(function(){ if(typeof FileActions !== 'undefined'){ - FileActions.register('text/calendar','importcal', '', Calendar_Import.importdialog); - FileActions.setDefault('text/calendar','importcal'); + FileActions.register('text/calendar','importCalendar', '', Calendar_Import.Dialog.open); + FileActions.setDefault('text/calendar','importCalendar'); }; }); diff --git a/apps/calendar/js/settings.js b/apps/calendar/js/settings.js index 03e4217573d..60741f2b6fc 100644 --- a/apps/calendar/js/settings.js +++ b/apps/calendar/js/settings.js @@ -34,6 +34,7 @@ $(document).ready(function(){ $.getJSON(OC.filePath('calendar', 'ajax/settings', 'timeformat.php'), function(jsondata, status) { $('#' + jsondata.timeformat).attr('selected',true); $('#timeformat').chosen(); + $('#timeformat_chzn').css('width', '100px'); }); $.getJSON(OC.filePath('calendar', 'ajax/settings', 'gettimezonedetection.php'), function(jsondata, status){ if(jsondata.detection == 'true'){ @@ -43,6 +44,7 @@ $(document).ready(function(){ $.getJSON(OC.filePath('calendar', 'ajax/settings', 'getfirstday.php'), function(jsondata, status) { $('#' + jsondata.firstday).attr('selected',true); $('#firstday').chosen(); + $('#firstday_chzn').css('width', '100px'); }); $('#cleancalendarcache').click(function(){ $.getJSON(OC.filePath('calendar', 'ajax/cache', 'rescan.php'), function(){ @@ -55,7 +57,7 @@ function calendarcachecheck(){ $.getJSON(OC.filePath('calendar', 'ajax/cache', 'status.php'), function(jsondata, status) { $('#cleancalendarcache').attr('title', jsondata.l10n.text); if(jsondata.status == 'success'){ - $('#cleancalendarcache').css('background', '#90EE90'); + $('#cleancalendarcache').css('background', '#F8F8F8'); $('#cleancalendarcache').css('color', '#333'); $('#cleancalendarcache').css('text-shadow', '#fff 0 1px 0'); }else{ diff --git a/apps/calendar/l10n/de.php b/apps/calendar/l10n/de.php index f12a18baad0..33c924ac2c3 100644 --- a/apps/calendar/l10n/de.php +++ b/apps/calendar/l10n/de.php @@ -2,11 +2,17 @@ "No calendars found." => "Keine Kalender gefunden", "No events found." => "Keine Termine gefunden", "Wrong calendar" => "Falscher Kalender", +"Import failed" => "Import fehlgeschlagen", "New Timezone:" => "Neue Zeitzone:", "Timezone changed" => "Zeitzone geändert", "Invalid request" => "Fehlerhafte Anfrage", "Calendar" => "Kalender", -"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "ddd d MMMM[ yyyy]{ -[ddd d] MMMM yyyy}", +"ddd" => "ddd", +"ddd M/d" => "ddd d.M", +"dddd M/d" => "dddd d.M", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, d. MMM yyyy", "Birthday" => "Geburtstag", "Business" => "Geschäftlich", "Call" => "Anruf", @@ -22,7 +28,9 @@ "Projects" => "Projekte", "Questions" => "Fragen", "Work" => "Arbeit", +"by" => "von", "unnamed" => "unbenannt", +"New Calendar" => "Neuer Kalender", "Does not repeat" => "einmalig", "Daily" => "täglich", "Weekly" => "wöchentlich", @@ -67,8 +75,26 @@ "by day and month" => "nach Tag und Monat", "Date" => "Datum", "Cal." => "Kal.", +"Sun." => "So", +"Mon." => "Mo", +"Tue." => "Di", +"Wed." => "Mi", +"Thu." => "Do", +"Fri." => "Fr", +"Sat." => "Sa", +"Jan." => "Jan.", +"Feb." => "Feb.", +"Mar." => "Mär.", +"Apr." => "Apr.", +"May." => "Mai", +"Jun." => "Jun.", +"Jul." => "Jul.", +"Aug." => "Aug.", +"Sep." => "Sep.", +"Oct." => "Okt.", +"Nov." => "Nov.", +"Dec." => "Dez.", "All day" => "Ganztags", -"New Calendar" => "Neuer Kalender", "Missing fields" => "fehlende Felder", "Title" => "Titel", "From Date" => "Startdatum", @@ -132,18 +158,14 @@ "Interval" => "Intervall", "End" => "Ende", "occurrences" => "Termine", -"Import a calendar file" => "Kalenderdatei Importieren", -"Please choose the calendar" => "Bitte wählen Sie den Kalender.", "create a new calendar" => "Neuen Kalender anlegen", +"Import a calendar file" => "Kalenderdatei Importieren", "Name of new calendar" => "Kalendername", "Import" => "Importieren", -"Importing calendar" => "Kalender wird importiert.", -"Calendar imported successfully" => "Kalender erfolgreich importiert", "Close Dialog" => "Dialog schließen", "Create a new event" => "Neues Ereignis", "View an event" => "Termin öffnen", "No categories selected" => "Keine Kategorie ausgewählt", -"Select category" => "Kategorie auswählen", "of" => "von", "at" => "um", "Timezone" => "Zeitzone", @@ -152,9 +174,8 @@ "24h" => "24h", "12h" => "12h", "First day of the week" => "erster Wochentag", -"Calendar CalDAV syncing address:" => "Kalender CalDAV Synchronisationsadresse:", -"Users" => "Nutzer", -"select users" => "Nutzer auswählen", +"Users" => "Benutzer", +"select users" => "Benutzer auswählen", "Editable" => "editierbar", "Groups" => "Gruppen", "select groups" => "Gruppen auswählen", diff --git a/apps/calendar/l10n/it.php b/apps/calendar/l10n/it.php index cdb2d99c82e..b91e8b0df0b 100644 --- a/apps/calendar/l10n/it.php +++ b/apps/calendar/l10n/it.php @@ -1,12 +1,23 @@ <?php $TRANSLATIONS = array( +"Not all calendars are completely cached" => "Non tutti i calendari sono mantenuti completamente in cache", +"Everything seems to be completely cached" => "Tutto sembra essere mantenuto completamente in cache", "No calendars found." => "Nessun calendario trovato.", "No events found." => "Nessun evento trovato.", "Wrong calendar" => "Calendario sbagliato", +"The file contained either no events or all events are already saved in your calendar." => "Il file non conteneva alcun evento o tutti gli eventi erano già salvati nel tuo calendario.", +"events has been saved in the new calendar" => "gli eventi sono stati salvati nel nuovo calendario", +"Import failed" => "Importazione non riuscita", +"events has been saved in your calendar" => "gli eventi sono stati salvati nel tuo calendario", "New Timezone:" => "Nuovo fuso orario:", "Timezone changed" => "Fuso orario cambiato", "Invalid request" => "Richiesta non valida", "Calendar" => "Calendario", +"ddd" => "ggg", +"ddd M/d" => "ggg M/g", +"dddd M/d" => "gggg M/g", +"MMMM yyyy" => "MMMM aaaa", "MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "gggg, MMM g, aaaa", "Birthday" => "Compleanno", "Business" => "Azienda", "Call" => "Chiama", @@ -22,7 +33,9 @@ "Projects" => "Progetti", "Questions" => "Domande", "Work" => "Lavoro", +"by" => "da", "unnamed" => "senza nome", +"New Calendar" => "Nuovo calendario", "Does not repeat" => "Non ripetere", "Daily" => "Giornaliero", "Weekly" => "Settimanale", @@ -67,8 +80,26 @@ "by day and month" => "per giorno e mese", "Date" => "Data", "Cal." => "Cal.", +"Sun." => "Dom.", +"Mon." => "Lun.", +"Tue." => "Mar.", +"Wed." => "Mer.", +"Thu." => "Gio.", +"Fri." => "Ven.", +"Sat." => "Sab.", +"Jan." => "Gen.", +"Feb." => "Feb.", +"Mar." => "Mar.", +"Apr." => "Apr.", +"May." => "Mag.", +"Jun." => "Giu.", +"Jul." => "Lug.", +"Aug." => "Ago.", +"Sep." => "Set.", +"Oct." => "Ott.", +"Nov." => "Nov.", +"Dec." => "Dic.", "All day" => "Tutti il giorno", -"New Calendar" => "Nuovo calendario", "Missing fields" => "Campi mancanti", "Title" => "Titolo", "From Date" => "Dal giorno", @@ -132,18 +163,17 @@ "Interval" => "Intervallo", "End" => "Fine", "occurrences" => "occorrenze", -"Import a calendar file" => "Importa un file di calendario", -"Please choose the calendar" => "Scegli il calendario", "create a new calendar" => "Crea un nuovo calendario", +"Import a calendar file" => "Importa un file di calendario", +"Please choose a calendar" => "Scegli un calendario", "Name of new calendar" => "Nome del nuovo calendario", +"Take an available name!" => "Usa un nome disponibile!", +"A Calendar with this name already exists. If you continue anyhow, these calendars will be merged." => "Un calendario con questo nome esiste già. Se continui, i due calendari saranno uniti.", "Import" => "Importa", -"Importing calendar" => "Importazione del calendario in corso", -"Calendar imported successfully" => "Calendario importato correttamente", "Close Dialog" => "Chiudi la finestra di dialogo", "Create a new event" => "Crea un nuovo evento", "View an event" => "Visualizza un evento", "No categories selected" => "Nessuna categoria selezionata", -"Select category" => "Seleziona una categoria", "of" => "di", "at" => "alle", "Timezone" => "Fuso orario", @@ -152,7 +182,13 @@ "24h" => "24h", "12h" => "12h", "First day of the week" => "Primo giorno della settimana", -"Calendar CalDAV syncing address:" => "Indirizzo sincronizzazione calendario CalDAV:", +"Cache" => "Cache", +"Clear cache for repeating events" => "Cancella gli eventi che si ripetono dalla cache", +"Calendar CalDAV syncing addresses" => "Indirizzi di sincronizzazione calendari CalDAV", +"more info" => "ulteriori informazioni", +"Primary address (Kontact et al)" => "Indirizzo principale (Kontact e altri)", +"iOS/OS X" => "iOS/OS X", +"Read only iCalendar link(s)" => "Collegamento(i) iCalendar sola lettura", "Users" => "Utenti", "select users" => "seleziona utenti", "Editable" => "Modificabile", diff --git a/apps/calendar/l10n/vi.php b/apps/calendar/l10n/vi.php new file mode 100644 index 00000000000..059b89e1635 --- /dev/null +++ b/apps/calendar/l10n/vi.php @@ -0,0 +1,135 @@ +<?php $TRANSLATIONS = array( +"No calendars found." => "Không tìm thấy lịch.", +"No events found." => "Không tìm thấy sự kiện nào", +"Wrong calendar" => "Sai lịch", +"New Timezone:" => "Múi giờ mới :", +"Timezone changed" => "Thay đổi múi giờ", +"Invalid request" => "Yêu cầu không hợp lệ", +"Calendar" => "Lịch", +"ddd" => "ddd", +"ddd M/d" => "ddd M/d", +"dddd M/d" => "dddd M/d", +"MMMM yyyy" => "MMMM yyyy", +"MMM d[ yyyy]{ '—'[ MMM] d yyyy}" => "MMM d[ yyyy]{ '—'[ MMM] d yyyy}", +"dddd, MMM d, yyyy" => "dddd, MMM d, yyyy", +"Birthday" => "Ngày sinh nhật", +"Business" => "Công việc", +"Call" => "Số điện thoại", +"Clients" => "Máy trạm", +"Holidays" => "Ngày lễ", +"Ideas" => "Ý tưởng", +"Jubilee" => "Lễ kỷ niệm", +"Meeting" => "Hội nghị", +"Other" => "Khác", +"Personal" => "Cá nhân", +"Projects" => "Dự án", +"Questions" => "Câu hỏi", +"Work" => "Công việc", +"New Calendar" => "Lịch mới", +"Does not repeat" => "Không lặp lại", +"Daily" => "Hàng ngày", +"Weekly" => "Hàng tuần", +"Every Weekday" => "Mỗi ngày trong tuần", +"Bi-Weekly" => "Hai tuần một lần", +"Monthly" => "Hàng tháng", +"Yearly" => "Hàng năm", +"never" => "không thay đổi", +"by occurrences" => "bởi xuất hiện", +"by date" => "bởi ngày", +"by monthday" => "bởi ngày trong tháng", +"by weekday" => "bởi ngày trong tuần", +"Monday" => "Thứ 2", +"Tuesday" => "Thứ 3", +"Wednesday" => "Thứ 4", +"Thursday" => "Thứ 5", +"Friday" => "Thứ ", +"Saturday" => "Thứ 7", +"Sunday" => "Chủ nhật", +"events week of month" => "sự kiện trong tuần của tháng", +"first" => "đầu tiên", +"second" => "Thứ hai", +"third" => "Thứ ba", +"fourth" => "Thứ tư", +"fifth" => "Thứ năm", +"January" => "Tháng 1", +"February" => "Tháng 2", +"March" => "Tháng 3", +"April" => "Tháng 4", +"May" => "Tháng 5", +"June" => "Tháng 6", +"July" => "Tháng 7", +"August" => "Tháng 8", +"September" => "Tháng 9", +"October" => "Tháng 10", +"November" => "Tháng 11", +"December" => "Tháng 12", +"by events date" => "Theo ngày tháng sự kiện", +"by weeknumber(s)" => "số tuần", +"by day and month" => "ngày, tháng", +"Date" => "Ngày", +"Cal." => "Cal.", +"All day" => "Tất cả các ngày", +"Title" => "Tiêu đề", +"From Date" => "Từ ngày", +"From Time" => "Từ thời gian", +"To Date" => "Tới ngày", +"To Time" => "Tới thời gian", +"The event ends before it starts" => "Sự kiện này kết thúc trước khi nó bắt đầu", +"Week" => "Tuần", +"Month" => "Tháng", +"List" => "Danh sách", +"Today" => "Hôm nay", +"Calendars" => "Lịch", +"There was a fail, while parsing the file." => "Có một thất bại, trong khi phân tích các tập tin.", +"Choose active calendars" => "Chọn lịch hoạt động", +"Your calendars" => "Lịch của bạn", +"CalDav Link" => "Liên kết CalDav ", +"Shared calendars" => "Chia sẻ lịch", +"No shared calendars" => "Không chia sẻ lcihj", +"Share Calendar" => "Chia sẻ lịch", +"Download" => "Tải về", +"Edit" => "Chỉnh sửa", +"Delete" => "Xóa", +"shared with you by" => "Chia sẻ bởi", +"New calendar" => "Lịch mới", +"Edit calendar" => "sửa Lịch", +"Displayname" => "Hiển thị tên", +"Active" => "Kích hoạt", +"Calendar color" => "Màu lịch", +"Save" => "Lưu", +"Submit" => "Submit", +"Cancel" => "Hủy", +"Edit an event" => "Sửa sự kiện", +"Share" => "Chia sẻ", +"Title of the Event" => "Tên sự kiện", +"Category" => "Danh mục", +"All Day Event" => "Sự kiện trong ngày", +"From" => "Từ", +"To" => "Tới", +"Advanced options" => "Tùy chọn nâng cao", +"Location" => "Nơi", +"Location of the Event" => "Nơi tổ chức sự kiện", +"Description" => "Mô tả", +"Description of the Event" => "Mô tả sự kiện", +"Repeat" => "Lặp lại", +"Advanced" => "Nâng cao", +"Select weekdays" => "Chọn ngày trong tuần", +"Select days" => "Chọn ngày", +"and the events day of year." => "và sự kiện của ngày trong năm", +"and the events day of month." => "và sự kiện của một ngày trong năm", +"Select months" => "Chọn tháng", +"Select weeks" => "Chọn tuần", +"and the events week of year." => "và sự kiện của tuần trong năm.", +"create a new calendar" => "Tạo lịch mới", +"Name of new calendar" => "Tên lịch mới", +"Close Dialog" => "Đóng hộp thoại", +"Create a new event" => "Tạo một sự kiện mới", +"View an event" => "Xem một sự kiện", +"No categories selected" => "Không danh sách nào được chọn", +"of" => "của", +"at" => "tại", +"Timezone" => "Múi giờ", +"Check always for changes of the timezone" => "Luôn kiểm tra múi giờ", +"24h" => "24h", +"12h" => "12h" +); diff --git a/apps/calendar/lib/calendar.php b/apps/calendar/lib/calendar.php index 128b55c48e9..7778242464c 100644 --- a/apps/calendar/lib/calendar.php +++ b/apps/calendar/lib/calendar.php @@ -267,8 +267,42 @@ class OC_Calendar_Calendar{ 'url' => OCP\Util::linkTo('calendar', 'ajax/events.php').'?calendar_id='.$calendar['id'], 'backgroundColor' => $calendar['calendarcolor'], 'borderColor' => '#888', - 'textColor' => 'black', + 'textColor' => self::generateTextColor($calendar['calendarcolor']), 'cache' => true, ); } + + /* + * @brief checks if a calendar name is available for a user + * @param string $calendarname + * @param string $userid + * @return boolean + */ + public static function isCalendarNameavailable($calendarname, $userid){ + $calendars = self::allCalendars($userid); + foreach($calendars as $calendar){ + if($calendar['displayname'] == $calendarname){ + return false; + } + } + return true; + } + + /* + * @brief generates the text color for the calendar + * @param string $calendarcolor rgb calendar color code in hex format (with or without the leading #) + * (this function doesn't pay attention on the alpha value of rgba color codes) + * @return boolean + */ + public static function generateTextColor($calendarcolor){ + if(substr_count($calendarcolor, '#') == 1){ + $calendarcolor = substr($calendarcolor,1); + } + $red = hexdec(substr($calendarcolor,0,2)); + $green = hexdec(substr($calendarcolor,2,2)); + $blue = hexdec(substr($calendarcolor,2,2)); + //recommendation by W3C + $computation = ((($red * 299) + ($green * 587) + ($blue * 114)) / 1000); + return ($computation > 130)?'#000000':'#FAFAFA'; + } } diff --git a/apps/calendar/lib/connector_sabre.php b/apps/calendar/lib/connector_sabre.php index 263fb7ffde5..8eea06da7e2 100644 --- a/apps/calendar/lib/connector_sabre.php +++ b/apps/calendar/lib/connector_sabre.php @@ -105,6 +105,9 @@ class OC_Connector_Sabre_CalDAV extends Sabre_CalDAV_Backend_Abstract { if(!isset($newValues['timezone'])) $newValues['timezone'] = null; if(!isset($newValues['calendarorder'])) $newValues['calendarorder'] = 0; if(!isset($newValues['calendarcolor'])) $newValues['calendarcolor'] = null; + if(!is_null($newValues['calendarcolor']) && strlen($newValues['calendarcolor']) == 9){ + $newValues['calendarcolor'] = substr($newValues['calendarcolor'], 0, 7); + } return OC_Calendar_Calendar::addCalendarFromDAVData($principalUri,$calendarUri,$newValues['displayname'],$newValues['components'],$newValues['timezone'],$newValues['calendarorder'],$newValues['calendarcolor']); } @@ -192,7 +195,10 @@ class OC_Connector_Sabre_CalDAV extends Sabre_CalDAV_Backend_Abstract { if(!isset($newValues['timezone'])) $newValues['timezone'] = null; if(!isset($newValues['calendarorder'])) $newValues['calendarorder'] = null; if(!isset($newValues['calendarcolor'])) $newValues['calendarcolor'] = null; - + if(!is_null($newValues['calendarcolor']) && strlen($newValues['calendarcolor']) == 9){ + $newValues['calendarcolor'] = substr($newValues['calendarcolor'], 0, 7); + } + OC_Calendar_Calendar::editCalendar($calendarId,$newValues['displayname'],null,$newValues['timezone'],$newValues['calendarorder'],$newValues['calendarcolor']); return true; diff --git a/apps/calendar/lib/import.php b/apps/calendar/lib/import.php new file mode 100644 index 00000000000..d36891cb2b9 --- /dev/null +++ b/apps/calendar/lib/import.php @@ -0,0 +1,334 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +/* + * This class does import and converts all times to the users current timezone + */ +class OC_Calendar_Import{ + /* + * @brief counts the absolute number of parsed elements + */ + private $abscount; + + /* + * @brief var saves if the percentage should be saved with OC_Cache + */ + private $cacheprogress; + + /* + * @brief Sabre_VObject_Component_VCalendar object - for documentation see http://code.google.com/p/sabredav/wiki/Sabre_VObject_Component_VCalendar + */ + private $calobject; + + /* + * @brief var counts the number of imported elements + */ + private $count; + + /* + * @brief var to check if errors happend while initialization + */ + private $error; + + /* + * @brief var saves the ical string that was submitted with the __construct function + */ + private $ical; + + /* + * @brief calendar id for import + */ + private $id; + + /* + * @brief var saves the percentage of the import's progress + */ + private $progress; + + /* + * @brief var saves the key for the percentage of the import's progress + */ + private $progresskey; + + /* + * @brief var saves the timezone the events shell converted to + */ + private $tz; + + /* + * @brief var saves the userid + */ + private $userid; + + /* + * public methods + */ + + /* + * @brief does general initialization for import object + * @param string $calendar content of ical file + * @param string $tz timezone of the user + * @return boolean + */ + public function __construct($ical){ + $this->error = null; + $this->ical = $ical; + $this->abscount = 0; + $this->count = 0; + try{ + $this->calobject = OC_VObject::parse($this->ical); + }catch(Exception $e){ + //MISSING: write some log + $this->error = true; + return false; + } + return true; + } + + /* + * @brief imports a calendar + * @return boolean + */ + public function import(){ + if(!$this->isValid()){ + return false; + } + $numofcomponents = count($this->calobject->getComponents()); + foreach($this->calobject->getComponents() as $object){ + if(!($object instanceof Sabre_VObject_Component_VEvent) && !($object instanceof Sabre_VObject_Component_VJournal) && !($object instanceof Sabre_VObject_Component_VTodo)){ + continue; + } + $dtend = OC_Calendar_Object::getDTEndFromVEvent($object); + $object->DTSTART->getDateTime()->setTimezone(new DateTimeZone($this->tz)); + $object->DTEND->setDateTime($dtend->getDateTime(), $object->DTSTART->getDateType()); + $object->DTEND->getDateTime()->setTimezone(new DateTimeZone($this->tz)); + $vcalendar = $this->createVCalendar($object->serialize()); + $insertid = OC_Calendar_Object::add($this->id, $vcalendar); + $this->abscount++; + if($this->isDuplicate($insertid)){ + OC_Calendar_Object::delete($insertid); + }else{ + $this->count++; + } + $this->updateProgress(intval(($this->abscount / $numofcomponents)*100)); + } + OC_Cache::remove($this->progresskey); + return true; + } + + /* + * @brief sets the timezone + * @return boolean + */ + public function setTimeZone($tz){ + $this->tz = $tz; + return true; + } + + /* + * @brief sets the progresskey + * @return boolean + */ + public function setProgresskey($progresskey){ + $this->progresskey = $progresskey; + return true; + } + + /* + * @brief checks if something went wrong while initialization + * @return boolean + */ + public function isValid(){ + if(is_null($this->error)){ + return true; + } + return false; + } + + /* + * @brief returns the percentage of progress + * @return integer + */ + public function getProgress(){ + return $this->progress; + } + + /* + * @brief enables the cache for the percentage of progress + * @return boolean + */ + public function enableProgressCache(){ + $this->cacheprogress = true; + return true; + } + + /* + * @brief disables the cache for the percentage of progress + * @return boolean + */ + public function disableProgressCache(){ + $this->cacheprogress = false; + return false; + } + + /* + * @brief generates a new calendar name + * @return string + */ + public function createCalendarName(){ + $calendars = OC_Calendar_Calendar::allCalendars($this->userid); + $calendarname = $guessedcalendarname = !is_null($this->guessCalendarName())?($this->guessCalendarName()):(OC_Calendar_App::$l10n->t('New Calendar')); + $i = 1; + while(!OC_Calendar_Calendar::isCalendarNameavailable($calendarname, $this->userid)){ + $calendarname = $guessedcalendarname . ' (' . $i . ')'; + $i++; + } + return $calendarname; + } + + /* + * @brief generates a new calendar color + * @return string + */ + public function createCalendarColor(){ + if(is_null($this->guessCalendarColor())){ + return '#9fc6e7'; + } + return $this->guessCalendarColor(); + } + + /* + * @brief sets the id for the calendar + * @param integer $id of the calendar + * @return boolean + */ + public function setCalendarID($id){ + $this->id = $id; + return true; + } + + /* + * @brief sets the userid to import the calendar + * @param string $id of the user + * @return boolean + */ + public function setUserID($userid){ + $this->userid = $userid; + return true; + } + + /* + * @brief returns the private + * @param string $id of the user + * @return boolean + */ + public function getCount(){ + return $this->count; + } + + /* + * private methods + */ + + /* + * @brief generates an unique ID + * @return string + */ + //private function createUID(){ + // return substr(md5(rand().time()),0,10); + //} + + /* + * @brief checks is the UID is already in use for another event + * @param string $uid uid to check + * @return boolean + */ + //private function isUIDAvailable($uid){ + // + //} + + /* + * @brief generates a proper VCalendar string + * @param string $vobject + * @return string + */ + private function createVCalendar($vobject){ + if(is_object($vobject)){ + $vobject = @$vobject->serialize(); + } + $vcalendar = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\n"; + $vcalendar .= $vobject; + $vcalendar .= "END:VCALENDAR"; + return $vcalendar; + } + + /* + * @brief checks if an event already exists in the user's calendars + * @param integer $insertid id of the new object + * @return boolean + */ + private function isDuplicate($insertid){ + $newobject = OC_Calendar_Object::find($insertid); + $stmt = OCP\DB::prepare('SELECT COUNT(*) as count FROM *PREFIX*calendar_objects WHERE objecttype=? AND startdate=? AND enddate=? AND repeating=? AND summary=? AND calendardata=?'); + $result = $stmt->execute(array($newobject['objecttype'],$newobject['startdate'],$newobject['enddate'],$newobject['repeating'],$newobject['summary'],$newobject['calendardata'])); + $result = $result->fetchRow(); + if($result['count'] >= 2){ + return true; + } + return false; + } + + /* + * @brief updates the progress var + * @param integer $percentage + * @return boolean + */ + private function updateProgress($percentage){ + $this->progress = $percentage; + if($this->cacheprogress){ + OC_Cache::set($this->progresskey, $this->progress, 300); + } + return true; + } + + /* + * public methods for (pre)rendering of X-... Attributes + */ + + /* + * @brief guesses the calendar color + * @return mixed - string or boolean + */ + public function guessCalendarColor(){ + if(!is_null($this->calobject->__get('X-APPLE-CALENDAR-COLOR'))){ + return $this->calobject->__get('X-APPLE-CALENDAR-COLOR'); + } + return null; + } + + /* + * @brief guesses the calendar description + * @return mixed - string or boolean + */ + public function guessCalendarDescription(){ + if(!is_null($this->calobject->__get('X-WR-CALDESC'))){ + return $this->calobject->__get('X-WR-CALDESC'); + } + return null; + } + + /* + * @brief guesses the calendar name + * @return mixed - string or boolean + */ + public function guessCalendarName(){ + if(!is_null($this->calobject->__get('X-WR-CALNAME'))){ + return $this->calobject->__get('X-WR-CALNAME'); + } + return null; + } +} diff --git a/apps/calendar/templates/part.import.php b/apps/calendar/templates/part.import.php index 70ff9612157..2ce3cc34239 100644 --- a/apps/calendar/templates/part.import.php +++ b/apps/calendar/templates/part.import.php @@ -1,30 +1,58 @@ -<div id="calendar_import_dialog" title="<?php echo $l->t("Import a calendar file"); ?>"> -<div id="form_container"> -<input type="hidden" id="filename" value="<?php echo $_['filename'];?>"> -<input type="hidden" id="path" value="<?php echo $_['path'];?>"> -<input type="hidden" id="progresskey" value="<?php echo rand() ?>"> -<p style="text-align:center;"><b><?php echo $l->t('Please choose the calendar'); ?></b></p> -<select style="width:100%;" id="calendar" name="calendar"> <?php +//Prerendering for iCalendar file +$file = OC_Filesystem::file_get_contents($_['path'] . '/' . $_['filename']); +if(!$file){ + OCP\JSON::error(array('error'=>'404')); +} +$import = new OC_Calendar_Import($file); +$import->setUserID(OCP\User::getUser()); +$newcalendarname = strip_tags($import->createCalendarName()); +$guessedcalendarname = strip_tags($import->guessCalendarName()); +$calendarcolor = strip_tags($import->createCalendarColor()); +//loading calendars for select box $calendar_options = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); $calendar_options[] = array('id'=>'newcal', 'displayname'=>$l->t('create a new calendar')); -for($i = 0;$i<count($calendar_options);$i++){ - $calendar_options[$i]['displayname'] = $calendar_options[$i]['displayname']; -} -echo OCP\html_select_options($calendar_options, $calendar_options[0]['id'], array('value'=>'id', 'label'=>'displayname')); +$defaultcolors = OC_Calendar_Calendar::getCalendarColorOptions(); ?> -</select> -<div id="newcalform" style="display: none;"> - <input type="text" style="width: 97%;" placeholder="<?php echo $l->t('Name of new calendar'); ?>" id="newcalendar" name="newcalendar"> -</div> -<input type="button" value="<?php echo $l->t("Import");?>!" id="startimport"> -</div> -<div id="progressbar_container" style="display: none"> -<p style="text-align:center;"><b><?php echo $l->t('Importing calendar'); ?></b></p> -<div id="progressbar"></div> -<div id="import_done" style="display: none;"> -<p style="text-align:center;"><b><?php echo $l->t('Calendar imported successfully'); ?></b></p> -<input type="button" value="<?php echo $l->t('Close Dialog'); ?>" id="import_done_button"> +<div id="calendar_import_dialog" title="<?php echo $l->t("Import a calendar file");?>"> +<div id="calendar_import_form"> + <form> + <input type="hidden" id="calendar_import_filename" value="<?php echo $_['filename'];?>"> + <input type="hidden" id="calendar_import_path" value="<?php echo $_['path'];?>"> + <input type="hidden" id="calendar_import_progresskey" value="<?php echo rand() ?>"> + <input type="hidden" id="calendar_import_availablename" value="<?php echo $newcalendarname ?>"> + <div id="calendar_import_form_message"><?php echo $l->t('Please choose a calendar'); ?></div> + <select style="width:100%;" id="calendar_import_calendar" name="calendar_import_calendar"> + <?php + for($i = 0;$i<count($calendar_options);$i++){ + $calendar_options[$i]['displayname'] = $calendar_options[$i]['displayname']; + } + echo OCP\html_select_options($calendar_options, $calendar_options[0]['id'], array('value'=>'id', 'label'=>'displayname')); + ?> + </select> + <br><br> + <div id="calendar_import_newcalform"> + <input id="calendar_import_newcalendar_color" class="color-picker" type="hidden" size="6" value="<?php echo substr($calendarcolor,1); ?>"> + <input id="calendar_import_newcalendar" class="" type="text" placeholder="<?php echo $l->t('Name of new calendar'); ?>" value="<?php echo $guessedcalendarname ?>"><br> + <div id="calendar_import_defaultcolors"> + <?php + foreach($defaultcolors as $color){ + echo '<span class="calendar-colorpicker-color" rel="' . $color . '" style="background-color: ' . $color . ';"></span>'; + } + ?> + </div> + <!--<input id="calendar_import_generatename" type="button" class="button" value="<?php echo $l->t('Take an available name!'); ?>"><br>--> + <div id="calendar_import_mergewarning" class="hint"><?php echo $l->t('A Calendar with this name already exists. If you continue anyhow, these calendars will be merged.'); ?></div> + </div> + <input id="calendar_import_submit" type="button" class="button" value="» <?php echo $l->t('Import'); ?> »" id="startimport"> + <form> </div> +<div id="calendar_import_process"> + <div id="calendar_import_process_message"></div> + <div id="calendar_import_progressbar"></div> + <br> + <div id="calendar_import_status" class="hint"></div> + <br> + <input id="calendar_import_done" type="button" value="<?php echo $l->t('Close Dialog'); ?>"> </div> </div>
\ No newline at end of file diff --git a/apps/contacts/ajax/loadphoto.php b/apps/contacts/ajax/loadphoto.php deleted file mode 100644 index be924b5db4d..00000000000 --- a/apps/contacts/ajax/loadphoto.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * ownCloud - Addressbook - * - * @author Thomas Tanghus - * @copyright 2012 Thomas Tanghus <thomas@tanghus.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -// Check if we are a user -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); - -require_once 'loghandler.php'; - -$id = isset($_GET['id']) ? $_GET['id'] : ''; -$refresh = isset($_GET['refresh']) ? true : false; - -if($id == '') { - bailOut(OC_Contacts_App::$l10n->t('Missing contact id.')); -} - -$checksum = ''; -$vcard = OC_Contacts_App::getContactVCard( $id ); -foreach($vcard->children as $property){ - if($property->name == 'PHOTO') { - $checksum = md5($property->serialize()); - break; - } -} - -OCP\JSON::success(array('data' => array('checksum'=>$checksum))); - diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 927e7309807..ddae27da211 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -125,3 +125,12 @@ input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; } .typelist[type="button"] { float: left; max-width: 10em; border: 0; background-color: #fff; color: #bbb} /* for multiselect */ .typelist[type="button"]:hover { color: #777; } /* for multiselect */ .addresslist { clear: both; font-weight: bold; } +#ninjahelp { position: absolute; bottom: 0; left: 0; right: 0; padding: 1em; margin: 1em; border: thin solid #eee; border-radius: 5px; background-color: #DBDBDB; opacity: 0.9; } +#ninjahelp .close { position: absolute; top: 5px; right: 5px; height: 20px; width: 20px; } +#ninjahelp h2, .help-section h3 { width: 100%; font-weight: bold; text-align: center; } +#ninjahelp h2 { font-size: 1.4em; } +.help-section { width: 45%; min-width: 35em; float: left; } +.help-section h3 { font-size: 1.2em; } +.help-section dl { width: 100%; float: left; clear: right; margin: 0; padding: 0; cursor: normal; } +.help-section dt { display: table-cell; clear: left; float: left; width: 35%; margin: 0; padding: 0.2em; text-align: right; text-overflow: ellipsis; vertical-align: text-bottom; font-weight: bold: } +.help-section dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0.2em; white-space: nowrap; vertical-align: text-bottom; } diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index 337f51839dc..4c6c8bf3d93 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -21,27 +21,31 @@ Contacts={ * data: An object that will be passed as argument to the timeouthandler and clickhandler functions. */ notify:function(params) { - var notifier = $('#notification'); - notifier.text(params.message); - notifier.fadeIn(); + self = this; + if(!self.notifier) { + self.notifier = $('#notification'); + } + self.notifier.text(params.message); + self.notifier.fadeIn(); + self.notifier.on('click', function() { $(this).fadeOut();}); var timer = setTimeout(function() { - notifier.fadeOut(); + self.notifier.fadeOut(); if(params.timeouthandler && $.isFunction(params.timeouthandler)) { - params.timeouthandler(notifier.data(dataid)); - notifier.off('click'); - notifier.data(dataid, null); + params.timeouthandler(self.notifier.data(dataid)); + self.notifier.off('click'); + self.notifier.removeData(dataid); } }, params.timeout && $.isNumeric(params.timeout) ? parseInt(params.timeout)*1000 : 10000); var dataid = timer.toString(); if(params.data) { - notifier.data(dataid, params.data); + self.notifier.data(dataid, params.data); } if(params.clickhandler && $.isFunction(params.clickhandler)) { - notifier.on('click', function() { + self.notifier.on('click', function() { clearTimeout(timer); - notifier.off('click'); - params.clickhandler(notifier.data(dataid)); - notifier.data(dataid, null); + self.notifier.off('click'); + params.clickhandler(self.notifier.data(dataid)); + self.notifier.removeData(dataid); }); } }, @@ -218,11 +222,7 @@ Contacts={ var item = $('.contacts li[data-id="'+Contacts.UI.Card.id+'"]').detach(); $(item).find('a').html(name); Contacts.UI.Card.fn = name; - Contacts.UI.Contacts.insertContact({ - contactlist:$('#contacts ul[data-id="'+Contacts.UI.Card.bookid+'"]'), - contacts:$('#contacts ul[data-id="'+Contacts.UI.Card.bookid+'"] li'), - contact:item, - }); + Contacts.UI.Contacts.insertContact({contact:item}); Contacts.UI.Contacts.scrollTo(Contacts.UI.Card.id); }); @@ -295,16 +295,6 @@ Contacts={ $('#contacts_propertymenu_dropdown a').keydown(propertyMenuItem); }, Card:{ - id:'', - fn:'', - fullname:'', - shortname:'', - famname:'', - givname:'', - addname:'', - honpre:'', - honsuf:'', - data:undefined, update:function(params) { // params {cid:int, aid:int} if(!params) { params = {}; } $('#contacts li,#contacts h3').removeClass('active'); @@ -321,10 +311,11 @@ Contacts={ newid = parseInt($('#contacts').find('li[data-bookid="'+bookid+'"]').first().data('id')); } else if(parseInt(params.cid) && !parseInt(params.aid)) { newid = parseInt(params.cid); - var listitem = $('#contacts li[data-id="'+newid+'"]'); + var listitem = Contacts.UI.Contacts.getContact(newid); //$('#contacts li[data-id="'+newid+'"]'); console.log('Is contact in list? ' + listitem.length); if(listitem.length) { - bookid = parseInt($('#contacts li[data-id="'+newid+'"]').data('bookid')); + //bookid = parseInt($('#contacts li[data-id="'+newid+'"]').data('bookid')); + bookid = parseInt(Contacts.UI.Contacts.getContact(newid).data('bookid')); } else { // contact isn't in list yet. bookid = 'unknown'; } @@ -412,19 +403,7 @@ Contacts={ $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){ if(jsondata.status == 'success'){ Contacts.UI.Card.loadContact(jsondata.data, aid); - $('#contacts .active').removeClass('active'); - var item = $('<li data-id="'+jsondata.data.id+'" class="active"><a href="index.php?id='+jsondata.data.id+'" style="background: url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+jsondata.data.id+') no-repeat scroll 0% 0% transparent;">'+Contacts.UI.Card.fn+'</a></li>'); - var added = false; - $('#contacts ul[data-id="'+aid+'"] li').each(function(){ - if ($(this).text().toLowerCase() > Contacts.UI.Card.fn.toLowerCase()) { - $(this).before(item).fadeIn('fast'); - added = true; - return false; - } - }); - if(!added) { - $('#contacts ul[data-id="'+aid+'"]').append(item); - } + var item = Contacts.UI.Contacts.insertContact({data:jsondata.data}); if(isnew) { // add some default properties Contacts.UI.Card.addProperty('EMAIL'); Contacts.UI.Card.addProperty('TEL'); @@ -461,9 +440,14 @@ Contacts={ } }, delayedDelete:function() { + /* TODO: + $(window).unload(function() { + deleteFilesInQueue(); + }); + */ $('#contacts_deletecard').tipsy('hide'); var newid = '', bookid; - var curlistitem = $('#contacts li[data-id="'+Contacts.UI.Card.id+'"]'); + var curlistitem = Contacts.UI.Contacts.getContact(this.id); curlistitem.removeClass('active'); var newlistitem = curlistitem.prev('li'); if(!newlistitem) { @@ -474,19 +458,21 @@ Contacts={ newid = newlistitem.data('id'); bookid = newlistitem.data('bookid'); } - $('#rightcontent').data('id',newid); - this.id = this.fn = this.fullname = this.shortname = this.famname = this.givname = this.addname = this.honpre = this.honsuf = ''; - this.data = undefined; + $('#rightcontent').data('id', newid); + + with(this) { + delete id; delete fn; delete fullname; delete shortname; delete famname; + delete givname; delete addname; delete honpre; delete honsuf; delete data; + } - if($('.contacts li').length > 0) { // Load first in list. + if($('.contacts li').length > 0) { Contacts.UI.Card.update({cid:newid, aid:bookid}); } else { // load intro page $.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){ if(jsondata.status == 'success'){ id = ''; - $('#rightcontent').data('id',''); - $('#rightcontent').html(jsondata.data.page); + $('#rightcontent').html(jsondata.data.page).removeData('id'); } else{ OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); @@ -526,21 +512,22 @@ Contacts={ this.loadAddresses(); this.loadSingleProperties(); Contacts.UI.loadListHandlers(); + var note = $('#note'); if(this.data.NOTE) { - $('#note').data('checksum', this.data.NOTE[0]['checksum']); - var note = $('#note').find('textarea'); + note.data('checksum', this.data.NOTE[0]['checksum']); + var textarea = note.find('textarea'); var txt = this.data.NOTE[0]['value']; var nheight = txt.split('\n').length > 4 ? txt.split('\n').length+2 : 5; - note.css('min-height', nheight+'em'); - note.attr('rows', nheight); - note.val(txt); - $('#note').show(); - note.expandingTextarea(); + textarea.css('min-height', nheight+'em'); + textarea.attr('rows', nheight); + textarea.val(txt); + note.show(); + textarea.expandingTextarea(); $('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().hide(); } else { - $('#note').data('checksum', ''); - $('#note').find('textarea').val(''); - $('#note').hide(); + note.removeData('checksum'); + note.find('textarea').val(''); + note.hide(); $('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().show(); } }, @@ -566,10 +553,11 @@ Contacts={ var val = $.datepicker.parseDate('yy-mm-dd', value.substring(0, 10)); value = $.datepicker.formatDate('dd-mm-yy', val); } - $('#contact_identity').find('#'+propname.toLowerCase()).val(value); - $('#contact_identity').find('#'+propname.toLowerCase()+'_value').data('checksum', checksum); - $('#contact_identity').find('#'+propname.toLowerCase()+'_label').show(); - $('#contact_identity').find('#'+propname.toLowerCase()+'_value').show(); + var identcontainer = $('#contact_identity'); + identcontainer.find('#'+propname.toLowerCase()).val(value); + identcontainer.find('#'+propname.toLowerCase()+'_value').data('checksum', checksum); + identcontainer.find('#'+propname.toLowerCase()+'_label').show(); + identcontainer.find('#'+propname.toLowerCase()+'_value').show(); } else { $('#contacts_propertymenu_dropdown a[data-type="'+propname+'"]').parent().show(); } @@ -584,8 +572,12 @@ Contacts={ $(this).find('input').val(''); } }); - this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = ''; - var narray = undefined; + + with(this) { + delete fn; delete fullname; delete givname; delete famname; + delete addname; delete honpre; delete honsuf; + } + if(this.data.FN) { this.fn = this.data.FN[0]['value']; } @@ -769,17 +761,9 @@ Contacts={ }, addProperty:function(type){ switch (type) { - case 'PHOTO': - this.loadPhoto(true); - $('#file_upload_form').show(); - $('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide(); - $('#file_upload_start').trigger('click'); - break; case 'NOTE': - $('#note').show(); $('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide(); - $('#note').find('textarea').expandingTextarea(); - $('#note').find('textarea').focus(); + $('#note').find('textarea').expandingTextarea().show().focus(); break; case 'EMAIL': if($('#emaillist>li').length == 1) { @@ -834,8 +818,7 @@ Contacts={ } } else { $('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide(); - $('dl dd[data-element="'+proptype+'"]').data('checksum', ''); - $('dl dd[data-element="'+proptype+'"]').find('input').val(''); + $('dl dd[data-element="'+proptype+'"]').data('checksum', '').find('input').val(''); } $('#contacts_propertymenu_dropdown a[data-type="'+proptype+'"]').parent().show(); Contacts.UI.loading(obj, false); @@ -862,14 +845,14 @@ Contacts={ } } }, - editName:function(){ + editName:function() { var params = {id: this.id}; /* Initialize the name edit dialog */ - if($('#edit_name_dialog').dialog('isOpen') == true){ + if($('#edit_name_dialog').dialog('isOpen') == true) { $('#edit_name_dialog').dialog('moveToTop'); - }else{ - $.getJSON(OC.filePath('contacts', 'ajax', 'editname.php'),{id: this.id},function(jsondata){ - if(jsondata.status == 'success'){ + } else { + $.getJSON(OC.filePath('contacts', 'ajax', 'editname.php'),{id: this.id},function(jsondata) { + if(jsondata.status == 'success') { $('body').append('<div id="name_dialog"></div>'); $('#name_dialog').html(jsondata.data.page).find('#edit_name_dialog' ).dialog({ modal: true, @@ -941,10 +924,11 @@ Contacts={ loadAddresses:function(){ $('#addresses').hide(); $('#addressdisplay dl.propertycontainer').remove(); + var addresscontainer = $('#addressdisplay'); for(var adr in this.data.ADR) { - $('#addressdisplay dl').first().clone().insertAfter($('#addressdisplay dl').last()).show(); - $('#addressdisplay dl').last().removeClass('template').addClass('propertycontainer'); - $('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']); + addresscontainer.find('dl').first().clone().insertAfter($('#addressdisplay dl').last()).show(); + addresscontainer.find('dl').last().removeClass('template').addClass('propertycontainer'); + addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']); var adrarray = this.data.ADR[adr]['value']; var adrtxt = ''; if(adrarray[0] && adrarray[0].length > 0) { @@ -956,7 +940,7 @@ Contacts={ if(adrarray[2] && adrarray[2].length > 0) { adrtxt = adrtxt + '<li>' + adrarray[2].strip_tags() + '</li>'; } - if((adrarray[3] && adrarray[5]) && adrarray[3].length > 0 || adrarray[5].length > 0) { + if((3 in adrarray && 5 in adrarray) && adrarray[3].length > 0 || adrarray[5].length > 0) { adrtxt = adrtxt + '<li>' + adrarray[5].strip_tags() + ' ' + adrarray[3].strip_tags() + '</li>'; } if(adrarray[4] && adrarray[4].length > 0) { @@ -965,7 +949,7 @@ Contacts={ if(adrarray[6] && adrarray[6].length > 0) { adrtxt = adrtxt + '<li>' + adrarray[6].strip_tags() + '</li>'; } - $('#addressdisplay dl').last().find('.addresslist').html(adrtxt); + addresscontainer.find('dl').last().find('.addresslist').html(adrtxt); var types = new Array(); var ttypes = new Array(); for(var param in this.data.ADR[adr]['parameters']) { @@ -974,12 +958,12 @@ Contacts={ ttypes.push(this.data.ADR[adr]['parameters'][param]); } } - $('#addressdisplay dl').last().find('.adr_type_label').text(types.join('/')); - $('#addressdisplay dl').last().find('.adr_type').val(ttypes.join(',')); - $('#addressdisplay dl').last().find('.adr').val(adrarray.join(';')); - $('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']); + addresscontainer.find('dl').last().find('.adr_type_label').text(types.join('/')); + addresscontainer.find('dl').last().find('.adr_type').val(ttypes.join(',')); + addresscontainer.find('dl').last().find('.adr').val(adrarray.join(';')); + addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']); } - if($('#addressdisplay dl').length > 1) { + if(addresscontainer.find('dl').length > 1) { $('#addresses').show(); $('#contact_communication').show(); } @@ -1024,9 +1008,6 @@ Contacts={ close : function(event, ui) { $(this).dialog('destroy').remove(); $('#address_dialog').remove(); - if(isnew) { - container.remove(); - } }, open : function(event, ui) { $( "#adr_city" ).autocomplete({ @@ -1065,7 +1046,7 @@ Contacts={ $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" ); } }); - $( "#adr_country" ).autocomplete({ + $('#adr_country').autocomplete({ source: function( request, response ) { $.ajax({ url: "http://ws.geonames.org/searchJSON", @@ -1116,15 +1097,23 @@ Contacts={ saveAddress:function(dlg, obj, isnew){ if(isnew) { container = $('#addressdisplay dl').last(); - obj = $('#addressdisplay dl:last-child').find('input').first(); + obj = container.find('input').first(); } else { checksum = Contacts.UI.checksumFor(obj); container = Contacts.UI.propertyContainerFor(obj); } - var adr = new Array($(dlg).find('#adr_pobox').val().strip_tags(),$(dlg).find('#adr_extended').val().strip_tags(),$(dlg).find('#adr_street').val().strip_tags(),$(dlg).find('#adr_city').val().strip_tags(),$(dlg).find('#adr_region').val().strip_tags(),$(dlg).find('#adr_zipcode').val().strip_tags(),$(dlg).find('#adr_country').val().strip_tags()); - $(container).find('.adr').val(adr.join(';')); - $(container).find('.adr_type').val($(dlg).find('#adr_type').val()); - $(container).find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase()))); + var adr = new Array( + $(dlg).find('#adr_pobox').val().strip_tags(), + $(dlg).find('#adr_extended').val().strip_tags(), + $(dlg).find('#adr_street').val().strip_tags(), + $(dlg).find('#adr_city').val().strip_tags(), + $(dlg).find('#adr_region').val().strip_tags(), + $(dlg).find('#adr_zipcode').val().strip_tags(), + $(dlg).find('#adr_country').val().strip_tags() + ); + container.find('.adr').val(adr.join(';')); + container.find('.adr_type').val($(dlg).find('#adr_type').val()); + container.find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase()))); Contacts.UI.Card.saveProperty($(container).find('input').first()); var adrtxt = ''; if(adr[0].length > 0) { @@ -1145,7 +1134,7 @@ Contacts={ if(adr[6].length > 0) { adrtxt = adrtxt + '<li>' + adr[6] + '</li>'; } - $(container).find('.addresslist').html(adrtxt); + container.find('.addresslist').html(adrtxt); }, uploadPhoto:function(filelist) { if(!filelist) { @@ -1172,24 +1161,25 @@ Contacts={ form.submit(); } }, - loadPhotoHandlers:function(){ - $('#phototools li a').tipsy('hide'); - $('#phototools li a').tipsy(); + loadPhotoHandlers:function() { + var phototools = $('#phototools'); + phototools.find('li a').tipsy('hide'); + phototools.find('li a').tipsy(); if(this.data.PHOTO) { - $('#phototools .delete').click(function() { + phototools.find('.delete').click(function() { $(this).tipsy('hide'); Contacts.UI.Card.deleteProperty($('#contacts_details_photo'), 'single'); $(this).hide(); }); - $('#phototools .edit').click(function() { + phototools.find('.edit').click(function() { $(this).tipsy('hide'); Contacts.UI.Card.editCurrentPhoto(); }); - $('#phototools .delete').show(); - $('#phototools .edit').show(); + phototools.find('.delete').show(); + phototools.find('.edit').show(); } else { - $('#phototools .delete').hide(); - $('#phototools .edit').hide(); + phototools.find('.delete').hide(); + phototools.find('.edit').hide(); } }, cloudPhotoSelected:function(path){ @@ -1210,28 +1200,18 @@ Contacts={ $('#phototools li a').tipsy('hide'); var wrapper = $('#contacts_details_photo_wrapper'); wrapper.addClass('loading').addClass('wait'); - - var img = new Image(); - $(img).load(function () { + delete this.photo; + this.photo = new Image(); + $(this.photo).load(function () { $('img.contacts_details_photo').remove() - $(this).addClass('contacts_details_photo').hide(); + $(this).addClass('contacts_details_photo'); wrapper.removeClass('loading').removeClass('wait'); $(this).insertAfter($('#phototools')).fadeIn(); }).error(function () { // notify the user that the image could not be loaded Contacts.UI.notify({message:t('contacts','Error loading profile picture.')}); }).attr('src', OC.linkTo('contacts', 'photo.php')+'?id='+self.id+refreshstr); - - $.getJSON(OC.filePath('contacts', 'ajax', 'loadphoto.php'),{'id':this.id, 'refresh': refresh},function(jsondata){ - if(jsondata.status == 'success'){ - $('#contacts_details_photo_wrapper').data('checksum', jsondata.data.checksum); - Contacts.UI.Card.loadPhotoHandlers(); - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - $('#file_upload_form').show(); + this.loadPhotoHandlers() }, editCurrentPhoto:function(){ $.getJSON(OC.filePath('contacts', 'ajax', 'currentphoto.php'),{'id':this.id},function(jsondata){ @@ -1285,10 +1265,11 @@ Contacts={ }, addMail:function() { //alert('addMail'); - $('#emaillist li.template:first-child').clone(true).appendTo($('#emaillist')).show().find('a .tip').tipsy(); - $('#emaillist li.template:last-child').find('select').addClass('contacts_property'); - $('#emaillist li.template:last-child').removeClass('template').addClass('propertycontainer'); - $('#emaillist li:last-child').find('input[type="email"]').focus(); + var emaillist = $('#emaillist'); + emaillist.find('li.template:first-child').clone(true).appendTo(emaillist).show().find('a .tip').tipsy(); + emaillist.find('li.template:last-child').find('select').addClass('contacts_property'); + emaillist.find('li.template:last-child').removeClass('template').addClass('propertycontainer'); + emaillist.find('li:last-child').find('input[type="email"]').focus(); return false; }, loadMails:function() { @@ -1324,35 +1305,37 @@ Contacts={ return false; }, addPhone:function() { - $('#phonelist li.template:first-child').clone(true).appendTo($('#phonelist')); //.show(); - $('#phonelist li.template:last-child').find('select').addClass('contacts_property'); - $('#phonelist li.template:last-child').removeClass('template').addClass('propertycontainer'); - $('#phonelist li:last-child').find('input[type="text"]').focus(); - $('#phonelist li:last-child').find('select').multiselect({ + var phonelist = $('#phonelist'); + phonelist.find('li.template:first-child').clone(true).appendTo(phonelist); //.show(); + phonelist.find('li.template:last-child').find('select').addClass('contacts_property'); + phonelist.find('li.template:last-child').removeClass('template').addClass('propertycontainer'); + phonelist.find('li:last-child').find('input[type="text"]').focus(); + phonelist.find('li:last-child').find('select').multiselect({ noneSelectedText: t('contacts', 'Select type'), header: false, selectedList: 4, classes: 'typelist' }); - $('#phonelist li:last-child').show(); + phonelist.find('li:last-child').show(); return false; }, loadPhones:function() { $('#phones').hide(); $('#phonelist li.propertycontainer').remove(); + var phonelist = $('#phonelist'); for(var phone in this.data.TEL) { this.addPhone(); - $('#phonelist li:last-child').find('select').multiselect('destroy'); - $('#phonelist li:last-child').data('checksum', this.data.TEL[phone]['checksum']) - $('#phonelist li:last-child').find('input[type="text"]').val(this.data.TEL[phone]['value']); + phonelist.find('li:last-child').find('select').multiselect('destroy'); + phonelist.find('li:last-child').data('checksum', this.data.TEL[phone]['checksum']) + phonelist.find('li:last-child').find('input[type="text"]').val(this.data.TEL[phone]['value']); for(var param in this.data.TEL[phone]['parameters']) { if(param.toUpperCase() == 'PREF') { - $('#phonelist li:last-child').find('input[type="checkbox"]').attr('checked', 'checked'); + phonelist.find('li:last-child').find('input[type="checkbox"]').attr('checked', 'checked'); } else if(param.toUpperCase() == 'TYPE') { for(ptype in this.data.TEL[phone]['parameters'][param]) { var pt = this.data.TEL[phone]['parameters'][param][ptype]; - $('#phonelist li:last-child').find('select option').each(function(){ + phonelist.find('li:last-child').find('select option').each(function(){ //if ($(this).val().toUpperCase() == pt.toUpperCase()) { if ($.inArray($(this).val().toUpperCase(), pt.toUpperCase().split(',')) > -1) { $(this).attr('selected', 'selected'); @@ -1361,14 +1344,14 @@ Contacts={ } } } - $('#phonelist li:last-child').find('select').multiselect({ - noneSelectedText: t('contacts', 'Select type'), - header: false, - selectedList: 4, - classes: 'typelist' - }); + phonelist.find('li:last-child').find('select').multiselect({ + noneSelectedText: t('contacts', 'Select type'), + header: false, + selectedList: 4, + classes: 'typelist' + }); } - if($('#phonelist li').length > 1) { + if(phonelist.find('li').length > 1) { $('#phones').show(); $('#contact_communication').show(); } @@ -1493,7 +1476,25 @@ Contacts={ } }, Contacts:{ + contacts:{}, batchnum:50, + getContact:function(id) { + if(!this.contacts[id]) { + this.contacts[id] = $('#contacts li[data-id="'+id+'"]'); + if(!this.contacts[id]) { + self = this; + $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){ + if(jsondata.status == 'success'){ + self.contacts[id] = self.insertContact({data:jsondata.data}); + } + else{ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + } + } + return this.contacts[id]; + }, drop:function(event, ui) { var dragitem = ui.draggable, droptarget = $(this); if(dragitem.is('li')) { @@ -1510,21 +1511,12 @@ Contacts={ $.post(OC.filePath('contacts', 'ajax', 'movetoaddressbook.php'), { ids: dragitem.data('id'), aid: droptarget.data('id') }, function(jsondata){ if(jsondata.status == 'success'){ - // Do some inserting/removing/sorting magic - var name = $(dragitem).find('a').html(); - var added = false; - $(droplist).children().each(function(){ - if ($(this).text().toLowerCase() > name.toLowerCase()) { - $(this).before(dragitem.detach()); //.fadeIn('slow'); - added = true; - return false; - } - }); - if(!added) { - $(droplist).append(dragitem.detach()); - } dragitem.attr('data-bookid', droptarget.data('id')) dragitem.data('bookid', droptarget.data('id')); + Contacts.UI.Contacts.insertContact({ + contactlist:droplist, + contact:dragitem.detach() + }); Contacts.UI.Contacts.scrollTo(dragitem.data('id')); } else { OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); @@ -1541,13 +1533,16 @@ Contacts={ * If 'contactlist' or 'contacts' aren't defined they will be search for based in the properties in 'data'. */ insertContact:function(params) { + var id, bookid; if(!params.contactlist) { // FIXME: Check if contact really exists. - var bookid = params.data ? params.data.addressbookid : params.contact.data('bookid'); + bookid = params.data ? params.data.addressbookid : params.contact.data('bookid'); + id = params.data ? params.data.id : params.contact.data('id'); params.contactlist = $('#contacts ul[data-id="'+bookid+'"]'); } if(!params.contacts) { - var bookid = params.data ? params.data.addressbookid : params.contact.data('bookid'); + bookid = params.data ? params.data.addressbookid : params.contact.data('bookid'); + id = params.data ? params.data.id : params.contact.data('id'); params.contacts = $('#contacts ul[data-id="'+bookid+'"] li'); } var contact = params.data @@ -1567,8 +1562,24 @@ Contacts={ if(!added || !params.contacts) { params.contactlist.append(contact); } + //this.contacts[id] = contact; return contact; }, + next:function(reverse) { + // TODO: Check if we're last-child/first-child and jump to next/prev address book. + var curlistitem = $('#contacts li[data-id="'+Contacts.UI.Card.id+'"]'); + var newlistitem = reverse ? curlistitem.prev('li') : curlistitem.next('li'); + if(newlistitem) { + curlistitem.removeClass('active'); + Contacts.UI.Card.update({ + cid:newlistitem.data('id'), + aid:newlistitem.data('bookid') + }); + } + }, + previous:function() { + this.next(true); + }, // Reload the contacts list. update:function(params){ if(!params) { params = {}; } @@ -1672,10 +1683,10 @@ Contacts={ }, scrollTo:function(id){ var item = $('#contacts li[data-id="'+id+'"]'); - console.log('scrollTo, found item '+id+'? ' + item.length); - if(item) { + if(item && $.isNumeric(item.offset().top)) { + console.log('scrollTo ' + parseInt(item.offset().top)); $('#contacts').animate({ - scrollTop: item.offset().top-40}, 'slow','swing'); + scrollTop: parseInt(item.offset()).top-40}, 'slow','swing'); } } } @@ -1686,23 +1697,90 @@ $(document).ready(function(){ OCCategories.changed = Contacts.UI.Card.categoriesChanged; OCCategories.app = 'contacts'; - $('#notification').click(function(){ - $('#notification').fadeOut(); + $('#chooseaddressbook').on('click keydown', Contacts.UI.Addressbooks.overview); + $('#contacts_newcontact').on('click keydown', Contacts.UI.Card.editNew); + + var ninjahelp = $('#ninjahelp'); + + ninjahelp.find('.close').on('click keydown',function() { + ninjahelp.hide(); }); - $('#chooseaddressbook').click(Contacts.UI.Addressbooks.overview); - $('#chooseaddressbook').keydown(Contacts.UI.Addressbooks.overview); + $(document).on('keyup', function(event) { + console.log(event.which + ' ' + event.target.nodeName); + if(event.target.nodeName.toUpperCase() != 'BODY' + || $('#contacts li').length == 0 + || !Contacts.UI.Card.id) { + return; + } + /** + * To add: + * (Shift)n/p: next/prev addressbook + * u (85): hide/show leftcontent + * f (70): add field + */ + switch(event.which) { + case 27: // Esc + ninjahelp.hide(); + break; + case 46: + if(event.shiftKey) { + Contacts.UI.Card.delayedDelete(); + } + break; + case 32: // space + if(event.shiftKey) { + Contacts.UI.Contacts.previous(); + break; + } + case 40: // down + case 75: // k + Contacts.UI.Contacts.next(); + break; + case 65: // a + if(event.shiftKey) { + // add addressbook + Contacts.UI.notImplemented(); + break; + } + Contacts.UI.Card.editNew(); + break; + case 38: // up + case 74: // j + Contacts.UI.Contacts.previous(); + break; + case 78: // n + // next addressbook + Contacts.UI.notImplemented(); + break; + case 13: // Enter + case 79: // o + var aid = $('#contacts h3.active').first().data('id'); + if(aid) { + $('#contacts ul[data-id="'+aid+'"]').slideToggle(300); + } + break; + case 80: // p + // prev addressbook + Contacts.UI.notImplemented(); + break; + case 82: // r + Contacts.UI.Contacts.update({cid:Contacts.UI.Card.id}); + break; + case 191: // ? + ninjahelp.toggle('fast'); + break; + } - $('#contacts_newcontact').click(Contacts.UI.Card.editNew); - $('#contacts_newcontact').keydown(Contacts.UI.Card.editNew); + }); - // Load a contact. + // Load a contact. $('.contacts').keydown(function(event) { if(event.which == 13 || event.which == 32) { $('.contacts').click(); } }); - $(document).on('click', '.contacts', function(event){ + $(document).on('click', '#contacts', function(event){ var $tgt = $(event.target); if ($tgt.is('li') || $tgt.is('a')) { var item = $tgt.is('li')?$($tgt):($tgt).parent(); @@ -1931,14 +2009,15 @@ $(document).ready(function(){ $('#selectaddressbook_dialog').dialog('moveToTop'); } else { $('#dialog_holder').html(jsondata.data.page).ready(function($) { - $('#selectaddressbook_dialog').dialog({ + var select_dlg = $('#selectaddressbook_dialog'); + select_dlg.dialog({ modal: true, height: 'auto', width: 'auto', buttons: { 'Ok':function() { - aid = $('#selectaddressbook_dialog').find('input:checked').val(); + aid = select_dlg.find('input:checked').val(); if(aid == 'new') { - var displayname = $('#selectaddressbook_dialog').find('input.name').val(); - var description = $('#selectaddressbook_dialog').find('input.desc').val(); + var displayname = select_dlg.find('input.name').val(); + var description = select_dlg.find('input.desc').val(); if(!displayname.trim()) { OC.dialogs.alert(t('contacts', 'The address book name cannot be empty.'), t('contacts', 'Error')); return false; diff --git a/apps/contacts/js/loader.js b/apps/contacts/js/loader.js index 577ad103064..5bca0ab7237 100644 --- a/apps/contacts/js/loader.js +++ b/apps/contacts/js/loader.js @@ -78,9 +78,9 @@ Contacts_Import={ }
$(document).ready(function(){
if(typeof FileActions !== 'undefined'){
- FileActions.register('text/vcard','importaddressbook', '', Contacts_Import.importdialog);
+ FileActions.register('text/vcard','importaddressbook', '', Contacts_Import.importdialog);
FileActions.setDefault('text/vcard','importaddressbook');
- FileActions.register('text/x-vcard','importaddressbook', '', Contacts_Import.importdialog);
+ FileActions.register('text/x-vcard','importaddressbook', '', Contacts_Import.importdialog);
FileActions.setDefault('text/x-vcard','importaddressbook');
};
});
\ No newline at end of file diff --git a/apps/contacts/l10n/it.php b/apps/contacts/l10n/it.php index 2a5478e6c4b..820104b7774 100644 --- a/apps/contacts/l10n/it.php +++ b/apps/contacts/l10n/it.php @@ -1,10 +1,13 @@ <?php $TRANSLATIONS = array( "Error (de)activating addressbook." => "Errore nel (dis)attivare la rubrica.", "There was an error adding the contact." => "Si è verificato un errore nell'aggiunta del contatto.", +"element name is not set." => "il nome dell'elemento non è impostato.", +"id is not set." => "ID non impostato.", +"Could not parse contact: " => "Impossibile elaborare il contatto: ", "Cannot add empty property." => "Impossibile aggiungere una proprietà vuota.", "At least one of the address fields has to be filled out." => "Deve essere riempito almeno un indirizzo.", "Trying to add duplicate property: " => "P", -"Error adding contact property." => "Errore durante l'aggiunta della proprietà del contatto.", +"Error adding contact property: " => "Errore durante l'aggiunta della proprietà del contatto: ", "No ID provided" => "Nessun ID fornito", "Error setting checksum." => "Errore di impostazione del codice di controllo.", "No categories selected for deletion." => "Nessuna categoria selezionata per l'eliminazione.", @@ -12,22 +15,23 @@ "No contacts found." => "Nessun contatto trovato.", "Missing ID" => "ID mancante", "Error parsing VCard for ID: \"" => "Errore in fase di elaborazione del file VCard per l'ID: \"", -"Cannot add addressbook with an empty name." => "Impossibile aggiungere una rubrica senza nome.", -"Error adding addressbook." => "Errore durante l'aggiunta della rubrica.", -"Error activating addressbook." => "Errore durante l'attivazione della rubrica.", "No contact ID was submitted." => "Nessun ID di contatto inviato.", "Error reading contact photo." => "Errore di lettura della foto del contatto.", "Error saving temporary file." => "Errore di salvataggio del file temporaneo.", "The loading photo is not valid." => "La foto caricata non è valida.", -"id is not set." => "ID non impostato.", "Information about vCard is incorrect. Please reload the page." => "Informazioni sulla vCard non corrette. Ricarica la pagina.", "Error deleting contact property." => "Errore durante l'eliminazione della proprietà del contatto.", "Contact ID is missing." => "Manca l'ID del contatto.", -"Missing contact id." => "ID di contatto mancante.", "No photo path was submitted." => "Non è stato inviato alcun percorso a una foto.", "File doesn't exist:" => "Il file non esiste:", "Error loading image." => "Errore di caricamento immagine.", -"element name is not set." => "il nome dell'elemento non è impostato.", +"Error getting contact object." => "Errore di recupero dell'oggetto contatto.", +"Error getting PHOTO property." => "Errore di recupero della proprietà FOTO.", +"Error saving contact." => "Errore di salvataggio del contatto.", +"Error resizing image" => "Errore di ridimensionamento dell'immagine", +"Error cropping image" => "Errore di ritaglio dell'immagine", +"Error creating temporary image" => "Errore durante la creazione dell'immagine temporanea", +"Error finding image: " => "Errore durante la ricerca dell'immagine: ", "checksum is not set." => "il codice di controllo non è impostato.", "Information about vCard is incorrect. Please reload the page: " => "Le informazioni della vCard non sono corrette. Ricarica la pagina: ", "Something went FUBAR. " => "Qualcosa è andato storto. ", @@ -41,8 +45,27 @@ "The uploaded file was only partially uploaded" => "Il file è stato inviato solo parzialmente", "No file was uploaded" => "Nessun file è stato inviato", "Missing a temporary folder" => "Manca una cartella temporanea", +"Couldn't save temporary image: " => "Impossibile salvare l'immagine temporanea: ", +"Couldn't load temporary image: " => "Impossibile caricare l'immagine temporanea: ", +"No file was uploaded. Unknown error" => "Nessun file è stato inviato. Errore sconosciuto", "Contacts" => "Contatti", -"Drop a VCF file to import contacts." => "Rilascia un file VCF per importare i contatti.", +"Sorry, this functionality has not been implemented yet" => "Siamo spiacenti, questa funzionalità non è stata ancora implementata", +"Not implemented" => "Non implementata", +"Couldn't get a valid address." => "Impossibile ottenere un indirizzo valido.", +"Error" => "Errore", +"Contact" => "Contatto", +"New" => "Nuovo", +"New Contact" => "Nuovo contatto", +"This property has to be non-empty." => "Questa proprietà non può essere vuota.", +"Couldn't serialize elements." => "Impossibile serializzare gli elementi.", +"'deleteProperty' called without type argument. Please report at bugs.owncloud.org" => "'deleteProperty' invocata senza l'argomento di tipo. Segnalalo a bugs.owncloud.org", +"Edit name" => "Modifica il nome", +"No files selected for upload." => "Nessun file selezionato per l'invio", +"The file you are trying to upload exceed the maximum size for file uploads on this server." => "Il file che stai cercando di inviare supera la dimensione massima per l'invio dei file su questo server.", +"Select type" => "Seleziona il tipo", +"Result: " => "Risultato: ", +" imported, " => " importato, ", +" failed." => " non riuscito.", "Addressbook not found." => "Rubrica non trovata.", "This is not your addressbook." => "Questa non è la tua rubrica.", "Contact could not be found." => "Il contatto non può essere trovato.", @@ -60,25 +83,54 @@ "Video" => "Video", "Pager" => "Cercapersone", "Internet" => "Internet", +"Birthday" => "Compleanno", +"Business" => "Lavoro", +"Call" => "Chiama", +"Clients" => "Client", +"Deliverer" => "Corriere", +"Holidays" => "Festività", +"Ideas" => "Idee", +"Journey" => "Viaggio", +"Jubilee" => "Anniversario", +"Meeting" => "Riunione", +"Other" => "Altro", +"Personal" => "Personale", +"Projects" => "Progetti", +"Questions" => "Domande", "{name}'s Birthday" => "Data di nascita di {name}", -"Contact" => "Contatto", "Add Contact" => "Aggiungi contatto", +"Import" => "Importa", "Addressbooks" => "Rubriche", +"Close" => "Chiudi", +"Keyboard shortcuts" => "Scorciatoie da tastiera", +"Navigation" => "Navigazione", +"Next contact in list" => "Contatto successivo in elenco", +"Previous contact in list" => "Contatto precedente in elenco", +"Expand/collapse current addressbook" => "Espandi/Contrai la rubrica corrente", +"Next/previous addressbook" => "Rubrica successiva/precedente", +"Actions" => "Azioni", +"Refresh contacts list" => "Aggiorna l'elenco dei contatti", +"Add new contact" => "Aggiungi un nuovo contatto", +"Add new addressbook" => "Aggiungi una nuova rubrica", +"Delete current contact" => "Elimina il contatto corrente", "Configure Address Books" => "Configura rubrica", "New Address Book" => "Nuova rubrica", -"Import from VCF" => "Importa da VCF", "CardDav Link" => "Link CardDav", "Download" => "Scarica", "Edit" => "Modifica", "Delete" => "Elimina", -"Download contact" => "Scarica contatto", -"Delete contact" => "Elimina contatto", "Drop photo to upload" => "Rilascia una foto da inviare", +"Delete current photo" => "Elimina la foto corrente", +"Edit current photo" => "Modifica la foto corrente", +"Upload new photo" => "Invia una nuova foto", +"Select photo from ownCloud" => "Seleziona la foto da ownCloud", "Format custom, Short name, Full name, Reverse or Reverse with comma" => "Formato personalizzato, nome breve, nome completo, invertito o invertito con virgola", "Edit name details" => "Modifica dettagli del nome", "Nickname" => "Pseudonimo", "Enter nickname" => "Inserisci pseudonimo", -"Birthday" => "Compleanno", +"Web site" => "Sito web", +"http://www.somesite.com" => "http://www.somesite.com", +"Go to web site" => "Vai al sito web", "dd-mm-yyyy" => "gg-mm-aaaa", "Groups" => "Gruppi", "Separate groups with commas" => "Separa i gruppi con virgole", @@ -94,24 +146,24 @@ "Edit address details" => "Modifica dettagli dell'indirizzo", "Add notes here." => "Aggiungi qui le note.", "Add field" => "Aggiungi campo", -"Profile picture" => "Immagine del profilo", "Phone" => "Telefono", "Note" => "Nota", -"Delete current photo" => "Elimina la foto corrente", -"Edit current photo" => "Modifica la foto corrente", -"Upload new photo" => "Invia una nuova foto", -"Select photo from ownCloud" => "Seleziona la foto da ownCloud", +"Download contact" => "Scarica contatto", +"Delete contact" => "Elimina contatto", +"The temporary image has been removed from cache." => "L'immagine temporanea è stata rimossa dalla cache.", "Edit address" => "Modifica indirizzo", "Type" => "Tipo", "PO Box" => "Casella postale", +"Street address" => "Indirizzo", +"Street and number" => "Via e numero", "Extended" => "Esteso", -"Street" => "Via", +"Apartment number etc." => "Numero appartamento ecc.", "City" => "Città", "Region" => "Regione", +"E.g. state or province" => "Ad es. stato o provincia", "Zipcode" => "CAP", +"Postal code" => "CAP", "Country" => "Stato", -"Edit categories" => "Modifica categorie", -"Add" => "Aggiungi", "Addressbook" => "Rubrica", "Hon. prefixes" => "Prefissi onorifici", "Miss" => "Sig.na", @@ -143,15 +195,16 @@ "Please choose the addressbook" => "Scegli la rubrica", "create a new addressbook" => "crea una nuova rubrica", "Name of new addressbook" => "Nome della nuova rubrica", -"Import" => "Importa", "Importing contacts" => "Importazione contatti", -"Select address book to import to:" => "Seleziona la rubrica di destinazione:", -"Select from HD" => "Seleziona da disco", "You have no contacts in your addressbook." => "Non hai contatti nella rubrica.", "Add contact" => "Aggiungi contatto", "Configure addressbooks" => "Configura rubriche", +"Select Address Books" => "Seleziona rubriche", +"Enter name" => "Inserisci il nome", +"Enter description" => "Inserisci una descrizione", "CardDAV syncing addresses" => "Indirizzi di sincronizzazione CardDAV", "more info" => "altre informazioni", "Primary address (Kontact et al)" => "Indirizzo principale (Kontact e altri)", -"iOS/OS X" => "iOS/OS X" +"iOS/OS X" => "iOS/OS X", +"Read only vCard directory link(s)" => "Collegamento(i) cartella vCard sola lettura" ); diff --git a/apps/contacts/l10n/vi.php b/apps/contacts/l10n/vi.php new file mode 100644 index 00000000000..5713ede7b00 --- /dev/null +++ b/apps/contacts/l10n/vi.php @@ -0,0 +1,48 @@ +<?php $TRANSLATIONS = array( +"element name is not set." => "tên phần tử không được thiết lập.", +"id is not set." => "id không được thiết lập.", +"No ID provided" => "Không có ID được cung cấp", +"No address books found." => "Không tìm thấy sổ địa chỉ.", +"No contacts found." => "Không tìm thấy danh sách", +"Missing ID" => "Missing ID", +"Error reading contact photo." => "Lỗi đọc liên lạc hình ảnh.", +"The loading photo is not valid." => "Các hình ảnh tải không hợp lệ.", +"File doesn't exist:" => "Tập tin không tồn tại", +"Error loading image." => "Lỗi khi tải hình ảnh.", +"Error uploading contacts to storage." => "Lỗi tải lên danh sách địa chỉ để lưu trữ.", +"There is no error, the file uploaded with success" => "Không có lỗi, các tập tin tải lên thành công", +"Contacts" => "Liên lạc", +"Contact" => "Danh sách", +"Address" => "Địa chỉ", +"Telephone" => "Điện thoại bàn", +"Email" => "Email", +"Organization" => "Tổ chức", +"Work" => "Công việc", +"Home" => "Nhà", +"Mobile" => "Di động", +"Fax" => "Fax", +"Video" => "Video", +"Pager" => "số trang", +"Birthday" => "Ngày sinh nhật", +"Add Contact" => "Thêm liên lạc", +"Addressbooks" => "Sổ địa chỉ", +"CardDav Link" => "CardDav Link", +"Download" => "Tải về", +"Edit" => "Sửa", +"Delete" => "Xóa", +"Phone" => "Điện thoại", +"Delete contact" => "Xóa liên lạc", +"PO Box" => "Hòm thư bưu điện", +"City" => "Thành phố", +"Region" => "Vùng/miền", +"Zipcode" => "Mã bưu điện", +"Country" => "Quốc gia", +"Addressbook" => "Sổ địa chỉ", +"New Addressbook" => "Sổ địa chỉ mới", +"Edit Addressbook" => "Sửa sổ địa chỉ", +"Displayname" => "Hiển thị tên", +"Active" => "Kích hoạt", +"Save" => "Lưu", +"Submit" => "Submit", +"Cancel" => "Hủy" +); diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php index 1bc4a195534..b2dde12684c 100644 --- a/apps/contacts/templates/index.php +++ b/apps/contacts/templates/index.php @@ -32,6 +32,38 @@ echo $this->inc('part.no_contacts'); } ?> + <div class="hidden" id="ninjahelp"> + <a class="close" tabindex="0" role="button"> + <img class="svg" src="core/img/actions/delete.svg" alt="<?php echo $l->t('Close'); ?>" /> + </a> + <h2><?php echo $l->t('Keyboard shortcuts'); ?></h2> + <div class="help-section"> + <h3><?php echo $l->t('Navigation'); ?></h3> + <dl> + <dt>j/Down/Space</dt> + <dd><?php echo $l->t('Next contact in list'); ?></dd> + <dt>k/Up/Shift-Space</dt> + <dd><?php echo $l->t('Previous contact in list'); ?></dd> + <dt>o/Enter</dt> + <dd><?php echo $l->t('Expand/collapse current addressbook'); ?></dd> + <dt>n/p</dt> + <dd><?php echo $l->t('Next/previous addressbook'); ?></dd> + </dl> + </div> + <div class="help-section"> + <h3><?php echo $l->t('Actions'); ?></h3> + <dl> + <dt>r</dt> + <dd><?php echo $l->t('Refresh contacts list'); ?></dd> + <dt>a</dt> + <dd><?php echo $l->t('Add new contact'); ?></dd> + <dt>Shift-a</dt> + <dd><?php echo $l->t('Add new addressbook'); ?></dd> + <dt>Shift-Delete</dt> + <dd><?php echo $l->t('Delete current contact'); ?></dd> + </dl> + </div> + </div> </div> <!-- Dialogs --> <div id="dialog_holder"></div> diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 7236deb65c9..cc9208ad08f 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -1,16 +1,25 @@ <?php // Init owncloud +global $eventSource; +if(!OC_User::isLoggedIn()){ + exit; +} -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); +session_write_close(); // Get the params -$dir = isset( $_POST['dir'] ) ? stripslashes($_POST['dir']) : ''; -$filename = isset( $_POST['filename'] ) ? stripslashes($_POST['filename']) : ''; -$content = isset( $_POST['content'] ) ? $_POST['content'] : ''; -$source = isset( $_POST['source'] ) ? stripslashes($_POST['source']) : ''; +$dir = isset( $_REQUEST['dir'] ) ? stripslashes($_REQUEST['dir']) : ''; +$filename = isset( $_REQUEST['filename'] ) ? stripslashes($_REQUEST['filename']) : ''; +$content = isset( $_REQUEST['content'] ) ? $_REQUEST['content'] : ''; +$source = isset( $_REQUEST['source'] ) ? stripslashes($_REQUEST['source']) : ''; + +if($source){ + $eventSource=new OC_EventSource(); +}else{ + OC_JSON::callCheck(); +} if($filename == '') { OCP\JSON::error(array("data" => array( "message" => "Empty Filename" ))); @@ -21,22 +30,49 @@ if(strpos($filename,'/')!==false){ exit(); } +function progress($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max){ + static $filesize = 0; + static $lastsize = 0; + global $eventSource; + + switch($notification_code) { + case STREAM_NOTIFY_FILE_SIZE_IS: + $filesize = $bytes_max; + break; + + case STREAM_NOTIFY_PROGRESS: + if ($bytes_transferred > 0) { + if (!isset($filesize)) { + } else { + $progress = (int)(($bytes_transferred/$filesize)*100); + if($progress>$lastsize){//limit the number or messages send + $eventSource->send('progress',$progress); + } + $lastsize=$progress; + } + } + break; + } +} + if($source){ if(substr($source,0,8)!='https://' and substr($source,0,7)!='http://'){ OCP\JSON::error(array("data" => array( "message" => "Not a valid source" ))); exit(); } - $sourceStream=fopen($source,'rb'); + + $ctx = stream_context_create(null, array('notification' =>'progress')); + $sourceStream=fopen($source,'rb', false, $ctx); $target=$dir.'/'.$filename; $result=OC_Filesystem::file_put_contents($target,$sourceStream); if($result){ $mime=OC_Filesystem::getMimetype($target); - OCP\JSON::success(array("data" => array('mime'=>$mime))); - exit(); + $eventSource->send('success',$mime); }else{ - OCP\JSON::error(array("data" => array( "message" => "Error while downloading ".$source. ' to '.$target ))); - exit(); + $eventSource->send('error',"Error while downloading ".$source. ' to '.$target); } + $eventSource->close(); + exit(); }else{ if($content){ if(OC_Filesystem::file_put_contents($dir.'/'.$filename,$content)){ diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php index 6fcf97688c2..eef38858516 100644 --- a/apps/files/ajax/scan.php +++ b/apps/files/ajax/scan.php @@ -16,6 +16,11 @@ session_write_close(); if($force or !OC_FileCache::inCache('')){ if(!$checkOnly){ OCP\DB::beginTransaction(); + + if(OC_Cache::isFast()){ + OC_Cache::clear('fileid/'); //make sure the old fileid's don't mess things up + } + OC_FileCache::scan($dir,$eventSource); OC_FileCache::clean(); OCP\DB::commit(); diff --git a/apps/files/appinfo/update.php b/apps/files/appinfo/update.php index f9953ba4de5..5514aed197f 100644 --- a/apps/files/appinfo/update.php +++ b/apps/files/appinfo/update.php @@ -1,5 +1,16 @@ <?php +// fix webdav properties, remove namespace information between curly bracket (update from OC4 to OC5) +$installedVersion=OCP\Config::getAppValue('files', 'installed_version');
+if (version_compare($installedVersion, '1.1.4', '<')) { + $query = OC_DB::prepare( "SELECT propertyname, propertypath, userid FROM `*PREFIX*properties`" );
+ $result = $query->execute();
+ while( $row = $result->fetchRow()){
+ $query = OC_DB::prepare( 'UPDATE *PREFIX*properties SET propertyname = ? WHERE userid = ? AND propertypath = ?' );
+ $query->execute( array( preg_replace("/^{.*}/", "", $row["propertyname"]),$row["userid"], $row["propertypath"] ));
+ } +} + //update from OC 3 //try to remove remaining files. diff --git a/apps/files/appinfo/version b/apps/files/appinfo/version index 9c1218c201f..1b87bcd0b09 100644 --- a/apps/files/appinfo/version +++ b/apps/files/appinfo/version @@ -1 +1 @@ -1.1.3
\ No newline at end of file +1.1.4
\ No newline at end of file diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 86c5185bf72..a4e2361feeb 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -497,23 +497,27 @@ $(document).ready(function() { localName=(localName.match(/:\/\/(.[^/]+)/)[1]).replace('www.',''); } localName = getUniqueName(localName); - $.post( - OC.filePath('files','ajax','newfile.php'), - {dir:$('#dir').val(),source:name,filename:localName}, - function(result){ - if(result.status == 'success'){ - var date=new Date(); - FileList.addFile(localName,0,date); - var tr=$('tr').filterAttr('data-file',localName); - tr.data('mime',result.data.mime); - getMimeIcon(result.data.mime,function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); - }); - }else{ + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); - } - } - ); + var eventSource=new OC.EventSource(OC.filePath('files','ajax','newfile.php'),{dir:$('#dir').val(),source:name,filename:localName}); + eventSource.listen('progress',function(progress){ + $('#uploadprogressbar').progressbar('value',progress); + }); + eventSource.listen('success',function(mime){ + $('#uploadprogressbar').fadeOut(); + var date=new Date(); + FileList.addFile(localName,0,date); + var tr=$('tr').filterAttr('data-file',localName); + tr.data('mime',mime); + getMimeIcon(mime,function(path){ + tr.find('td.filename').attr('style','background-image:url('+path+')'); + }); + }); + eventSource.listen('error',function(error){ + $('#uploadprogressbar').fadeOut(); + alert(error); + }); break; } var li=$(this).parent(); diff --git a/apps/files/l10n/es.php b/apps/files/l10n/es.php index 67bfb4702e8..506218815bb 100644 --- a/apps/files/l10n/es.php +++ b/apps/files/l10n/es.php @@ -7,8 +7,21 @@ "Missing a temporary folder" => "Falta un directorio temporal", "Failed to write to disk" => "La escritura en disco ha fallado", "Files" => "Archivos", +"Unshare" => "No compartir", +"Delete" => "Eliminado", +"undo deletion" => "deshacer la eliminación", +"generating ZIP-file, it may take some time." => "generando un fichero ZIP, puede llevar un tiempo.", +"Unable to upload your file as it is a directory or has 0 bytes" => "No ha sido posible subir tu archivo porque es un directorio o tiene 0 bytes", +"Upload Error" => "Error al subir el archivo", +"Pending" => "Pendiente", +"Upload cancelled." => "Subida cancelada.", +"Invalid name, '/' is not allowed." => "Nombre no válido, '/' no está permitido.", "Size" => "Tamaño", "Modified" => "Modificado", +"folder" => "carpeta", +"folders" => "carpetas", +"file" => "archivo", +"files" => "archivos", "File handling" => "Tratamiento de archivos", "Maximum upload size" => "Tamaño máximo de subida", "max. possible: " => "máx. posible:", @@ -26,7 +39,6 @@ "Name" => "Nombre", "Share" => "Compartir", "Download" => "Descargar", -"Delete" => "Eliminado", "Upload too large" => "El archivo es demasiado grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "Los archivos que estás intentando subir sobrepasan el tamaño máximo permitido por este servidor.", "Files are being scanned, please wait." => "Se están escaneando los archivos, por favor espere.", diff --git a/apps/files/l10n/it.php b/apps/files/l10n/it.php index 82871826c18..0bf113eba1b 100644 --- a/apps/files/l10n/it.php +++ b/apps/files/l10n/it.php @@ -7,8 +7,21 @@ "Missing a temporary folder" => "Cartella temporanea mancante", "Failed to write to disk" => "Scrittura su disco non riuscita", "Files" => "File", +"Unshare" => "Rimuovi condivisione", +"Delete" => "Elimina", +"undo deletion" => "annulla l'eliminazione", +"generating ZIP-file, it may take some time." => "creazione file ZIP, potrebbe richiedere del tempo.", +"Unable to upload your file as it is a directory or has 0 bytes" => "Impossibile inviare il file poiché è una cartella o ha dimensione 0 byte", +"Upload Error" => "Errore di invio", +"Pending" => "In corso", +"Upload cancelled." => "Invio annullato", +"Invalid name, '/' is not allowed." => "Nome non valido", "Size" => "Dimensione", "Modified" => "Modificato", +"folder" => "cartella", +"folders" => "cartelle", +"file" => "file", +"files" => "file", "File handling" => "Gestione file", "Maximum upload size" => "Dimensione massima upload", "max. possible: " => "numero mass.: ", @@ -26,7 +39,6 @@ "Name" => "Nome", "Share" => "Condividi", "Download" => "Scarica", -"Delete" => "Elimina", "Upload too large" => "Il file caricato è troppo grande", "The files you are trying to upload exceed the maximum size for file uploads on this server." => "I file che stai provando a caricare superano la dimensione massima consentita su questo server.", "Files are being scanned, please wait." => "Scansione dei file in corso, attendi", diff --git a/apps/files/l10n/vi.php b/apps/files/l10n/vi.php new file mode 100644 index 00000000000..c2284d5feb9 --- /dev/null +++ b/apps/files/l10n/vi.php @@ -0,0 +1,31 @@ +<?php $TRANSLATIONS = array( +"Files" => "Tập tin", +"Delete" => "Xóa", +"Upload Error" => "Tải lên lỗi", +"Pending" => "Chờ", +"Upload cancelled." => "Hủy tải lên", +"Invalid name, '/' is not allowed." => "Tên không hợp lệ ,không được phép dùng '/'", +"Size" => "Kích cỡ", +"Modified" => "Thay đổi", +"folder" => "folder", +"folders" => "folders", +"file" => "file", +"files" => "files", +"File handling" => "Xử lý tập tin", +"Maximum upload size" => "Kích thước tối đa ", +"Enable ZIP-download" => "Cho phép ZIP-download", +"0 is unlimited" => "0 là không giới hạn", +"Maximum input size for ZIP files" => "Kích thước tối đa cho các tập tin ZIP", +"New" => "Mới", +"Text file" => "Tập tin văn bản", +"Folder" => "Folder", +"From url" => "Từ url", +"Upload" => "Tải lên", +"Cancel upload" => "Hủy upload", +"Nothing in here. Upload something!" => "Không có gì ở đây .Hãy tải lên một cái gì đó !", +"Name" => "Tên", +"Share" => "Chia sẻ", +"Download" => "Tải xuống", +"Upload too large" => "File tải lên quá lớn", +"Files are being scanned, please wait." => "Tập tin đang được quét ,vui lòng chờ." +); diff --git a/apps/files_imageviewer/js/jquery.fancybox-1.3.4.js b/apps/files_imageviewer/js/jquery.fancybox-1.3.4.js index a1db7b6198c..e5493cd9393 100644 --- a/apps/files_imageviewer/js/jquery.fancybox-1.3.4.js +++ b/apps/files_imageviewer/js/jquery.fancybox-1.3.4.js @@ -124,9 +124,7 @@ } else if (href.indexOf("#") === 0) {
type = 'inline';
- } else {
- type = 'ajax';
- }
+ }
}
if (!type) {
diff --git a/apps/files_imageviewer/js/jquery.fancybox-1.3.4.pack.js b/apps/files_imageviewer/js/jquery.fancybox-1.3.4.pack.js index e5ee2ae3595..260f2c2d466 100644 --- a/apps/files_imageviewer/js/jquery.fancybox-1.3.4.pack.js +++ b/apps/files_imageviewer/js/jquery.fancybox-1.3.4.pack.js @@ -1 +1 @@ -(function(B){var L,T,Q,M,d,m,J,A,O,z,C=0,H={},j=[],e=0,G={},y=[],f=null,o=new Image(),i=/\.(jpg|gif|png|bmp|jpeg)(.*)?$/i,k=/[^\.]\.(swf)\s*$/i,p,N=1,h=0,t="",b,c,P=false,s=B.extend(B("<div/>")[0],{prop:0}),S=B.browser.msie&&B.browser.version<7&&!window.XMLHttpRequest,r=function(){T.hide();o.onerror=o.onload=null;if(f){f.abort()}L.empty()},x=function(){if(false===H.onError(j,C,H)){T.hide();P=false;return}H.titleShow=false;H.width="auto";H.height="auto";L.html('<p id="fancybox-error">The requested content cannot be loaded.<br />Please try again later.</p>');n()},w=function(){var Z=j[C],W,Y,ab,aa,V,X;r();H=B.extend({},B.fn.fancybox.defaults,(typeof B(Z).data("fancybox")=="undefined"?H:B(Z).data("fancybox")));X=H.onStart(j,C,H);if(X===false){P=false;return}else{if(typeof X=="object"){H=B.extend(H,X)}}ab=H.title||(Z.nodeName?B(Z).attr("title"):Z.title)||"";if(Z.nodeName&&!H.orig){H.orig=B(Z).children("img:first").length?B(Z).children("img:first"):B(Z)}if(ab===""&&H.orig&&H.titleFromAlt){ab=H.orig.attr("alt")}ab=ab.replace(/</,"<").replace(/>/,">");W=H.href||(Z.nodeName?B(Z).attr("href"):Z.href)||null;if((/^(?:javascript)/i).test(W)||W=="#"){W=null}if(H.type){Y=H.type;if(!W){W=H.content}}else{if(H.content){Y="html"}else{if(W){if(W.match(i)){Y="image"}else{if(W.match(k)){Y="swf"}else{if(B(Z).hasClass("iframe")){Y="iframe"}else{if(W.indexOf("#")===0){Y="inline"}else{Y="ajax"}}}}}}}if(!Y){x();return}if(Y=="inline"){Z=W.substr(W.indexOf("#"));Y=B(Z).length>0?"inline":"ajax"}H.type=Y;H.href=W;H.title=ab;if(H.autoDimensions){if(H.type=="html"||H.type=="inline"||H.type=="ajax"){H.width="auto";H.height="auto"}else{H.autoDimensions=false}}if(H.modal){H.overlayShow=true;H.hideOnOverlayClick=false;H.hideOnContentClick=false;H.enableEscapeButton=false;H.showCloseButton=false}H.padding=parseInt(H.padding,10);H.margin=parseInt(H.margin,10);L.css("padding",(H.padding+H.margin));B(".fancybox-inline-tmp").unbind("fancybox-cancel").bind("fancybox-change",function(){B(this).replaceWith(m.children())});switch(Y){case"html":L.html(H.content);n();break;case"inline":if(B(Z).parent().is("#fancybox-content")===true){P=false;return}B('<div class="fancybox-inline-tmp" />').hide().insertBefore(B(Z)).bind("fancybox-cleanup",function(){B(this).replaceWith(m.children())}).bind("fancybox-cancel",function(){B(this).replaceWith(L.children())});B(Z).appendTo(L);n();break;case"image":P=false;B.fancybox.showActivity();o=new Image();o.onerror=function(){x()};o.onload=function(){P=true;o.onerror=o.onload=null;F()};o.src=W;break;case"swf":H.scrolling="no";aa='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+H.width+'" height="'+H.height+'"><param name="movie" value="'+W+'"></param>';V="";B.each(H.swf,function(ac,ad){aa+='<param name="'+ac+'" value="'+ad+'"></param>';V+=" "+ac+'="'+ad+'"'});aa+='<embed src="'+W+'" type="application/x-shockwave-flash" width="'+H.width+'" height="'+H.height+'"'+V+"></embed></object>";L.html(aa);n();break;case"ajax":P=false;B.fancybox.showActivity();H.ajax.win=H.ajax.success;f=B.ajax(B.extend({},H.ajax,{url:W,data:H.ajax.data||{},error:function(ac,ae,ad){if(ac.status>0){x()}},success:function(ad,af,ac){var ae=typeof ac=="object"?ac:f;if(ae.status==200){if(typeof H.ajax.win=="function"){X=H.ajax.win(W,ad,af,ac);if(X===false){T.hide();return}else{if(typeof X=="string"||typeof X=="object"){ad=X}}}L.html(ad);n()}}}));break;case"iframe":E();break}},n=function(){var V=H.width,W=H.height;if(V.toString().indexOf("%")>-1){V=parseInt((B(window).width()-(H.margin*2))*parseFloat(V)/100,10)+"px"}else{V=V=="auto"?"auto":V+"px"}if(W.toString().indexOf("%")>-1){W=parseInt((B(window).height()-(H.margin*2))*parseFloat(W)/100,10)+"px"}else{W=W=="auto"?"auto":W+"px"}L.wrapInner('<div style="width:'+V+";height:"+W+";overflow: "+(H.scrolling=="auto"?"auto":(H.scrolling=="yes"?"scroll":"hidden"))+';position:relative;"></div>');H.width=L.width();H.height=L.height();E()},F=function(){H.width=o.width;H.height=o.height;B("<img />").attr({id:"fancybox-img",src:o.src,alt:H.title}).appendTo(L);E()},E=function(){var W,V;T.hide();if(M.is(":visible")&&false===G.onCleanup(y,e,G)){B.event.trigger("fancybox-cancel");P=false;return}P=true;B(m.add(Q)).unbind();B(window).unbind("resize.fb scroll.fb");B(document).unbind("keydown.fb");if(M.is(":visible")&&G.titlePosition!=="outside"){M.css("height",M.height())}y=j;e=C;G=H;if(G.overlayShow){Q.css({"background-color":G.overlayColor,opacity:G.overlayOpacity,cursor:G.hideOnOverlayClick?"pointer":"auto",height:B(document).height()});if(!Q.is(":visible")){if(S){B("select:not(#fancybox-tmp select)").filter(function(){return this.style.visibility!=="hidden"}).css({visibility:"hidden"}).one("fancybox-cleanup",function(){this.style.visibility="inherit"})}Q.show()}}else{Q.hide()}c=R();l();if(M.is(":visible")){B(J.add(O).add(z)).hide();W=M.position(),b={top:W.top,left:W.left,width:M.width(),height:M.height()};V=(b.width==c.width&&b.height==c.height);m.fadeTo(G.changeFade,0.3,function(){var X=function(){m.html(L.contents()).fadeTo(G.changeFade,1,v)};B.event.trigger("fancybox-change");m.empty().removeAttr("filter").css({"border-width":G.padding,width:c.width-G.padding*2,height:H.autoDimensions?"auto":c.height-h-G.padding*2});if(V){X()}else{s.prop=0;B(s).animate({prop:1},{duration:G.changeSpeed,easing:G.easingChange,step:U,complete:X})}});return}M.removeAttr("style");m.css("border-width",G.padding);if(G.transitionIn=="elastic"){b=I();m.html(L.contents());M.show();if(G.opacity){c.opacity=0}s.prop=0;B(s).animate({prop:1},{duration:G.speedIn,easing:G.easingIn,step:U,complete:v});return}if(G.titlePosition=="inside"&&h>0){A.show()}m.css({width:c.width-G.padding*2,height:H.autoDimensions?"auto":c.height-h-G.padding*2}).html(L.contents());M.css(c).fadeIn(G.transitionIn=="none"?0:G.speedIn,v)},D=function(V){if(V&&V.length){if(G.titlePosition=="float"){return'<table id="fancybox-title-float-wrap" cellpadding="0" cellspacing="0"><tr><td id="fancybox-title-float-left"></td><td id="fancybox-title-float-main">'+V+'</td><td id="fancybox-title-float-right"></td></tr></table>'}return'<div id="fancybox-title-'+G.titlePosition+'">'+V+"</div>"}return false},l=function(){t=G.title||"";h=0;A.empty().removeAttr("style").removeClass();if(G.titleShow===false){A.hide();return}t=B.isFunction(G.titleFormat)?G.titleFormat(t,y,e,G):D(t);if(!t||t===""){A.hide();return}A.addClass("fancybox-title-"+G.titlePosition).html(t).appendTo("body").show();switch(G.titlePosition){case"inside":A.css({width:c.width-(G.padding*2),marginLeft:G.padding,marginRight:G.padding});h=A.outerHeight(true);A.appendTo(d);c.height+=h;break;case"over":A.css({marginLeft:G.padding,width:c.width-(G.padding*2),bottom:G.padding}).appendTo(d);break;case"float":A.css("left",parseInt((A.width()-c.width-40)/2,10)*-1).appendTo(M);break;default:A.css({width:c.width-(G.padding*2),paddingLeft:G.padding,paddingRight:G.padding}).appendTo(M);break}A.hide()},g=function(){if(G.enableEscapeButton||G.enableKeyboardNav){B(document).bind("keydown.fb",function(V){if(V.keyCode==27&&G.enableEscapeButton){V.preventDefault();B.fancybox.close()}else{if((V.keyCode==37||V.keyCode==39)&&G.enableKeyboardNav&&V.target.tagName!=="INPUT"&&V.target.tagName!=="TEXTAREA"&&V.target.tagName!=="SELECT"){V.preventDefault();B.fancybox[V.keyCode==37?"prev":"next"]()}}})}if(!G.showNavArrows){O.hide();z.hide();return}if((G.cyclic&&y.length>1)||e!==0){O.show()}if((G.cyclic&&y.length>1)||e!=(y.length-1)){z.show()}},v=function(){if(!B.support.opacity){m.get(0).style.removeAttribute("filter");M.get(0).style.removeAttribute("filter")}if(H.autoDimensions){m.css("height","auto")}M.css("height","auto");if(t&&t.length){A.show()}if(G.showCloseButton){J.show()}g();if(G.hideOnContentClick){m.bind("click",B.fancybox.close)}if(G.hideOnOverlayClick){Q.bind("click",B.fancybox.close)}B(window).bind("resize.fb",B.fancybox.resize);if(G.centerOnScroll){B(window).bind("scroll.fb",B.fancybox.center)}if(G.type=="iframe"){B('<iframe id="fancybox-frame" name="fancybox-frame'+new Date().getTime()+'" frameborder="0" hspace="0" '+(B.browser.msie?'allowtransparency="true""':"")+' scrolling="'+H.scrolling+'" src="'+G.href+'"></iframe>').appendTo(m)}M.show();P=false;B.fancybox.center();G.onComplete(y,e,G);K()},K=function(){var V,W;if((y.length-1)>e){V=y[e+1].href;if(typeof V!=="undefined"&&V.match(i)){W=new Image();W.src=V}}if(e>0){V=y[e-1].href;if(typeof V!=="undefined"&&V.match(i)){W=new Image();W.src=V}}},U=function(W){var V={width:parseInt(b.width+(c.width-b.width)*W,10),height:parseInt(b.height+(c.height-b.height)*W,10),top:parseInt(b.top+(c.top-b.top)*W,10),left:parseInt(b.left+(c.left-b.left)*W,10)};if(typeof c.opacity!=="undefined"){V.opacity=W<0.5?0.5:W}M.css(V);m.css({width:V.width-G.padding*2,height:V.height-(h*W)-G.padding*2})},u=function(){return[B(window).width()-(G.margin*2),B(window).height()-(G.margin*2),B(document).scrollLeft()+G.margin,B(document).scrollTop()+G.margin]},R=function(){var V=u(),Z={},W=G.autoScale,X=G.padding*2,Y;if(G.width.toString().indexOf("%")>-1){Z.width=parseInt((V[0]*parseFloat(G.width))/100,10)}else{Z.width=G.width+X}if(G.height.toString().indexOf("%")>-1){Z.height=parseInt((V[1]*parseFloat(G.height))/100,10)}else{Z.height=G.height+X}if(W&&(Z.width>V[0]||Z.height>V[1])){if(H.type=="image"||H.type=="swf"){Y=(G.width)/(G.height);if((Z.width)>V[0]){Z.width=V[0];Z.height=parseInt(((Z.width-X)/Y)+X,10)}if((Z.height)>V[1]){Z.height=V[1];Z.width=parseInt(((Z.height-X)*Y)+X,10)}}else{Z.width=Math.min(Z.width,V[0]);Z.height=Math.min(Z.height,V[1])}}Z.top=parseInt(Math.max(V[3]-20,V[3]+((V[1]-Z.height-40)*0.5)),10);Z.left=parseInt(Math.max(V[2]-20,V[2]+((V[0]-Z.width-40)*0.5)),10);return Z},q=function(V){var W=V.offset();W.top+=parseInt(V.css("paddingTop"),10)||0;W.left+=parseInt(V.css("paddingLeft"),10)||0;W.top+=parseInt(V.css("border-top-width"),10)||0;W.left+=parseInt(V.css("border-left-width"),10)||0;W.width=V.width();W.height=V.height();return W},I=function(){var Y=H.orig?B(H.orig):false,X={},W,V;if(Y&&Y.length){W=q(Y);X={width:W.width+(G.padding*2),height:W.height+(G.padding*2),top:W.top-G.padding-20,left:W.left-G.padding-20}}else{V=u();X={width:G.padding*2,height:G.padding*2,top:parseInt(V[3]+V[1]*0.5,10),left:parseInt(V[2]+V[0]*0.5,10)}}return X},a=function(){if(!T.is(":visible")){clearInterval(p);return}B("div",T).css("top",(N*-40)+"px");N=(N+1)%12};B.fn.fancybox=function(V){if(!B(this).length){return this}B(this).data("fancybox",B.extend({},V,(B.metadata?B(this).metadata():{}))).unbind("click.fb").bind("click.fb",function(X){X.preventDefault();if(P){return}P=true;B(this).blur();j=[];C=0;var W=B(this).attr("rel")||"";if(!W||W==""||W==="nofollow"){j.push(this)}else{j=B("a[rel="+W+"], area[rel="+W+"]");C=j.index(this)}w();return});return this};B.fancybox=function(Y){var X;if(P){return}P=true;X=typeof arguments[1]!=="undefined"?arguments[1]:{};j=[];C=parseInt(X.index,10)||0;if(B.isArray(Y)){for(var W=0,V=Y.length;W<V;W++){if(typeof Y[W]=="object"){B(Y[W]).data("fancybox",B.extend({},X,Y[W]))}else{Y[W]=B({}).data("fancybox",B.extend({content:Y[W]},X))}}j=jQuery.merge(j,Y)}else{if(typeof Y=="object"){B(Y).data("fancybox",B.extend({},X,Y))}else{Y=B({}).data("fancybox",B.extend({content:Y},X))}j.push(Y)}if(C>j.length||C<0){C=0}w()};B.fancybox.showActivity=function(){clearInterval(p);T.show();p=setInterval(a,66)};B.fancybox.hideActivity=function(){T.hide()};B.fancybox.next=function(){return B.fancybox.pos(e+1)};B.fancybox.prev=function(){return B.fancybox.pos(e-1)};B.fancybox.pos=function(V){if(P){return}V=parseInt(V);j=y;if(V>-1&&V<y.length){C=V;w()}else{if(G.cyclic&&y.length>1){C=V>=y.length?0:y.length-1;w()}}return};B.fancybox.cancel=function(){if(P){return}P=true;B.event.trigger("fancybox-cancel");r();H.onCancel(j,C,H);P=false};B.fancybox.close=function(){if(P||M.is(":hidden")){return}P=true;if(G&&false===G.onCleanup(y,e,G)){P=false;return}r();B(J.add(O).add(z)).hide();B(m.add(Q)).unbind();B(window).unbind("resize.fb scroll.fb");B(document).unbind("keydown.fb");m.find("iframe").attr("src",S&&/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank");if(G.titlePosition!=="inside"){A.empty()}M.stop();function V(){Q.fadeOut("fast");A.empty().hide();M.hide();B.event.trigger("fancybox-cleanup");m.empty();G.onClosed(y,e,G);y=H=[];e=C=0;G=H={};P=false}if(G.transitionOut=="elastic"){b=I();var W=M.position();c={top:W.top,left:W.left,width:M.width(),height:M.height()};if(G.opacity){c.opacity=1}A.empty().hide();s.prop=1;B(s).animate({prop:0},{duration:G.speedOut,easing:G.easingOut,step:U,complete:V})}else{M.fadeOut(G.transitionOut=="none"?0:G.speedOut,V)}};B.fancybox.resize=function(){if(Q.is(":visible")){Q.css("height",B(document).height())}B.fancybox.center(true)};B.fancybox.center=function(){var V,W;if(P){return}W=arguments[0]===true?1:0;V=u();if(!W&&(M.width()>V[0]||M.height()>V[1])){return}M.stop().animate({top:parseInt(Math.max(V[3]-20,V[3]+((V[1]-m.height()-40)*0.5)-G.padding)),left:parseInt(Math.max(V[2]-20,V[2]+((V[0]-m.width()-40)*0.5)-G.padding))},typeof arguments[0]=="number"?arguments[0]:200)};B.fancybox.init=function(){if(B("#fancybox-wrap").length){return}B("body").append(L=B('<div id="fancybox-tmp"></div>'),T=B('<div id="fancybox-loading"><div></div></div>'),Q=B('<div id="fancybox-overlay"></div>'),M=B('<div id="fancybox-wrap"></div>'));d=B('<div id="fancybox-outer"></div>').append('<div class="fancybox-bg" id="fancybox-bg-n"></div><div class="fancybox-bg" id="fancybox-bg-ne"></div><div class="fancybox-bg" id="fancybox-bg-e"></div><div class="fancybox-bg" id="fancybox-bg-se"></div><div class="fancybox-bg" id="fancybox-bg-s"></div><div class="fancybox-bg" id="fancybox-bg-sw"></div><div class="fancybox-bg" id="fancybox-bg-w"></div><div class="fancybox-bg" id="fancybox-bg-nw"></div>').appendTo(M);d.append(m=B('<div id="fancybox-content"></div>'),J=B('<a id="fancybox-close"></a>'),A=B('<div id="fancybox-title"></div>'),O=B('<a href="javascript:;" id="fancybox-left"><span class="fancy-ico" id="fancybox-left-ico"></span></a>'),z=B('<a href="javascript:;" id="fancybox-right"><span class="fancy-ico" id="fancybox-right-ico"></span></a>'));J.click(B.fancybox.close);T.click(B.fancybox.cancel);O.click(function(V){V.preventDefault();B.fancybox.prev()});z.click(function(V){V.preventDefault();B.fancybox.next()});if(B.fn.mousewheel){M.bind("mousewheel.fb",function(V,W){if(P){V.preventDefault()}else{if(B(V.target).get(0).clientHeight==0||B(V.target).get(0).scrollHeight===B(V.target).get(0).clientHeight){V.preventDefault();B.fancybox[W>0?"prev":"next"]()}}})}if(!B.support.opacity){M.addClass("fancybox-ie")}if(S){T.addClass("fancybox-ie6");M.addClass("fancybox-ie6");B('<iframe id="fancybox-hide-sel-frame" src="'+(/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank")+'" scrolling="no" border="0" frameborder="0" tabindex="-1"></iframe>').prependTo(d)}};B.fn.fancybox.defaults={padding:10,margin:40,opacity:false,modal:false,cyclic:false,scrolling:"auto",width:560,height:340,autoScale:true,autoDimensions:true,centerOnScroll:false,ajax:{},swf:{wmode:"transparent"},hideOnOverlayClick:true,hideOnContentClick:false,overlayShow:true,overlayOpacity:0.7,overlayColor:"#777",titleShow:true,titlePosition:"float",titleFormat:null,titleFromAlt:false,transitionIn:"fade",transitionOut:"fade",speedIn:300,speedOut:300,changeSpeed:300,changeFade:"fast",easingIn:"swing",easingOut:"swing",showCloseButton:true,showNavArrows:true,enableEscapeButton:true,enableKeyboardNav:true,onStart:function(){},onCancel:function(){},onComplete:function(){},onCleanup:function(){},onClosed:function(){},onError:function(){}};B(document).ready(function(){B.fancybox.init()})})(jQuery);
\ No newline at end of file +(function(B){var L,T,Q,M,d,m,J,A,O,z,C=0,H={},j=[],e=0,G={},y=[],f=null,o=new Image(),i=/\.(jpg|gif|png|bmp|jpeg)(.*)?$/i,k=/[^\.]\.(swf)\s*$/i,p,N=1,h=0,t="",b,c,P=false,s=B.extend(B("<div/>")[0],{prop:0}),S=B.browser.msie&&B.browser.version<7&&!window.XMLHttpRequest,r=function(){T.hide();o.onerror=o.onload=null;if(f){f.abort()}L.empty()},x=function(){if(false===H.onError(j,C,H)){T.hide();P=false;return}H.titleShow=false;H.width="auto";H.height="auto";L.html('<p id="fancybox-error">The requested content cannot be loaded.<br />Please try again later.</p>');n()},w=function(){var Z=j[C],W,Y,ab,aa,V,X;r();H=B.extend({},B.fn.fancybox.defaults,(typeof B(Z).data("fancybox")=="undefined"?H:B(Z).data("fancybox")));X=H.onStart(j,C,H);if(X===false){P=false;return}else{if(typeof X=="object"){H=B.extend(H,X)}}ab=H.title||(Z.nodeName?B(Z).attr("title"):Z.title)||"";if(Z.nodeName&&!H.orig){H.orig=B(Z).children("img:first").length?B(Z).children("img:first"):B(Z)}if(ab===""&&H.orig&&H.titleFromAlt){ab=H.orig.attr("alt")}ab=ab.replace(/</,"<").replace(/>/,">");W=H.href||(Z.nodeName?B(Z).attr("href"):Z.href)||null;if((/^(?:javascript)/i).test(W)||W=="#"){W=null}if(H.type){Y=H.type;if(!W){W=H.content}}else{if(H.content){Y="html"}else{if(W){if(W.match(i)){Y="image"}else{if(W.match(k)){Y="swf"}else{if(B(Z).hasClass("iframe")){Y="iframe"}else{if(W.indexOf("#")===0){Y="inline"}}}}}}}if(!Y){x();return}if(Y=="inline"){Z=W.substr(W.indexOf("#"));Y=B(Z).length>0?"inline":"ajax"}H.type=Y;H.href=W;H.title=ab;if(H.autoDimensions){if(H.type=="html"||H.type=="inline"||H.type=="ajax"){H.width="auto";H.height="auto"}else{H.autoDimensions=false}}if(H.modal){H.overlayShow=true;H.hideOnOverlayClick=false;H.hideOnContentClick=false;H.enableEscapeButton=false;H.showCloseButton=false}H.padding=parseInt(H.padding,10);H.margin=parseInt(H.margin,10);L.css("padding",(H.padding+H.margin));B(".fancybox-inline-tmp").unbind("fancybox-cancel").bind("fancybox-change",function(){B(this).replaceWith(m.children())});switch(Y){case"html":L.html(H.content);n();break;case"inline":if(B(Z).parent().is("#fancybox-content")===true){P=false;return}B('<div class="fancybox-inline-tmp" />').hide().insertBefore(B(Z)).bind("fancybox-cleanup",function(){B(this).replaceWith(m.children())}).bind("fancybox-cancel",function(){B(this).replaceWith(L.children())});B(Z).appendTo(L);n();break;case"image":P=false;B.fancybox.showActivity();o=new Image();o.onerror=function(){x()};o.onload=function(){P=true;o.onerror=o.onload=null;F()};o.src=W;break;case"swf":H.scrolling="no";aa='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="'+H.width+'" height="'+H.height+'"><param name="movie" value="'+W+'"></param>';V="";B.each(H.swf,function(ac,ad){aa+='<param name="'+ac+'" value="'+ad+'"></param>';V+=" "+ac+'="'+ad+'"'});aa+='<embed src="'+W+'" type="application/x-shockwave-flash" width="'+H.width+'" height="'+H.height+'"'+V+"></embed></object>";L.html(aa);n();break;case"ajax":P=false;B.fancybox.showActivity();H.ajax.win=H.ajax.success;f=B.ajax(B.extend({},H.ajax,{url:W,data:H.ajax.data||{},error:function(ac,ae,ad){if(ac.status>0){x()}},success:function(ad,af,ac){var ae=typeof ac=="object"?ac:f;if(ae.status==200){if(typeof H.ajax.win=="function"){X=H.ajax.win(W,ad,af,ac);if(X===false){T.hide();return}else{if(typeof X=="string"||typeof X=="object"){ad=X}}}L.html(ad);n()}}}));break;case"iframe":E();break}},n=function(){var V=H.width,W=H.height;if(V.toString().indexOf("%")>-1){V=parseInt((B(window).width()-(H.margin*2))*parseFloat(V)/100,10)+"px"}else{V=V=="auto"?"auto":V+"px"}if(W.toString().indexOf("%")>-1){W=parseInt((B(window).height()-(H.margin*2))*parseFloat(W)/100,10)+"px"}else{W=W=="auto"?"auto":W+"px"}L.wrapInner('<div style="width:'+V+";height:"+W+";overflow: "+(H.scrolling=="auto"?"auto":(H.scrolling=="yes"?"scroll":"hidden"))+';position:relative;"></div>');H.width=L.width();H.height=L.height();E()},F=function(){H.width=o.width;H.height=o.height;B("<img />").attr({id:"fancybox-img",src:o.src,alt:H.title}).appendTo(L);E()},E=function(){var W,V;T.hide();if(M.is(":visible")&&false===G.onCleanup(y,e,G)){B.event.trigger("fancybox-cancel");P=false;return}P=true;B(m.add(Q)).unbind();B(window).unbind("resize.fb scroll.fb");B(document).unbind("keydown.fb");if(M.is(":visible")&&G.titlePosition!=="outside"){M.css("height",M.height())}y=j;e=C;G=H;if(G.overlayShow){Q.css({"background-color":G.overlayColor,opacity:G.overlayOpacity,cursor:G.hideOnOverlayClick?"pointer":"auto",height:B(document).height()});if(!Q.is(":visible")){if(S){B("select:not(#fancybox-tmp select)").filter(function(){return this.style.visibility!=="hidden"}).css({visibility:"hidden"}).one("fancybox-cleanup",function(){this.style.visibility="inherit"})}Q.show()}}else{Q.hide()}c=R();l();if(M.is(":visible")){B(J.add(O).add(z)).hide();W=M.position(),b={top:W.top,left:W.left,width:M.width(),height:M.height()};V=(b.width==c.width&&b.height==c.height);m.fadeTo(G.changeFade,0.3,function(){var X=function(){m.html(L.contents()).fadeTo(G.changeFade,1,v)};B.event.trigger("fancybox-change");m.empty().removeAttr("filter").css({"border-width":G.padding,width:c.width-G.padding*2,height:H.autoDimensions?"auto":c.height-h-G.padding*2});if(V){X()}else{s.prop=0;B(s).animate({prop:1},{duration:G.changeSpeed,easing:G.easingChange,step:U,complete:X})}});return}M.removeAttr("style");m.css("border-width",G.padding);if(G.transitionIn=="elastic"){b=I();m.html(L.contents());M.show();if(G.opacity){c.opacity=0}s.prop=0;B(s).animate({prop:1},{duration:G.speedIn,easing:G.easingIn,step:U,complete:v});return}if(G.titlePosition=="inside"&&h>0){A.show()}m.css({width:c.width-G.padding*2,height:H.autoDimensions?"auto":c.height-h-G.padding*2}).html(L.contents());M.css(c).fadeIn(G.transitionIn=="none"?0:G.speedIn,v)},D=function(V){if(V&&V.length){if(G.titlePosition=="float"){return'<table id="fancybox-title-float-wrap" cellpadding="0" cellspacing="0"><tr><td id="fancybox-title-float-left"></td><td id="fancybox-title-float-main">'+V+'</td><td id="fancybox-title-float-right"></td></tr></table>'}return'<div id="fancybox-title-'+G.titlePosition+'">'+V+"</div>"}return false},l=function(){t=G.title||"";h=0;A.empty().removeAttr("style").removeClass();if(G.titleShow===false){A.hide();return}t=B.isFunction(G.titleFormat)?G.titleFormat(t,y,e,G):D(t);if(!t||t===""){A.hide();return}A.addClass("fancybox-title-"+G.titlePosition).html(t).appendTo("body").show();switch(G.titlePosition){case"inside":A.css({width:c.width-(G.padding*2),marginLeft:G.padding,marginRight:G.padding});h=A.outerHeight(true);A.appendTo(d);c.height+=h;break;case"over":A.css({marginLeft:G.padding,width:c.width-(G.padding*2),bottom:G.padding}).appendTo(d);break;case"float":A.css("left",parseInt((A.width()-c.width-40)/2,10)*-1).appendTo(M);break;default:A.css({width:c.width-(G.padding*2),paddingLeft:G.padding,paddingRight:G.padding}).appendTo(M);break}A.hide()},g=function(){if(G.enableEscapeButton||G.enableKeyboardNav){B(document).bind("keydown.fb",function(V){if(V.keyCode==27&&G.enableEscapeButton){V.preventDefault();B.fancybox.close()}else{if((V.keyCode==37||V.keyCode==39)&&G.enableKeyboardNav&&V.target.tagName!=="INPUT"&&V.target.tagName!=="TEXTAREA"&&V.target.tagName!=="SELECT"){V.preventDefault();B.fancybox[V.keyCode==37?"prev":"next"]()}}})}if(!G.showNavArrows){O.hide();z.hide();return}if((G.cyclic&&y.length>1)||e!==0){O.show()}if((G.cyclic&&y.length>1)||e!=(y.length-1)){z.show()}},v=function(){if(!B.support.opacity){m.get(0).style.removeAttribute("filter");M.get(0).style.removeAttribute("filter")}if(H.autoDimensions){m.css("height","auto")}M.css("height","auto");if(t&&t.length){A.show()}if(G.showCloseButton){J.show()}g();if(G.hideOnContentClick){m.bind("click",B.fancybox.close)}if(G.hideOnOverlayClick){Q.bind("click",B.fancybox.close)}B(window).bind("resize.fb",B.fancybox.resize);if(G.centerOnScroll){B(window).bind("scroll.fb",B.fancybox.center)}if(G.type=="iframe"){B('<iframe id="fancybox-frame" name="fancybox-frame'+new Date().getTime()+'" frameborder="0" hspace="0" '+(B.browser.msie?'allowtransparency="true""':"")+' scrolling="'+H.scrolling+'" src="'+G.href+'"></iframe>').appendTo(m)}M.show();P=false;B.fancybox.center();G.onComplete(y,e,G);K()},K=function(){var V,W;if((y.length-1)>e){V=y[e+1].href;if(typeof V!=="undefined"&&V.match(i)){W=new Image();W.src=V}}if(e>0){V=y[e-1].href;if(typeof V!=="undefined"&&V.match(i)){W=new Image();W.src=V}}},U=function(W){var V={width:parseInt(b.width+(c.width-b.width)*W,10),height:parseInt(b.height+(c.height-b.height)*W,10),top:parseInt(b.top+(c.top-b.top)*W,10),left:parseInt(b.left+(c.left-b.left)*W,10)};if(typeof c.opacity!=="undefined"){V.opacity=W<0.5?0.5:W}M.css(V);m.css({width:V.width-G.padding*2,height:V.height-(h*W)-G.padding*2})},u=function(){return[B(window).width()-(G.margin*2),B(window).height()-(G.margin*2),B(document).scrollLeft()+G.margin,B(document).scrollTop()+G.margin]},R=function(){var V=u(),Z={},W=G.autoScale,X=G.padding*2,Y;if(G.width.toString().indexOf("%")>-1){Z.width=parseInt((V[0]*parseFloat(G.width))/100,10)}else{Z.width=G.width+X}if(G.height.toString().indexOf("%")>-1){Z.height=parseInt((V[1]*parseFloat(G.height))/100,10)}else{Z.height=G.height+X}if(W&&(Z.width>V[0]||Z.height>V[1])){if(H.type=="image"||H.type=="swf"){Y=(G.width)/(G.height);if((Z.width)>V[0]){Z.width=V[0];Z.height=parseInt(((Z.width-X)/Y)+X,10)}if((Z.height)>V[1]){Z.height=V[1];Z.width=parseInt(((Z.height-X)*Y)+X,10)}}else{Z.width=Math.min(Z.width,V[0]);Z.height=Math.min(Z.height,V[1])}}Z.top=parseInt(Math.max(V[3]-20,V[3]+((V[1]-Z.height-40)*0.5)),10);Z.left=parseInt(Math.max(V[2]-20,V[2]+((V[0]-Z.width-40)*0.5)),10);return Z},q=function(V){var W=V.offset();W.top+=parseInt(V.css("paddingTop"),10)||0;W.left+=parseInt(V.css("paddingLeft"),10)||0;W.top+=parseInt(V.css("border-top-width"),10)||0;W.left+=parseInt(V.css("border-left-width"),10)||0;W.width=V.width();W.height=V.height();return W},I=function(){var Y=H.orig?B(H.orig):false,X={},W,V;if(Y&&Y.length){W=q(Y);X={width:W.width+(G.padding*2),height:W.height+(G.padding*2),top:W.top-G.padding-20,left:W.left-G.padding-20}}else{V=u();X={width:G.padding*2,height:G.padding*2,top:parseInt(V[3]+V[1]*0.5,10),left:parseInt(V[2]+V[0]*0.5,10)}}return X},a=function(){if(!T.is(":visible")){clearInterval(p);return}B("div",T).css("top",(N*-40)+"px");N=(N+1)%12};B.fn.fancybox=function(V){if(!B(this).length){return this}B(this).data("fancybox",B.extend({},V,(B.metadata?B(this).metadata():{}))).unbind("click.fb").bind("click.fb",function(X){X.preventDefault();if(P){return}P=true;B(this).blur();j=[];C=0;var W=B(this).attr("rel")||"";if(!W||W==""||W==="nofollow"){j.push(this)}else{j=B("a[rel="+W+"], area[rel="+W+"]");C=j.index(this)}w();return});return this};B.fancybox=function(Y){var X;if(P){return}P=true;X=typeof arguments[1]!=="undefined"?arguments[1]:{};j=[];C=parseInt(X.index,10)||0;if(B.isArray(Y)){for(var W=0,V=Y.length;W<V;W++){if(typeof Y[W]=="object"){B(Y[W]).data("fancybox",B.extend({},X,Y[W]))}else{Y[W]=B({}).data("fancybox",B.extend({content:Y[W]},X))}}j=jQuery.merge(j,Y)}else{if(typeof Y=="object"){B(Y).data("fancybox",B.extend({},X,Y))}else{Y=B({}).data("fancybox",B.extend({content:Y},X))}j.push(Y)}if(C>j.length||C<0){C=0}w()};B.fancybox.showActivity=function(){clearInterval(p);T.show();p=setInterval(a,66)};B.fancybox.hideActivity=function(){T.hide()};B.fancybox.next=function(){return B.fancybox.pos(e+1)};B.fancybox.prev=function(){return B.fancybox.pos(e-1)};B.fancybox.pos=function(V){if(P){return}V=parseInt(V);j=y;if(V>-1&&V<y.length){C=V;w()}else{if(G.cyclic&&y.length>1){C=V>=y.length?0:y.length-1;w()}}return};B.fancybox.cancel=function(){if(P){return}P=true;B.event.trigger("fancybox-cancel");r();H.onCancel(j,C,H);P=false};B.fancybox.close=function(){if(P||M.is(":hidden")){return}P=true;if(G&&false===G.onCleanup(y,e,G)){P=false;return}r();B(J.add(O).add(z)).hide();B(m.add(Q)).unbind();B(window).unbind("resize.fb scroll.fb");B(document).unbind("keydown.fb");m.find("iframe").attr("src",S&&/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank");if(G.titlePosition!=="inside"){A.empty()}M.stop();function V(){Q.fadeOut("fast");A.empty().hide();M.hide();B.event.trigger("fancybox-cleanup");m.empty();G.onClosed(y,e,G);y=H=[];e=C=0;G=H={};P=false}if(G.transitionOut=="elastic"){b=I();var W=M.position();c={top:W.top,left:W.left,width:M.width(),height:M.height()};if(G.opacity){c.opacity=1}A.empty().hide();s.prop=1;B(s).animate({prop:0},{duration:G.speedOut,easing:G.easingOut,step:U,complete:V})}else{M.fadeOut(G.transitionOut=="none"?0:G.speedOut,V)}};B.fancybox.resize=function(){if(Q.is(":visible")){Q.css("height",B(document).height())}B.fancybox.center(true)};B.fancybox.center=function(){var V,W;if(P){return}W=arguments[0]===true?1:0;V=u();if(!W&&(M.width()>V[0]||M.height()>V[1])){return}M.stop().animate({top:parseInt(Math.max(V[3]-20,V[3]+((V[1]-m.height()-40)*0.5)-G.padding)),left:parseInt(Math.max(V[2]-20,V[2]+((V[0]-m.width()-40)*0.5)-G.padding))},typeof arguments[0]=="number"?arguments[0]:200)};B.fancybox.init=function(){if(B("#fancybox-wrap").length){return}B("body").append(L=B('<div id="fancybox-tmp"></div>'),T=B('<div id="fancybox-loading"><div></div></div>'),Q=B('<div id="fancybox-overlay"></div>'),M=B('<div id="fancybox-wrap"></div>'));d=B('<div id="fancybox-outer"></div>').append('<div class="fancybox-bg" id="fancybox-bg-n"></div><div class="fancybox-bg" id="fancybox-bg-ne"></div><div class="fancybox-bg" id="fancybox-bg-e"></div><div class="fancybox-bg" id="fancybox-bg-se"></div><div class="fancybox-bg" id="fancybox-bg-s"></div><div class="fancybox-bg" id="fancybox-bg-sw"></div><div class="fancybox-bg" id="fancybox-bg-w"></div><div class="fancybox-bg" id="fancybox-bg-nw"></div>').appendTo(M);d.append(m=B('<div id="fancybox-content"></div>'),J=B('<a id="fancybox-close"></a>'),A=B('<div id="fancybox-title"></div>'),O=B('<a href="javascript:;" id="fancybox-left"><span class="fancy-ico" id="fancybox-left-ico"></span></a>'),z=B('<a href="javascript:;" id="fancybox-right"><span class="fancy-ico" id="fancybox-right-ico"></span></a>'));J.click(B.fancybox.close);T.click(B.fancybox.cancel);O.click(function(V){V.preventDefault();B.fancybox.prev()});z.click(function(V){V.preventDefault();B.fancybox.next()});if(B.fn.mousewheel){M.bind("mousewheel.fb",function(V,W){if(P){V.preventDefault()}else{if(B(V.target).get(0).clientHeight==0||B(V.target).get(0).scrollHeight===B(V.target).get(0).clientHeight){V.preventDefault();B.fancybox[W>0?"prev":"next"]()}}})}if(!B.support.opacity){M.addClass("fancybox-ie")}if(S){T.addClass("fancybox-ie6");M.addClass("fancybox-ie6");B('<iframe id="fancybox-hide-sel-frame" src="'+(/^https/i.test(window.location.href||"")?"javascript:void(false)":"about:blank")+'" scrolling="no" border="0" frameborder="0" tabindex="-1"></iframe>').prependTo(d)}};B.fn.fancybox.defaults={padding:10,margin:40,opacity:false,modal:false,cyclic:false,scrolling:"auto",width:560,height:340,autoScale:true,autoDimensions:true,centerOnScroll:false,ajax:{},swf:{wmode:"transparent"},hideOnOverlayClick:true,hideOnContentClick:false,overlayShow:true,overlayOpacity:0.7,overlayColor:"#777",titleShow:true,titlePosition:"float",titleFormat:null,titleFromAlt:false,transitionIn:"fade",transitionOut:"fade",speedIn:300,speedOut:300,changeSpeed:300,changeFade:"fast",easingIn:"swing",easingOut:"swing",showCloseButton:true,showNavArrows:true,enableEscapeButton:true,enableKeyboardNav:true,onStart:function(){},onCancel:function(){},onComplete:function(){},onCleanup:function(){},onClosed:function(){},onError:function(){}};B(document).ready(function(){B.fancybox.init()})})(jQuery);
\ No newline at end of file diff --git a/apps/files_sharing/sharedstorage.php b/apps/files_sharing/sharedstorage.php index 32fd2124429..fc0d272b54e 100644 --- a/apps/files_sharing/sharedstorage.php +++ b/apps/files_sharing/sharedstorage.php @@ -410,7 +410,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } } - public function hash($type, $path, $raw) { + public function hash($type, $path, $raw = false) { $source = $this->getSource($path); if ($source) { $storage = OC_Filesystem::getStorage($source); diff --git a/apps/gallery/l10n/de.php b/apps/gallery/l10n/de.php index 6c3d9fc7389..cd580cf303c 100644 --- a/apps/gallery/l10n/de.php +++ b/apps/gallery/l10n/de.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Bilder", -"Settings" => "Einstellungen", -"Rescan" => "Erneut Scannen", -"Stop" => "Stopp", -"Share" => "Teilen", +"Share gallery" => "Galerie teilen", +"Error: " => "Fehler:", +"Internal error" => "Interner Fehler", +"Slideshow" => "Slideshow", "Back" => "Zurück", "Remove confirmation" => "Bestätigung entfernen", "Do you want to remove album" => "Soll das Album entfernt werden", diff --git a/apps/gallery/l10n/es.php b/apps/gallery/l10n/es.php index 03e8d6a4563..aa425a0bd04 100644 --- a/apps/gallery/l10n/es.php +++ b/apps/gallery/l10n/es.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Imágenes", -"Settings" => "Preferencias", -"Rescan" => "Refrescar", -"Stop" => "Parar", -"Share" => "Compartir", +"Share gallery" => "Compartir galería", +"Error: " => "Fallo ", +"Internal error" => "Fallo interno", +"Slideshow" => "Presentación", "Back" => "Atrás", "Remove confirmation" => "Borrar confirmación", "Do you want to remove album" => "¿Quieres eliminar el álbum", diff --git a/apps/gallery/l10n/it.php b/apps/gallery/l10n/it.php index e21a1d6524b..ef8d596e7eb 100644 --- a/apps/gallery/l10n/it.php +++ b/apps/gallery/l10n/it.php @@ -1,9 +1,9 @@ <?php $TRANSLATIONS = array( "Pictures" => "Immagini", -"Settings" => "Impostazioni", -"Rescan" => "Nuova scansione", -"Stop" => "Ferma", -"Share" => "Condividi", +"Share gallery" => "Condividi la galleria", +"Error: " => "Errore: ", +"Internal error" => "Errore interno", +"Slideshow" => "Presentazione", "Back" => "Indietro", "Remove confirmation" => "Rimuovi conferma", "Do you want to remove album" => "Vuoi rimuovere l'album", diff --git a/apps/gallery/l10n/vi.php b/apps/gallery/l10n/vi.php new file mode 100644 index 00000000000..d1d7fc64fca --- /dev/null +++ b/apps/gallery/l10n/vi.php @@ -0,0 +1,11 @@ +<?php $TRANSLATIONS = array( +"Pictures" => "Hình ảnh", +"Share gallery" => "Chia sẻ gallery", +"Error: " => "Lỗi :", +"Internal error" => "Lỗi nội bộ", +"Back" => "Trở lại", +"Remove confirmation" => "Xóa xác nhận", +"Do you want to remove album" => "Bạn muốn xóa album này ", +"Change album name" => "Đổi tên album", +"New album name" => "Tên album mới" +); diff --git a/apps/media/l10n/vi.php b/apps/media/l10n/vi.php new file mode 100644 index 00000000000..01942ba173f --- /dev/null +++ b/apps/media/l10n/vi.php @@ -0,0 +1,14 @@ +<?php $TRANSLATIONS = array( +"Music" => "Âm nhạc", +"Add album to playlist" => "Thêm album vào playlist", +"Play" => "Play", +"Pause" => "Tạm dừng", +"Previous" => "Trang trước", +"Next" => "Tiếp theo", +"Mute" => "Tắt", +"Unmute" => "Bật", +"Rescan Collection" => "Quét lại bộ sưu tập", +"Artist" => "Nghệ sỹ", +"Album" => "Album", +"Title" => "Tiêu đề" +); diff --git a/apps/user_ldap/appinfo/app.php b/apps/user_ldap/appinfo/app.php index 330574c1d42..3c6da47d71a 100644 --- a/apps/user_ldap/appinfo/app.php +++ b/apps/user_ldap/appinfo/app.php @@ -21,15 +21,17 @@ * */ -require_once('apps/user_ldap/lib_ldap.php'); -require_once('apps/user_ldap/user_ldap.php'); -require_once('apps/user_ldap/group_ldap.php'); +OCP\App::registerAdmin('user_ldap', 'settings'); -OCP\App::registerAdmin('user_ldap','settings'); +$connector = new OCA\user_ldap\lib\Connection('user_ldap'); +$userBackend = new OCA\user_ldap\USER_LDAP(); +$userBackend->setConnector($connector); +$groupBackend = new OCA\user_ldap\GROUP_LDAP(); +$groupBackend->setConnector($connector); // register user backend -OC_User::useBackend( 'LDAP' ); -OC_Group::useBackend( new OC_GROUP_LDAP() ); +OC_User::useBackend($userBackend); +OC_Group::useBackend($groupBackend); // add settings page to navigation $entry = array( diff --git a/apps/user_ldap/group_ldap.php b/apps/user_ldap/group_ldap.php index d438c7d84df..b9f4bdf1990 100644 --- a/apps/user_ldap/group_ldap.php +++ b/apps/user_ldap/group_ldap.php @@ -21,24 +21,22 @@ * */ -class OC_GROUP_LDAP extends OC_Group_Backend { -// //group specific settings - protected $ldapGroupFilter; - protected $ldapGroupMemberAssocAttr; - protected $configured = false; +namespace OCA\user_ldap; + +class GROUP_LDAP extends lib\Access implements \OCP\GroupInterface { + protected $enabled = false; protected $_group_user = array(); protected $_user_groups = array(); protected $_group_users = array(); protected $_groups = array(); - public function __construct() { - $this->ldapGroupFilter = OCP\Config::getAppValue('user_ldap', 'ldap_group_filter', '(objectClass=posixGroup)'); - $this->ldapGroupMemberAssocAttr = OCP\Config::getAppValue('user_ldap', 'ldap_group_member_assoc_attribute', 'uniqueMember'); - - if(!empty($this->ldapGroupFilter) && !empty($this->ldapGroupMemberAssocAttr)) { - $this->configured = true; + public function setConnector(lib\Connection &$connection) { + parent::setConnector($connection); + if(empty($this->connection->ldapGroupFilter) || empty($this->connection->ldapGroupMemberAssocAttr)) { + $this->enabled = false; } + $this->enabled = true; } /** @@ -50,31 +48,31 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * Checks whether the user is member of a group or not. */ public function inGroup($uid, $gid) { - if(!$this->configured) { + if(!$this->enabled) { return false; } if(isset($this->_group_user[$gid][$uid])) { return $this->_group_user[$gid][$uid]; } - $dn_user = OC_LDAP::username2dn($uid); - $dn_group = OC_LDAP::groupname2dn($gid); + $dn_user = $this->username2dn($uid); + $dn_group = $this->groupname2dn($gid); // just in case if(!$dn_group || !$dn_user) { return false; } //usually, LDAP attributes are said to be case insensitive. But there are exceptions of course. - $members = OC_LDAP::readAttribute($dn_group, $this->ldapGroupMemberAssocAttr); + $members = $this->readAttribute($dn_group, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { return false; } //extra work if we don't get back user DNs //TODO: this can be done with one LDAP query - if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') { + if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { $dns = array(); foreach($members as $mid) { - $filter = str_replace('%uid', $mid, OC_LDAP::conf('ldapLoginFilter')); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = str_replace('%uid', $mid, $this->connection->ldapLoginFilter); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { continue; } @@ -96,36 +94,37 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * if the user exists at all. */ public function getUserGroups($uid) { - if(!$this->configured) { + if(!$this->enabled) { return array(); } if(isset($this->_user_groups[$uid])) { return $this->_user_groups[$uid]; } - $userDN = OC_LDAP::username2dn($uid); + $userDN = $this->username2dn($uid); if(!$userDN) { $this->_user_groups[$uid] = array(); return array(); } //uniqueMember takes DN, memberuid the uid, so we need to distinguish - if((strtolower($this->ldapGroupMemberAssocAttr) == 'uniquemember') - || (strtolower($this->ldapGroupMemberAssocAttr) == 'member')) { + if((strtolower($this->connection->ldapGroupMemberAssocAttr) == 'uniquemember') + || (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'member') + ) { $uid = $userDN; - } else if(strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid') { - $result = OC_LDAP::readAttribute($userDN, 'uid'); + } else if(strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid') { + $result = $this->readAttribute($userDN, 'uid'); $uid = $result[0]; } else { // just in case $uid = $userDN; } - $filter = OC_LDAP::combineFilterWithAnd(array( - $this->ldapGroupFilter, - $this->ldapGroupMemberAssocAttr.'='.$uid + $filter = $this->combineFilterWithAnd(array( + $this->connection->ldapGroupFilter, + $this->connection->ldapGroupMemberAssocAttr.'='.$uid )); - $groups = OC_LDAP::fetchListOfGroups($filter, array(OC_LDAP::conf('ldapGroupDisplayName'),'dn')); - $this->_user_groups[$uid] = array_unique(OC_LDAP::ownCloudGroupNames($groups), SORT_LOCALE_STRING); + $groups = $this->fetchListOfGroups($filter, array($this->connection->ldapGroupDisplayName,'dn')); + $this->_user_groups[$uid] = array_unique($this->ownCloudGroupNames($groups), SORT_LOCALE_STRING); return $this->_user_groups[$uid]; } @@ -135,44 +134,44 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * @returns array with user ids */ public function usersInGroup($gid) { - if(!$this->configured) { + if(!$this->enabled) { return array(); } if(isset($this->_group_users[$gid])) { return $this->_group_users[$gid]; } - $groupDN = OC_LDAP::groupname2dn($gid); + $groupDN = $this->groupname2dn($gid); if(!$groupDN) { $this->_group_users[$gid] = array(); return array(); } - $members = OC_LDAP::readAttribute($groupDN, $this->ldapGroupMemberAssocAttr); + $members = $this->readAttribute($groupDN, $this->connection->ldapGroupMemberAssocAttr); if(!$members) { $this->_group_users[$gid] = array(); return array(); } $result = array(); - $isMemberUid = (strtolower($this->ldapGroupMemberAssocAttr) == 'memberuid'); + $isMemberUid = (strtolower($this->connection->ldapGroupMemberAssocAttr) == 'memberuid'); foreach($members as $member) { if($isMemberUid) { - $filter = OCP\Util::mb_str_replace('%uid', $member, OC_LDAP::conf('ldapLoginFilter'), 'UTF-8'); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = \OCP\Util::mb_str_replace('%uid', $member, $this->connection->ldapLoginFilter, 'UTF-8'); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { continue; } - $result[] = OC_LDAP::dn2username($ldap_users[0]); + $result[] = $this->dn2username($ldap_users[0]); continue; } else { - if($ocname = OC_LDAP::dn2username($member)){ + if($ocname = $this->dn2username($member)) { $result[] = $ocname; } } } if(!$isMemberUid) { - $result = array_intersect($result, OCP\User::getUsers()); + $result = array_intersect($result, \OCP\User::getUsers()); } $this->_group_users[$gid] = array_unique($result, SORT_LOCALE_STRING); return $this->_group_users[$gid]; @@ -185,12 +184,12 @@ class OC_GROUP_LDAP extends OC_Group_Backend { * Returns a list with all groups */ public function getGroups() { - if(!$this->configured) { + if(!$this->enabled) { return array(); } if(empty($this->_groups)) { - $ldap_groups = OC_LDAP::fetchListOfGroups($this->ldapGroupFilter, array(OC_LDAP::conf('ldapGroupDisplayName'), 'dn')); - $this->_groups = OC_LDAP::ownCloudGroupNames($ldap_groups); + $ldap_groups = $this->fetchListOfGroups($this->connection->ldapGroupFilter, array($this->connection->ldapGroupDisplayName, 'dn')); + $this->_groups = $this->ownCloudGroupNames($ldap_groups); } return $this->_groups; } @@ -203,4 +202,17 @@ class OC_GROUP_LDAP extends OC_Group_Backend { public function groupExists($gid){ return in_array($gid, $this->getGroups()); } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + //always returns false, because possible actions are modifying actions. We do not write to LDAP, at least for now. + return false; + } }
\ No newline at end of file diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php new file mode 100644 index 00000000000..19122b34c7d --- /dev/null +++ b/apps/user_ldap/lib/access.php @@ -0,0 +1,597 @@ +<?php + +/** + * ownCloud – LDAP Access + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\user_ldap\lib; + +abstract class Access { + protected $connection; + + public function setConnector(Connection &$connection) { + $this->connection = $connection; + } + + private function checkConnection() { + return ($this->connection instanceof Connection); + } + + /** + * @brief reads a given attribute for an LDAP record identified by a DN + * @param $dn the record in question + * @param $attr the attribute that shall be retrieved + * @returns the values in an array on success, false otherwise + * + * Reads an attribute from an LDAP entry + */ + public function readAttribute($dn, $attr) { + if(!$this->checkConnection()) { + \OCP\Util::writeLog('user_ldap', 'No LDAP Connector assigned, access impossible for readAttribute.', \OCP\Util::WARN); + return false; + } + $cr = $this->connection->getConnectionResource(); + if(!is_resource($cr)) { + //LDAP not available + return false; + } + $rr = @ldap_read($cr, $dn, 'objectClass=*', array($attr)); + if(!is_resource($rr)) { + \OCP\Util::writeLog('user_ldap', 'readAttribute failed for DN '.$dn, \OCP\Util::DEBUG); + //in case an error occurs , e.g. object does not exist + return false; + } + $er = ldap_first_entry($cr, $rr); + //LDAP attributes are not case sensitive + $result = \OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); + $attr = mb_strtolower($attr, 'UTF-8'); + + if(isset($result[$attr]) && $result[$attr]['count'] > 0) { + $values = array(); + for($i=0;$i<$result[$attr]['count'];$i++) { + $values[] = $this->resemblesDN($attr) ? $this->sanitizeDN($result[$attr][$i]) : $result[$attr][$i]; + } + return $values; + } + return false; + } + + /** + * @brief checks wether the given attribute`s valua is probably a DN + * @param $attr the attribute in question + * @return if so true, otherwise false + */ + private function resemblesDN($attr) { + $resemblingAttributes = array( + 'dn', + 'uniquemember', + 'member' + ); + return in_array($attr, $resemblingAttributes); + } + + /** + * @brief sanitizes a DN received from the LDAP server + * @param $dn the DN in question + * @return the sanitized DN + */ + private function sanitizeDN($dn) { + //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this! + $dn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); + + //make comparisons and everything work + $dn = mb_strtolower($dn, 'UTF-8'); + + return $dn; + } + + /** + * gives back the database table for the query + */ + private function getMapTable($isUser) { + if($isUser) { + return '*PREFIX*ldap_user_mapping'; + } else { + return '*PREFIX*ldap_group_mapping'; + } + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name of the group + * @param $name the ownCloud name in question + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name of the group + */ + public function groupname2dn($name) { + return $this->ocname2dn($name, false); + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name of the user + * @param $name the ownCloud name in question + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name of the user + */ + public function username2dn($name) { + $dn = $this->ocname2dn($name, true); + if($dn) { + return $dn; + } else { + //fallback: user is not mapped + $filter = $this->combineFilterWithAnd(array( + $this->connection->ldapUserFilter, + $this->connection->ldapUserDisplayName . '=' . $name, + )); + $result = $this->searchUsers($filter, 'dn'); + if(isset($result[0]['dn'])) { + $this->mapComponent($result[0], $name, true); + return $result[0]; + } + } + + return false; + } + + /** + * @brief returns the LDAP DN for the given internal ownCloud name + * @param $name the ownCloud name in question + * @param $isUser is it a user? otherwise group + * @returns string with the LDAP DN on success, otherwise false + * + * returns the LDAP DN for the given internal ownCloud name + */ + private function ocname2dn($name, $isUser) { + $table = $this->getMapTable($isUser); + + $query = \OCP\DB::prepare(' + SELECT ldap_dn + FROM '.$table.' + WHERE owncloud_name = ? + '); + + $record = $query->execute(array($name))->fetchOne(); + return $record; + } + + /** + * @brief returns the internal ownCloud name for the given LDAP DN of the group + * @param $dn the dn of the group object + * @param $ldapname optional, the display name of the object + * @returns string with with the name to use in ownCloud, false on DN outside of search DN + * + * returns the internal ownCloud name for the given LDAP DN of the group + */ + public function dn2groupname($dn, $ldapname = null) { + if(mb_strripos($dn, $this->connection->ldapBaseGroups, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->connection->ldapBaseGroups, 'UTF-8'))) { + return false; + } + return $this->dn2ocname($dn, $ldapname, false); + } + + /** + * @brief returns the internal ownCloud name for the given LDAP DN of the user + * @param $dn the dn of the user object + * @param $ldapname optional, the display name of the object + * @returns string with with the name to use in ownCloud + * + * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN + */ + public function dn2username($dn, $ldapname = null) { + if(mb_strripos($dn, $this->connection->ldapBaseUsers, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen($this->connection->ldapBaseUsers, 'UTF-8'))) { + return false; + } + return $this->dn2ocname($dn, $ldapname, true); + } + + /** + * @brief returns an internal ownCloud name for the given LDAP DN + * @param $dn the dn of the user object + * @param $ldapname optional, the display name of the object + * @param $isUser optional, wether it is a user object (otherwise group assumed) + * @returns string with with the name to use in ownCloud + * + * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN + */ + public function dn2ocname($dn, $ldapname = null, $isUser = true) { + $dn = $this->sanitizeDN($dn); + $table = $this->getMapTable($isUser); + if($isUser) { + $nameAttribute = $this->connection->ldapUserDisplayName; + } else { + $nameAttribute = $this->connection->ldapGroupDisplayName; + } + + $query = \OCP\DB::prepare(' + SELECT owncloud_name + FROM '.$table.' + WHERE ldap_dn = ? + '); + + $component = $query->execute(array($dn))->fetchOne(); + if($component) { + return $component; + } + + if(is_null($ldapname)) { + $ldapname = $this->readAttribute($dn, $nameAttribute); + $ldapname = $ldapname[0]; + } + $ldapname = $this->sanitizeUsername($ldapname); + + //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot. + if($this->mapComponent($dn, $ldapname, $isUser)) { + return $ldapname; + } + + //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located. + $oc_name = $this->alternateOwnCloudName($ldapname, $dn); + if($this->mapComponent($dn, $oc_name, $isUser)) { + return $oc_name; + } + + //TODO: do not simple die away! + //and this of course should never been thrown :) + throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.'); + } + + /** + * @brief gives back the user names as they are used ownClod internally + * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... ) + * @returns an array with the user names to use in ownCloud + * + * gives back the user names as they are used ownClod internally + */ + public function ownCloudUserNames($ldapUsers) { + return $this->ldap2ownCloudNames($ldapUsers, true); + } + + /** + * @brief gives back the group names as they are used ownClod internally + * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... ) + * @returns an array with the group names to use in ownCloud + * + * gives back the group names as they are used ownClod internally + */ + public function ownCloudGroupNames($ldapGroups) { + return $this->ldap2ownCloudNames($ldapGroups, false); + } + + private function ldap2ownCloudNames($ldapObjects, $isUsers) { + if($isUsers) { + $knownObjects = $this->mappedUsers(); + $nameAttribute = $this->connection->ldapUserDisplayName; + } else { + $knownObjects = $this->mappedGroups(); + $nameAttribute = $this->connection->ldapGroupDisplayName; + } + $ownCloudNames = array(); + + foreach($ldapObjects as $ldapObject) { + $key = \OCP\Util::recursiveArraySearch($knownObjects, $ldapObject['dn']); + + //everything is fine when we know the group + if($key !== false) { + $ownCloudNames[] = $knownObjects[$key]['owncloud_name']; + continue; + } + + //a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters. + $ocname = $this->sanitizeUsername($ldapObject[$nameAttribute]); + if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) { + $ownCloudNames[] = $ocname; + continue; + } + + //doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located. + $ocname = $this->alternateOwnCloudName($ocname, $ldapObject['dn']); + if($this->mapComponent($ldapObject['dn'], $ocname, $isUsers)) { + $ownCloudNames[] = $ocname; + continue; + } + + //TODO: do not simple die away + //and this of course should never been thrown :) + throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.'); + } + return $ownCloudNames; + } + + /** + * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + * @param $name the display name of the object + * @param $dn the dn of the object + * @returns string with with the name to use in ownCloud + * + * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object + */ + private function alternateOwnCloudName($name, $dn) { + $ufn = ldap_dn2ufn($dn); + $name = $name . '@' . trim(\OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8')); + $name = $this->sanitizeUsername($name); + return $name; + } + + /** + * @brief retrieves all known groups from the mappings table + * @returns array with the results + * + * retrieves all known groups from the mappings table + */ + private function mappedGroups() { + return $this->mappedComponents(false); + } + + /** + * @brief retrieves all known users from the mappings table + * @returns array with the results + * + * retrieves all known users from the mappings table + */ + private function mappedUsers() { + return $this->mappedComponents(true); + } + + private function mappedComponents($isUsers) { + $table = $this->getMapTable($isUsers); + + $query = \OCP\DB::prepare(' + SELECT ldap_dn, owncloud_name + FROM '. $table + ); + + return $query->execute()->fetchAll(); + } + + /** + * @brief inserts a new user or group into the mappings table + * @param $dn the record in question + * @param $ocname the name to use in ownCloud + * @param $isUser is it a user or a group? + * @returns true on success, false otherwise + * + * inserts a new user or group into the mappings table + */ + private function mapComponent($dn, $ocname, $isUser = true) { + $table = $this->getMapTable($isUser); + $dn = $this->sanitizeDN($dn); + + $sqlAdjustment = ''; + $dbtype = \OCP\Config::getSystemValue('dbtype'); + if($dbtype == 'mysql') { + $sqlAdjustment = 'FROM dual'; + } + + $insert = \OCP\DB::prepare(' + INSERT INTO '.$table.' (ldap_dn, owncloud_name) + SELECT ?,? + '.$sqlAdjustment.' + WHERE NOT EXISTS ( + SELECT 1 + FROM '.$table.' + WHERE ldap_dn = ? + OR owncloud_name = ? ) + '); + + $res = $insert->execute(array($dn, $ocname, $dn, $ocname)); + + if(\OCP\DB::isError($res)) { + return false; + } + + $insRows = $res->numRows(); + + if($insRows == 0) { + return false; + } + + return true; + } + + public function fetchListOfUsers($filter, $attr) { + return $this->fetchList($this->searchUsers($filter, $attr), (count($attr) > 1)); + } + + public function fetchListOfGroups($filter, $attr) { + return $this->fetchList($this->searchGroups($filter, $attr), (count($attr) > 1)); + } + + private function fetchList($list, $manyAttributes) { + if(is_array($list)) { + if($manyAttributes) { + return $list; + } else { + return array_unique($list, SORT_LOCALE_STRING); + } + } + + //error cause actually, maybe throw an exception in future. + return array(); + } + + /** + * @brief executes an LDAP search, optimized for Users + * @param $filter the LDAP filter for the search + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + public function searchUsers($filter, $attr = null) { + return $this->search($filter, $this->connection->ldapBaseUsers, $attr); + } + + /** + * @brief executes an LDAP search, optimized for Groups + * @param $filter the LDAP filter for the search + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + public function searchGroups($filter, $attr = null) { + return $this->search($filter, $this->connection->ldapBaseGroups, $attr); + } + + /** + * @brief executes an LDAP search + * @param $filter the LDAP filter for the search + * @param $base the LDAP subtree that shall be searched + * @param $attr optional, when a certain attribute shall be filtered out + * @returns array with the search result + * + * Executes an LDAP search + */ + private function search($filter, $base, $attr = null) { + if(!is_null($attr) && !is_array($attr)) { + $attr = array(mb_strtolower($attr, 'UTF-8')); + } + + // See if we have a resource + $link_resource = $this->connection->getConnectionResource(); + if(is_resource($link_resource)) { + $sr = ldap_search($link_resource, $base, $filter, $attr); + $findings = ldap_get_entries($link_resource, $sr ); + + // if we're here, probably no connection resource is returned. + // to make ownCloud behave nicely, we simply give back an empty array. + if(is_null($findings)) { + return array(); + } + } else { + // Seems like we didn't find any resource. + // Return an empty array just like before. + \OCP\Util::writeLog('user_ldap', 'Could not search, because resource is missing.', \OCP\Util::DEBUG); + return array(); + } + + if(!is_null($attr)) { + $selection = array(); + $multiarray = false; + if(count($attr) > 1) { + $multiarray = true; + $i = 0; + } + foreach($findings as $item) { + if(!is_array($item)) { + continue; + } + $item = \OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8'); + + if($multiarray) { + foreach($attr as $key) { + $key = mb_strtolower($key, 'UTF-8'); + if(isset($item[$key])) { + if($key != 'dn') { + $selection[$i][$key] = $this->resemblesDN($key) ? $this->sanitizeDN($item[$key][0]) : $item[$key][0]; + } else { + $selection[$i][$key] = $this->sanitizeDN($item[$key]); + } + } + + } + $i++; + } else { + //tribute to case insensitivity + $key = mb_strtolower($attr[0], 'UTF-8'); + + if(isset($item[$key])) { + if($this->resemblesDN($key)) { + $selection[] = $this->sanitizeDN($item[$key]); + } else { + $selection[] = $item[$key]; + } + } + } + } + return $selection; + } + return $findings; + } + + public function sanitizeUsername($name) { + if($this->connection->ldapIgnoreNamingRules) { + return $name; + } + + //REPLACEMENTS + $name = \OCP\Util::mb_str_replace(' ', '_', $name, 'UTF-8'); + + //every remaining unallowed characters will be removed + $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name); + + return $name; + } + + /** + * @brief combines the input filters with AND + * @param $filters array, the filters to connect + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + public function combineFilterWithAnd($filters) { + return $this->combineFilter($filters, '&'); + } + + /** + * @brief combines the input filters with AND + * @param $filters array, the filters to connect + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + public function combineFilterWithOr($filters) { + return $this->combineFilter($filters, '|'); + } + + /** + * @brief combines the input filters with given operator + * @param $filters array, the filters to connect + * @param $operator either & or | + * @returns the combined filter + * + * Combines Filter arguments with AND + */ + private function combineFilter($filters, $operator) { + $combinedFilter = '('.$operator; + foreach($filters as $filter) { + if($filter[0] != '(') { + $filter = '('.$filter.')'; + } + $combinedFilter.=$filter; + } + $combinedFilter.=')'; + return $combinedFilter; + } + + public function areCredentialsValid($name, $password) { + $testConnection = clone $this->connection; + $credentials = array( + 'ldapAgentName' => $name, + 'ldapAgentPassword' => $password + ); + if(!$testConnection->setConfiguration($credentials)) { + return false; + } + return $testConnection->bind(); + } +}
\ No newline at end of file diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php new file mode 100644 index 00000000000..e54a8e2b241 --- /dev/null +++ b/apps/user_ldap/lib/connection.php @@ -0,0 +1,255 @@ +<?php + +/** + * ownCloud – LDAP Access + * + * @author Arthur Schiwon + * @copyright 2012 Arthur Schiwon blizzz@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\user_ldap\lib; + +class Connection { + private $ldapConnectionRes = null; + private $configID; + private $configured = false; + + //cached settings + protected $config = array( + 'ldapHost' => null, + 'ldapPort' => null, + 'ldapBase' => null, + 'ldapBaseUsers' => null, + 'ldapBaseGroups' => null, + 'ldapAgentName' => null, + 'ldapAgentPassword' => null, + 'ldapTLS' => null, + 'ldapNoCase' => null, + 'ldapIgnoreNamingRules' => null, + 'ldapUserDisplayName' => null, + 'ldapUserFilter' => null, + 'ldapGroupFilter' => null, + 'ldapGroupDisplayName' => null, + 'ldapLoginFilter' => null, + 'ldapQuotaAttribute' => null, + 'ldapQuotaDefault' => null, + 'ldapEmailAttribute' => null, + ); + + public function __construct($configID = 'user_ldap') { + $this->configID = $configID; + } + + public function __destruct() { + @ldap_unbind($this->ldapConnectionRes); + } + + public function __get($name) { + if(!$this->configured) { + $this->readConfiguration(); + } + + if(isset($this->config[$name])) { + return $this->config[$name]; + } + } + + /** + * @brief initializes the LDAP backend + * @param $force read the config settings no matter what + * + * initializes the LDAP backend + */ + public function init($force = false) { + $this->readConfiguration($force); + $this->establishConnection(); + } + + /** + * Returns the LDAP handler + */ + public function getConnectionResource() { + if(!$this->ldapConnectionRes) { + $this->init(); + } + if(is_null($this->ldapConnectionRes)) { + \OCP\Util::writeLog('user_ldap', 'Connection could not be established', \OCP\Util::ERROR); + } + return $this->ldapConnectionRes; + } + + /** + * Caches the general LDAP configuration. + */ + private function readConfiguration($force = false) { + \OCP\Util::writeLog('user_ldap','Checking conf state: isConfigured? '.print_r($this->configured, true).' isForce? '.print_r($force, true).' configID? '.print_r($this->configID, true), \OCP\Util::DEBUG); + if((!$this->configured || $force) && !is_null($this->configID)) { + \OCP\Util::writeLog('user_ldap','Reading the configuration', \OCP\Util::DEBUG); + $this->config['ldapHost'] = \OCP\Config::getAppValue($this->configID, 'ldap_host', ''); + $this->config['ldapPort'] = \OCP\Config::getAppValue($this->configID, 'ldap_port', 389); + $this->config['ldapAgentName'] = \OCP\Config::getAppValue($this->configID, 'ldap_dn',''); + $this->config['ldapAgentPassword'] = base64_decode(\OCP\Config::getAppValue($this->configID, 'ldap_agent_password','')); + $this->config['ldapBase'] = \OCP\Config::getAppValue($this->configID, 'ldap_base', ''); + $this->config['ldapBaseUsers'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_users',$this->config['ldapBase']); + $this->config['ldapBaseGroups'] = \OCP\Config::getAppValue($this->configID, 'ldap_base_groups', $this->config['ldapBase']); + $this->config['ldapTLS'] = \OCP\Config::getAppValue($this->configID, 'ldap_tls',0); + $this->config['ldapNoCase'] = \OCP\Config::getAppValue($this->configID, 'ldap_nocase', 0); + $this->config['ldapUserDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_display_name', 'uid'), 'UTF-8'); + $this->config['ldapUserFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_userlist_filter','objectClass=person'); + $this->config['ldapGroupFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_filter','(objectClass=posixGroup)'); + $this->config['ldapLoginFilter'] = \OCP\Config::getAppValue($this->configID, 'ldap_login_filter', '(uid=%uid)'); + $this->config['ldapGroupDisplayName'] = mb_strtolower(\OCP\Config::getAppValue($this->configID, 'ldap_group_display_name', 'uid'), 'UTF-8'); + $this->config['ldapQuotaAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_attr', ''); + $this->config['ldapQuotaDefault'] = \OCP\Config::getAppValue($this->configID, 'ldap_quota_def', ''); + $this->config['ldapEmailAttribute'] = \OCP\Config::getAppValue($this->configID, 'ldap_email_attr', ''); + $this->config['ldapGroupMemberAssocAttr'] = \OCP\Config::getAppValue($this->configID, 'ldap_group_member_assoc_attribute', 'uniqueMember'); + $this->config['ldapIgnoreNamingRules'] = \OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); + + $this->configured = $this->validateConfiguration(); + } + } + + /** + * @brief set LDAP configuration with values delivered by an array, not read from configuration + * @param $config array that holds the config parameters in an associated array + * @param &$setParameters optional; array where the set fields will be given to + * @return true if config validates, false otherwise. Check with $setParameters for detailed success on single parameters + */ + public function setConfiguration($config, &$setParameters = null) { + if(!is_array($config)) { + return false; + } + + foreach($config as $parameter => $value) { + if(isset($this->config[$parameter])) { + $this->config[$parameter] = $value; + if(is_array($setParameters)) { + $setParameters[] = $parameter; + } + } + } + + $this->configured = $this->validateConfiguration(); + + return $this->configured; + } + + /** + * @brief Validates the user specified configuration + * @returns true if configuration seems OK, false otherwise + */ + private function validateConfiguration() { + //first step: "soft" checks: settings that are not really necessary, but advisable. If left empty, give an info message + if(empty($this->config['ldapBaseUsers'])) { + \OCP\Util::writeLog('user_ldap', 'Base tree for Users is empty, using Base DN', \OCP\Util::INFO); + $this->config['ldapBaseUsers'] = $this->config['ldapBase']; + } + if(empty($this->config['ldapBaseGroups'])) { + \OCP\Util::writeLog('user_ldap', 'Base tree for Groups is empty, using Base DN', \OCP\Util::INFO); + $this->config['ldapBaseGroups'] = $this->config['ldapBase']; + } + if(empty($this->config['ldapGroupFilter']) && empty($this->config['ldapGroupMemberAssocAttr'])) { + \OCP\Util::writeLog('user_ldap', 'No group filter is specified, LDAP group feature will not be used.', \OCP\Util::INFO); + } + + //second step: critical checks. If left empty or filled wrong, set as unconfigured and give a warning. + $configurationOK = true; + if(empty($this->config['ldapHost'])) { + \OCP\Util::writeLog('user_ldap', 'No LDAP host given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapPort'])) { + \OCP\Util::writeLog('user_ldap', 'No LDAP Port given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if((empty($this->config['ldapAgentName']) && !empty($this->config['ldapAgentPassword'])) + || (!empty($this->config['ldapAgentName']) && empty($this->config['ldapAgentPassword']))) { + \OCP\Util::writeLog('user_ldap', 'Either no password given for the user agent or a password is given, but no LDAP agent; won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + //TODO: check if ldapAgentName is in DN form + if(empty($this->config['ldapBase']) && (empty($this->config['ldapBaseUsers']) && empty($this->config['ldapBaseGroups']))) { + \OCP\Util::writeLog('user_ldap', 'No Base DN given, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapUserDisplayName'])) { + \OCP\Util::writeLog('user_ldap', 'No user display name attribute specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapGroupDisplayName'])) { + \OCP\Util::writeLog('user_ldap', 'No group display name attribute specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(empty($this->config['ldapLoginFilter'])) { + \OCP\Util::writeLog('user_ldap', 'No login filter specified, won`t connect.', \OCP\Util::WARN); + $configurationOK = false; + } + if(mb_strpos($this->config['ldapLoginFilter'], '%uid', 0, 'UTF-8') === false) { + \OCP\Util::writeLog('user_ldap', 'Login filter does not contain %uid place holder, won`t connect.', \OCP\Util::WARN); + \OCP\Util::writeLog('user_ldap', 'Login filter was ' . $this->config['ldapLoginFilter'], \OCP\Util::DEBUG); + $configurationOK = false; + } + + return $configurationOK; + } + + /** + * Connects and Binds to LDAP + */ + private function establishConnection() { + static $phpLDAPinstalled = true; + if(!$phpLDAPinstalled) { + return false; + } + if(!$this->configured) { + \OCP\Util::writeLog('user_ldap', 'Configuration is invalid, cannot connect', \OCP\Util::WARN); + return false; + } + if(!$this->ldapConnectionRes) { + if(!function_exists('ldap_connect')) { + $phpLDAPinstalled = false; + \OCP\Util::writeLog('user_ldap', 'function ldap_connect is not available. Make sure that the PHP ldap module is installed.', \OCP\Util::ERROR); + + return false; + } + $this->ldapConnectionRes = ldap_connect($this->config['ldapHost'], $this->config['ldapPort']); + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { + if(ldap_set_option($this->ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { + if($this->config['ldapTLS']) { + ldap_start_tls($this->ldapConnectionRes); + } + } + } + + return $this->bind(); + } + } + + /** + * Binds to LDAP + */ + public function bind() { + $ldapLogin = @ldap_bind($this->getConnectionResource(), $this->config['ldapAgentName'], $this->config['ldapAgentPassword']); + if(!$ldapLogin) { + \OCP\Util::writeLog('user_ldap', 'Bind failed: ' . ldap_errno($this->ldapConnectionRes) . ': ' . ldap_error($this->ldapConnectionRes), \OCP\Util::ERROR); + $this->ldapConnectionRes = null; + return false; + } + return true; + } + +}
\ No newline at end of file diff --git a/apps/user_ldap/lib_ldap.php b/apps/user_ldap/lib_ldap.php deleted file mode 100644 index 08b09304d78..00000000000 --- a/apps/user_ldap/lib_ldap.php +++ /dev/null @@ -1,721 +0,0 @@ -<?php - -/** - * ownCloud – LDAP lib - * - * @author Arthur Schiwon - * @copyright 2012 Arthur Schiwon blizzz@owncloud.com - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -define('LDAP_GROUP_MEMBER_ASSOC_ATTR','uniqueMember'); -define('LDAP_GROUP_DISPLAY_NAME_ATTR','cn'); - -//needed to unbind, because we use OC_LDAP only statically -class OC_LDAP_DESTRUCTOR { - public function __destruct() { - OC_LDAP::destruct(); - } -} - -class OC_LDAP { - static protected $ldapConnectionRes = false; - static protected $configured = false; - - //cached settings - static protected $ldapHost; - static protected $ldapPort; - static protected $ldapBase; - static protected $ldapBaseUsers; - static protected $ldapBaseGroups; - static protected $ldapAgentName; - static protected $ldapAgentPassword; - static protected $ldapTLS; - static protected $ldapNoCase; - static protected $ldapIgnoreNamingRules; - // user and group settings, that are needed in both backends - static protected $ldapUserDisplayName; - static protected $ldapUserFilter; - static protected $ldapGroupDisplayName; - static protected $ldapLoginFilter; - - static protected $__d; - - /** - * @brief initializes the LDAP backend - * @param $force read the config settings no matter what - * - * initializes the LDAP backend - */ - static public function init($force = false) { - if(is_null(self::$__d)) { - self::$__d = new OC_LDAP_DESTRUCTOR(); - } - self::readConfiguration($force); - self::establishConnection(); - } - - static public function destruct() { - @ldap_unbind(self::$ldapConnectionRes); - } - - /** - * @brief returns a read-only configuration value - * @param $key the name of the configuration value - * @returns the value on success, otherwise null - * - * returns a read-only configuration values - * - * we cannot work with getters, because it is a static class - */ - static public function conf($key) { - if(!self::$configured) { - self::init(); - } - - $availableProperties = array( - 'ldapUserDisplayName', - 'ldapGroupDisplayName', - 'ldapLoginFilter' - ); - - if(in_array($key, $availableProperties)) { - return self::$$key; - } - - return null; - } - - /** - * gives back the database table for the query - */ - static private function getMapTable($isUser) { - if($isUser) { - return '*PREFIX*ldap_user_mapping'; - } else { - return '*PREFIX*ldap_group_mapping'; - } - } - - /** - * @brief returns the LDAP DN for the given internal ownCloud name of the group - * @param $name the ownCloud name in question - * @returns string with the LDAP DN on success, otherwise false - * - * returns the LDAP DN for the given internal ownCloud name of the group - */ - static public function groupname2dn($name) { - return self::ocname2dn($name, false); - } - - /** - * @brief returns the LDAP DN for the given internal ownCloud name of the user - * @param $name the ownCloud name in question - * @returns string with the LDAP DN on success, otherwise false - * - * returns the LDAP DN for the given internal ownCloud name of the user - */ - static public function username2dn($name) { - $dn = self::ocname2dn($name, true); - if($dn) { - return $dn; - } else { - //fallback: user is not mapped - self::init(); - $filter = self::combineFilterWithAnd(array( - self::$ldapUserFilter, - self::$ldapUserDisplayName . '=' . $name, - )); - $result = self::searchUsers($filter, 'dn'); - if(isset($result[0]['dn'])) { - self::mapUser($result[0], $name); - return $result[0]; - } - } - - return false; - } - - static private function ocname2dn($name, $isUser) { - $table = self::getMapTable($isUser); - - $query = OCP\DB::prepare(' - SELECT ldap_dn - FROM '.$table.' - WHERE owncloud_name = ? - '); - - $record = $query->execute(array($name))->fetchOne(); - return $record; - } - - /** - * @brief returns the internal ownCloud name for the given LDAP DN of the group - * @param $dn the dn of the group object - * @param $ldapname optional, the display name of the object - * @returns string with with the name to use in ownCloud, false on DN outside of search DN - * - * returns the internal ownCloud name for the given LDAP DN of the group - */ - static public function dn2groupname($dn, $ldapname = null) { - if(mb_strripos($dn, self::$ldapBaseGroups, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen(self::$ldapBaseGroups, 'UTF-8'))) { - return false; - } - return self::dn2ocname($dn, $ldapname, false); - } - - /** - * @brief returns the internal ownCloud name for the given LDAP DN of the user - * @param $dn the dn of the user object - * @param $ldapname optional, the display name of the object - * @returns string with with the name to use in ownCloud - * - * returns the internal ownCloud name for the given LDAP DN of the user, false on DN outside of search DN - */ - static public function dn2username($dn, $ldapname = null) { - if(mb_strripos($dn, self::$ldapBaseUsers, 0, 'UTF-8') !== (mb_strlen($dn, 'UTF-8')-mb_strlen(self::$ldapBaseUsers, 'UTF-8'))) { - return false; - } - return self::dn2ocname($dn, $ldapname, true); - } - - static public function dn2ocname($dn, $ldapname = null, $isUser = true) { - $dn = self::sanitizeDN($dn); - $table = self::getMapTable($isUser); - if($isUser) { - $nameAttribute = self::conf('ldapUserDisplayName'); - } else { - $nameAttribute = self::conf('ldapGroupDisplayName'); - } - - $query = OCP\DB::prepare(' - SELECT owncloud_name - FROM '.$table.' - WHERE ldap_dn = ? - '); - - $component = $query->execute(array($dn))->fetchOne(); - if($component) { - return $component; - } - - if(is_null($ldapname)) { - $ldapname = self::readAttribute($dn, $nameAttribute); - $ldapname = $ldapname[0]; - } - $ldapname = self::sanitizeUsername($ldapname); - - //a new user/group! Then let's try to add it. We're shooting into the blue with the user/group name, assuming that in most cases there will not be a conflict. Otherwise an error will occur and we will continue with our second shot. - if(self::mapComponent($dn, $ldapname, $isUser)) { - return $ldapname; - } - - //doh! There is a conflict. We need to distinguish between users/groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this object is located. - $oc_name = self::alternateOwnCloudName($ldapname, $dn); - if(self::mapComponent($dn, $oc_name, $isUser)) { - return $oc_name; - } - - //and this of course should never been thrown :) - throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.'); - } - - /** - * @brief gives back the user names as they are used ownClod internally - * @param $ldapGroups an array with the ldap Users result in style of array ( array ('dn' => foo, 'uid' => bar), ... ) - * @returns an array with the user names to use in ownCloud - * - * gives back the user names as they are used ownClod internally - */ - static public function ownCloudUserNames($ldapUsers) { - return self::ldap2ownCloudNames($ldapUsers, true); - } - - /** - * @brief gives back the group names as they are used ownClod internally - * @param $ldapGroups an array with the ldap Groups result in style of array ( array ('dn' => foo, 'cn' => bar), ... ) - * @returns an array with the group names to use in ownCloud - * - * gives back the group names as they are used ownClod internally - */ - static public function ownCloudGroupNames($ldapGroups) { - return self::ldap2ownCloudNames($ldapGroups, false); - } - - static private function ldap2ownCloudNames($ldapObjects, $isUsers) { - if($isUsers) { - $knownObjects = self::mappedUsers(); - $nameAttribute = self::conf('ldapUserDisplayName'); - } else { - $knownObjects = self::mappedGroups(); - $nameAttribute = self::conf('ldapGroupDisplayName'); - } - $ownCloudNames = array(); - - foreach($ldapObjects as $ldapObject) { - $key = self::recursiveArraySearch($knownObjects, $ldapObject['dn']); - - //everything is fine when we know the group - if($key !== false) { - $ownCloudNames[] = $knownObjects[$key]['owncloud_name']; - continue; - } - - //a new group! Then let's try to add it. We're shooting into the blue with the group name, assuming that in most cases there will not be a conflict. But first make sure, that the display name contains only allowed characters. - $ocname = self::sanitizeUsername($ldapObject[$nameAttribute]); - if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) { - $ownCloudNames[] = $ocname; - continue; - } - - //doh! There is a conflict. We need to distinguish between groups. Adding indexes is an idea, but not much of a help for the user. The DN is ugly, but for now the only reasonable way. But we transform it to a readable format and remove the first part to only give the path where this entry is located. - $ocname = self::alternateOwnCloudName($ocname, $ldapObject['dn']); - if(self::mapComponent($ldapObject['dn'], $ocname, $isUsers)) { - $ownCloudNames[] = $ocname; - continue; - } - - //and this of course should never been thrown :) - throw new Exception('LDAP backend: unexpected collision of DN and ownCloud Name.'); - } - return $ownCloudNames; - } - - /** - * @brief creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object - * @param $name the display name of the object - * @param $dn the dn of the object - * @returns string with with the name to use in ownCloud - * - * creates a hopefully unique name for owncloud based on the display name and the dn of the LDAP object - */ - static private function alternateOwnCloudName($name, $dn) { - $ufn = ldap_dn2ufn($dn); - $name = $name . '@' . trim(OCP\Util::mb_substr_replace($ufn, '', 0, mb_strpos($ufn, ',', 0, 'UTF-8'), 'UTF-8')); - $name = self::sanitizeUsername($name); - return $name; - } - - /** - * @brief retrieves all known groups from the mappings table - * @returns array with the results - * - * retrieves all known groups from the mappings table - */ - static private function mappedGroups() { - return self::mappedComponents(false); - } - - /** - * @brief retrieves all known users from the mappings table - * @returns array with the results - * - * retrieves all known users from the mappings table - */ - static private function mappedUsers() { - return self::mappedComponents(true); - } - - static private function mappedComponents($isUsers) { - $table = self::getMapTable($isUsers); - - $query = OCP\DB::prepare(' - SELECT ldap_dn, owncloud_name - FROM '. $table - ); - - return $query->execute()->fetchAll(); - } - - /** - * @brief inserts a new user or group into the mappings table - * @param $dn the record in question - * @param $ocname the name to use in ownCloud - * @param $isUser is it a user or a group? - * @returns true on success, false otherwise - * - * inserts a new user or group into the mappings table - */ - static private function mapComponent($dn, $ocname, $isUser = true) { - $table = self::getMapTable($isUser); - $dn = self::sanitizeDN($dn); - - $sqlAdjustment = ''; - $dbtype = OCP\Config::getSystemValue('dbtype'); - if($dbtype == 'mysql') { - $sqlAdjustment = 'FROM dual'; - } - - $insert = OCP\DB::prepare(' - INSERT INTO '.$table.' (ldap_dn, owncloud_name) - SELECT ?,? - '.$sqlAdjustment.' - WHERE NOT EXISTS ( - SELECT 1 - FROM '.$table.' - WHERE ldap_dn = ? - OR owncloud_name = ? ) - '); - - $res = $insert->execute(array($dn, $ocname, $dn, $ocname)); - - if(OCP\DB::isError($res)) { - return false; - } - - $insRows = $res->numRows(); - - if($insRows == 0) { - return false; - } - - return true; - } - - static public function fetchListOfUsers($filter, $attr) { - return self::fetchList(OC_LDAP::searchUsers($filter, $attr), (count($attr) > 1)); - } - - static public function fetchListOfGroups($filter, $attr) { - return self::fetchList(OC_LDAP::searchGroups($filter, $attr), (count($attr) > 1)); - } - - static private function fetchList($list, $manyAttributes) { - if(is_array($list)) { - if($manyAttributes) { - return $list; - } else { - return array_unique($list, SORT_LOCALE_STRING); - } - } - - //error cause actually, maybe throw an exception in future. - return array(); - } - - /** - * @brief reads a given attribute for an LDAP record identified by a DN - * @param $dn the record in question - * @param $attr the attribute that shall be retrieved - * @returns the values in an array on success, false otherwise - * - * Reads an attribute from an LDAP entry - */ - static public function readAttribute($dn, $attr) { - $cr = self::getConnectionResource(); - $rr = ldap_read($cr, $dn, 'objectClass=*', array($attr)); - $er = ldap_first_entry($cr, $rr); - //LDAP attributes are not case sensitive - $result = OCP\Util::mb_array_change_key_case(ldap_get_attributes($cr, $er), MB_CASE_LOWER, 'UTF-8'); - $attr = mb_strtolower($attr, 'UTF-8'); - - if(isset($result[$attr]) && $result[$attr]['count'] > 0){ - $values = array(); - for($i=0;$i<$result[$attr]['count'];$i++) { - $values[] = self::resemblesDN($attr) ? self::sanitizeDN($result[$attr][$i]) : $result[$attr][$i]; - } - return $values; - } - return false; - } - - /** - * @brief executes an LDAP search, optimized for Users - * @param $filter the LDAP filter for the search - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static public function searchUsers($filter, $attr = null) { - self::init(); - return self::search($filter, self::$ldapBaseUsers, $attr); - } - - /** - * @brief executes an LDAP search, optimized for Groups - * @param $filter the LDAP filter for the search - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static public function searchGroups($filter, $attr = null) { - self::init(); - return self::search($filter, self::$ldapBaseGroups, $attr); - } - - /** - * @brief executes an LDAP search - * @param $filter the LDAP filter for the search - * @param $base the LDAP subtree that shall be searched - * @param $attr optional, when a certain attribute shall be filtered out - * @returns array with the search result - * - * Executes an LDAP search - */ - static private function search($filter, $base, $attr = null) { - if(!is_null($attr) && !is_array($attr)) { - $attr = array(mb_strtolower($attr, 'UTF-8')); - } - - // See if we have a resource - $link_resource = self::getConnectionResource(); - if(is_resource($link_resource)) { - $sr = ldap_search($link_resource, $base, $filter, $attr); - $findings = ldap_get_entries($link_resource, $sr ); - - // if we're here, probably no connection resource is returned. - // to make ownCloud behave nicely, we simply give back an empty array. - if(is_null($findings)) { - return array(); - } - } else { - // Seems like we didn't find any resource. - // Return an empty array just like before. - return array(); - } - - if(!is_null($attr)) { - $selection = array(); - $multiarray = false; - if(count($attr) > 1) { - $multiarray = true; - $i = 0; - } - foreach($findings as $item) { - if(!is_array($item)) { - continue; - } - $item = OCP\Util::mb_array_change_key_case($item, MB_CASE_LOWER, 'UTF-8'); - - if($multiarray) { - foreach($attr as $key) { - $key = mb_strtolower($key, 'UTF-8'); - if(isset($item[$key])) { - if($key != 'dn'){ - $selection[$i][$key] = self::resemblesDN($key) ? self::sanitizeDN($item[$key][0]) : $item[$key][0]; - } else { - $selection[$i][$key] = self::sanitizeDN($item[$key]); - } - } - - } - $i++; - } else { - //tribute to case insensitivity - $key = mb_strtolower($attr[0], 'UTF-8'); - - if(isset($item[$key])) { - if(self::resemblesDN($key)) { - $selection[] = self::sanitizeDN($item[$key]); - } else { - $selection[] = $item[$key]; - } - } - } - - } - return $selection; - } - - return $findings; - } - - static private function resemblesDN($attr) { - $resemblingAttributes = array( - 'dn', - 'uniquemember', - 'member' - ); - return in_array($attr, $resemblingAttributes); - } - - static private function sanitizeDN($dn) { - //OID sometimes gives back DNs with whitespace after the comma a la "uid=foo, cn=bar, dn=..." We need to tackle this! - $dn = preg_replace('/([^\\\]),(\s+)/u','\1,',$dn); - - //make comparisons and everything work - $dn = mb_strtolower($dn, 'UTF-8'); - - return $dn; - } - - static private function sanitizeUsername($name) { - if(self::$ldapIgnoreNamingRules) { - return $name; - } - - //REPLACEMENTS - $name = OCP\Util::mb_str_replace(' ', '_', $name, 'UTF-8'); - - //every remaining unallowed characters will be removed - $name = preg_replace('/[^a-zA-Z0-9_.@-]/u', '', $name); - - return $name; - } - - /** - * @brief combines the input filters with AND - * @param $filters array, the filters to connect - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static public function combineFilterWithAnd($filters) { - return self::combineFilter($filters,'&'); - } - - /** - * @brief combines the input filters with AND - * @param $filters array, the filters to connect - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static public function combineFilterWithOr($filters) { - return self::combineFilter($filters,'|'); - } - - /** - * @brief combines the input filters with given operator - * @param $filters array, the filters to connect - * @param $operator either & or | - * @returns the combined filter - * - * Combines Filter arguments with AND - */ - static private function combineFilter($filters, $operator) { - $combinedFilter = '('.$operator; - foreach($filters as $filter) { - if($filter[0] != '(') { - $filter = '('.$filter.')'; - } - $combinedFilter.=$filter; - } - $combinedFilter.=')'; - return $combinedFilter; - } - - /** - * Returns the LDAP handler - */ - static private function getConnectionResource() { - if(!self::$ldapConnectionRes) { - self::init(); - } - if(is_null(self::$ldapConnectionRes)) { - OCP\Util::writeLog('ldap', 'Connection could not be established', OCP\Util::INFO); - } - return self::$ldapConnectionRes; - } - - /** - * Caches the general LDAP configuration. - */ - static private function readConfiguration($force = false) { - if(!self::$configured || $force) { - self::$ldapHost = OCP\Config::getAppValue('user_ldap', 'ldap_host', ''); - self::$ldapPort = OCP\Config::getAppValue('user_ldap', 'ldap_port', 389); - self::$ldapAgentName = OCP\Config::getAppValue('user_ldap', 'ldap_dn',''); - self::$ldapAgentPassword = base64_decode(OCP\Config::getAppValue('user_ldap', 'ldap_agent_password','')); - self::$ldapBase = OCP\Config::getAppValue('user_ldap', 'ldap_base', ''); - self::$ldapBaseUsers = OCP\Config::getAppValue('user_ldap', 'ldap_base_users',self::$ldapBase); - self::$ldapBaseGroups = OCP\Config::getAppValue('user_ldap', 'ldap_base_groups', self::$ldapBase); - self::$ldapTLS = OCP\Config::getAppValue('user_ldap', 'ldap_tls',0); - self::$ldapNoCase = OCP\Config::getAppValue('user_ldap', 'ldap_nocase', 0); - self::$ldapUserDisplayName = mb_strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_display_name', 'uid'), 'UTF-8'); - self::$ldapUserFilter = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter','objectClass=person'); - self::$ldapLoginFilter = OCP\Config::getAppValue('user_ldap', 'ldap_login_filter', '(uid=%uid)'); - self::$ldapGroupDisplayName = mb_strtolower(OCP\Config::getAppValue('user_ldap', 'ldap_group_display_name', LDAP_GROUP_DISPLAY_NAME_ATTR), 'UTF-8'); - self::$ldapIgnoreNamingRules = OCP\Config::getSystemValue('ldapIgnoreNamingRules', false); - - if(empty(self::$ldapBaseUsers)) { - OCP\Util::writeLog('ldap', 'Base for Users is empty, using Base DN', OCP\Util::INFO); - self::$ldapBaseUsers = self::$ldapBase; - } - if(empty(self::$ldapBaseGroups)) { - OCP\Util::writeLog('ldap', 'Base for Groups is empty, using Base DN', OCP\Util::INFO); - self::$ldapBaseGroups = self::$ldapBase; - } - - if( - !empty(self::$ldapHost) - && !empty(self::$ldapPort) - && ( - (!empty(self::$ldapAgentName) && !empty(self::$ldapAgentPassword)) - || ( empty(self::$ldapAgentName) && empty(self::$ldapAgentPassword)) - ) - && !empty(self::$ldapBase) - && !empty(self::$ldapUserDisplayName) - ) - { - self::$configured = true; - } - } - } - - /** - * Connects and Binds to LDAP - */ - static private function establishConnection() { - if(!self::$configured) { - OCP\Util::writeLog('ldap', 'Configuration is invalid, cannot connect', OCP\Util::INFO); - return false; - } - if(!self::$ldapConnectionRes) { - self::$ldapConnectionRes = ldap_connect(self::$ldapHost, self::$ldapPort); - if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_PROTOCOL_VERSION, 3)) { - if(ldap_set_option(self::$ldapConnectionRes, LDAP_OPT_REFERRALS, 0)) { - if(self::$ldapTLS) { - ldap_start_tls(self::$ldapConnectionRes); - } - } - } - - $ldapLogin = @ldap_bind(self::$ldapConnectionRes, self::$ldapAgentName, self::$ldapAgentPassword ); - if(!$ldapLogin) { - OCP\Util::writeLog('ldap', 'Bind failed: ' . ldap_errno(self::$ldapConnectionRes) . ': ' . ldap_error(self::$ldapConnectionRes), OCP\Util::ERROR); - self::$ldapConnectionRes = null; - return false; - } - } - } - - static public function areCredentialsValid($name, $password) { - return @ldap_bind(self::getConnectionResource(), $name, $password); - } - - /** - * taken from http://www.php.net/manual/en/function.array-search.php#97645 - * TODO: move somewhere, where its better placed since it is not LDAP specific. OC_Helper maybe? - */ - static public function recursiveArraySearch($haystack, $needle, $index = null) { - $aIt = new RecursiveArrayIterator($haystack); - $it = new RecursiveIteratorIterator($aIt); - - while($it->valid()) { - if (((isset($index) AND ($it->key() == $index)) OR (!isset($index))) AND ($it->current() == $needle)) { - return $aIt->key(); - } - - $it->next(); - } - - return false; - } - - } diff --git a/apps/user_ldap/tests/group_ldap.php b/apps/user_ldap/tests/group_ldap.php index 2be6b46fb23..106459580fa 100644 --- a/apps/user_ldap/tests/group_ldap.php +++ b/apps/user_ldap/tests/group_ldap.php @@ -26,8 +26,8 @@ class Test_Group_Ldap extends UnitTestCase { } function testSingleBackend(){ - OC_Group::useBackend(new OC_GROUP_LDAP()); - $group_ldap = new OC_GROUP_LDAP(); + OC_Group::useBackend(new OCA\user_ldap\GROUP_LDAP()); + $group_ldap = new OCA\user_ldap\GROUP_LDAP(); $this->assertIsA(OC_Group::getGroups(),gettype(array())); $this->assertIsA($group_ldap->getGroups(),gettype(array())); diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index b51d9a55cc7..a4a8921d08d 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -23,13 +23,9 @@ * */ -class OC_USER_LDAP extends OC_User_Backend { +namespace OCA\user_ldap; - // cached settings - protected $ldapUserFilter; - protected $ldapQuotaAttribute; - protected $ldapQuotaDefault; - protected $ldapEmailAttribute; +class USER_LDAP extends lib\Access implements \OCP\UserInterface { // will be retrieved from LDAP server protected $ldap_dc = false; @@ -37,39 +33,32 @@ class OC_USER_LDAP extends OC_User_Backend { // cache getUsers() protected $_users = null; - public function __construct() { - $this->ldapUserFilter = OCP\Config::getAppValue('user_ldap', 'ldap_userlist_filter', '(objectClass=posixAccount)'); - $this->ldapQuotaAttribute = OCP\Config::getAppValue('user_ldap', 'ldap_quota_attr', ''); - $this->ldapQuotaDefault = OCP\Config::getAppValue('user_ldap', 'ldap_quota_def', ''); - $this->ldapEmailAttribute = OCP\Config::getAppValue('user_ldap', 'ldap_email_attr', ''); - } - private function updateQuota($dn) { $quota = null; - if(!empty($this->ldapQuotaDefault)) { - $quota = $this->ldapQuotaDefault; + if(!empty($this->connection->ldapQuotaDefault)) { + $quota = $this->connection->ldapQuotaDefault; } - if(!empty($this->ldapQuotaAttribute)) { - $aQuota = OC_LDAP::readAttribute($dn, $this->ldapQuotaAttribute); + if(!empty($this->connection->ldapQuotaAttribute)) { + $aQuota = $this->readAttribute($dn, $this->connection->ldapQuotaAttribute); if($aQuota && (count($aQuota) > 0)) { $quota = $aQuota[0]; } } if(!is_null($quota)) { - OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'files', 'quota', OCP\Util::computerFileSize($quota)); + \OCP\Config::setUserValue($this->dn2username($dn), 'files', 'quota', \OCP\Util::computerFileSize($quota)); } } private function updateEmail($dn) { $email = null; - if(!empty($this->ldapEmailAttribute)) { - $aEmail = OC_LDAP::readAttribute($dn, $this->ldapEmailAttribute); + if(!empty($this->connection->ldapEmailAttribute)) { + $aEmail = $this->readAttribute($dn, $this->connection->ldapEmailAttribute); if($aEmail && (count($aEmail) > 0)) { $email = $aEmail[0]; } - if(!is_null($email)){ - OCP\Config::setUserValue(OC_LDAP::dn2username($dn), 'settings', 'email', $email); + if(!is_null($email)) { + \OCP\Config::setUserValue($this->dn2username($dn), 'settings', 'email', $email); } } } @@ -84,15 +73,15 @@ class OC_USER_LDAP extends OC_User_Backend { */ public function checkPassword($uid, $password){ //find out dn of the user name - $filter = OCP\Util::mb_str_replace('%uid', $uid, OC_LDAP::conf('ldapLoginFilter'), 'UTF-8'); - $ldap_users = OC_LDAP::fetchListOfUsers($filter, 'dn'); + $filter = \OCP\Util::mb_str_replace('%uid', $uid, $this->connection->ldapLoginFilter, 'UTF-8'); + $ldap_users = $this->fetchListOfUsers($filter, 'dn'); if(count($ldap_users) < 1) { return false; } $dn = $ldap_users[0]; //are the credentials OK? - if(!OC_LDAP::areCredentialsValid($dn, $password)) { + if(!$this->areCredentialsValid($dn, $password)) { return false; } @@ -101,7 +90,7 @@ class OC_USER_LDAP extends OC_User_Backend { $this->updateEmail($dn); //give back the display name - return OC_LDAP::dn2username($dn); + return $this->dn2username($dn); } /** @@ -112,8 +101,8 @@ class OC_USER_LDAP extends OC_User_Backend { */ public function getUsers(){ if(is_null($this->_users)) { - $ldap_users = OC_LDAP::fetchListOfUsers($this->ldapUserFilter, array(OC_LDAP::conf('ldapUserDisplayName'), 'dn')); - $this->_users = OC_LDAP::ownCloudUserNames($ldap_users); + $ldap_users = $this->fetchListOfUsers($this->connection->ldapUserFilter, array($this->connection->ldapUserDisplayName, 'dn')); + $this->_users = $this->ownCloudUserNames($ldap_users); } return $this->_users; } @@ -125,13 +114,13 @@ class OC_USER_LDAP extends OC_User_Backend { */ public function userExists($uid){ //getting dn, if false the user does not exist. If dn, he may be mapped only, requires more checking. - $dn = OC_LDAP::username2dn($uid); + $dn = $this->username2dn($uid); if(!$dn) { return false; } //if user really still exists, we will be able to read his cn - $cn = OC_LDAP::readAttribute($dn, 'cn'); + $cn = $this->readAttribute($dn, 'cn'); if(!$cn || empty($cn)) { return false; } @@ -139,4 +128,27 @@ class OC_USER_LDAP extends OC_User_Backend { return true; } + /** + * @brief delete a user + * @param $uid The username of the user to delete + * @returns true/false + * + * Deletes a user + */ + public function deleteUser($uid) { + return false; + } + + /** + * @brief Check if backend implements actions + * @param $actions bitwise-or'ed actions + * @returns boolean + * + * Returns the supported actions as int to be + * compared with OC_USER_BACKEND_CREATE_USER etc. + */ + public function implementsActions($actions) { + return (bool)(OC_USER_BACKEND_CHECK_PASSWORD & $actions); + } + }
\ No newline at end of file diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php index 1718abe9e0f..bce5fb2d7ca 100644 --- a/apps/user_migrate/templates/settings.php +++ b/apps/user_migrate/templates/settings.php @@ -12,7 +12,7 @@ <?php } ?> <legend><strong><?php echo $l->t('Import user account');?></strong></legend> </p> - <p><input type="file" id="owncloud_import" name="owncloud_import" style="width:180px;"><label for="owncloud_import"> <?php echo $l->t('ownCloud User Zip');?></label> + <p><input type="file" id="owncloud_import" name="owncloud_import" style="width:280px;"><label for="owncloud_import"> <?php echo $l->t('ownCloud User Zip');?></label> </p> <input type="submit" name="user_import" value="<?php echo $l->t('Import'); ?>" /> </fieldset> diff --git a/apps/user_openid/class.openid.v3.php b/apps/user_openid/class.openid.v3.php deleted file mode 100644 index eeb31986659..00000000000 --- a/apps/user_openid/class.openid.v3.php +++ /dev/null @@ -1,326 +0,0 @@ -<?php -/* - FREE TO USE - Under License: GPLv3 - Simple OpenID PHP Class - - Some modifications by Eddie Roosenmaallen, eddie@roosenmaallen.com - --=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= - -This Class was written to make easy for you to integrate OpenID on your website. -This is just a client, which checks for user's identity. This Class Requires CURL Module. -It should be easy to use some other HTTP Request Method, but remember, often OpenID servers -are using SSL. -We need to be able to perform SSL Verification on the background to check for valid signature. - -HOW TO USE THIS CLASS: - STEP 1) - $openid = new SimpleOpenID; - :: SET IDENTITY :: - $openid->SetIdentity($_POST['openid_url']); - :: SET RETURN URL :: - $openid->SetApprovedURL('http://www.yoursite.com/return.php'); // Script which handles a response from OpenID Server - :: SET TRUST ROOT :: - $openid->SetTrustRoot('http://www.yoursite.com/'); - :: FETCH SERVER URL FROM IDENTITY PAGE :: [Note: It is recomended to cache this (Session, Cookie, Database)] - $openid->GetOpenIDServer(); // Returns false if server is not found - :: REDIRECT USER TO OPEN ID SERVER FOR APPROVAL :: - - :: (OPTIONAL) SET OPENID SERVER :: - $openid->SetOpenIDServer($server_url); // If you have cached previously this, you don't have to call GetOpenIDServer and set value this directly - - STEP 2) - Once user gets returned we must validate signature - :: VALIDATE REQUEST :: - true|false = $openid->ValidateWithServer(); - - ERRORS: - array = $openid->GetError(); // Get latest Error code - - FIELDS: - OpenID allowes you to retreive a profile. To set what fields you'd like to get use (accepts either string or array): - $openid->SetRequiredFields(array('email','fullname','dob','gender','postcode','country','language','timezone')); - or - $openid->SetOptionalFields('postcode'); - -IMPORTANT TIPS: -OPENID as is now, is not trust system. It is a great single-sign on method. If you want to -store information about OpenID in your database for later use, make sure you handle url identities -properly. - For example: - https://steve.myopenid.com/ - https://steve.myopenid.com - http://steve.myopenid.com/ - http://steve.myopenid.com - ... are representing one single user. Some OpenIDs can be in format openidserver.com/users/user/ - keep this in mind when storing identities - - To help you store an OpenID in your DB, you can use function: - $openid_db_safe = $openid->OpenID_Standarize($upenid); - This may not be comatible with current specs, but it works in current enviroment. Use this function to get openid - in one format like steve.myopenid.com (without trailing slashes and http/https). - Use output to insert Identity to database. Don't use this for validation - it may fail. - -*/ - -class SimpleOpenID{ - var $openid_url_identity; - var $URLs = array(); - var $error = array(); - var $fields = array( - 'required' => array(), - 'optional' => array(), - ); - - function SimpleOpenID(){ - if (!function_exists('curl_exec')) { - die('Error: Class SimpleOpenID requires curl extension to work'); - } - } - function SetOpenIDServer($a){ - $this->URLs['openid_server'] = $a; - } - function SetTrustRoot($a){ - $this->URLs['trust_root'] = $a; - } - function SetCancelURL($a){ - $this->URLs['cancel'] = $a; - } - function SetApprovedURL($a){ - $this->URLs['approved'] = $a; - } - function SetRequiredFields($a){ - if (is_array($a)){ - $this->fields['required'] = $a; - }else{ - $this->fields['required'][] = $a; - } - } - function SetOptionalFields($a){ - if (is_array($a)){ - $this->fields['optional'] = $a; - }else{ - $this->fields['optional'][] = $a; - } - } - function SetIdentity($a){ // Set Identity URL - if ((stripos($a, 'http://') === false) - && (stripos($a, 'https://') === false)){ - $a = 'http://'.$a; - } -/* - $u = parse_url(trim($a)); - if (!isset($u['path'])){ - $u['path'] = '/'; - }else if(substr($u['path'],-1,1) == '/'){ - $u['path'] = substr($u['path'], 0, strlen($u['path'])-1); - } - if (isset($u['query'])){ // If there is a query string, then use identity as is - $identity = $a; - }else{ - $identity = $u['scheme'] . '://' . $u['host'] . $u['path']; - } -//*/ - $this->openid_url_identity = $a; - } - function GetIdentity(){ // Get Identity - return $this->openid_url_identity; - } - function GetError(){ - $e = $this->error; - return array('code'=>$e[0],'description'=>$e[1]); - } - - function ErrorStore($code, $desc = null){ - $errs['OPENID_NOSERVERSFOUND'] = 'Cannot find OpenID Server TAG on Identity page.'; - if ($desc == null){ - $desc = $errs[$code]; - } - $this->error = array($code,$desc); - } - - function IsError(){ - if (count($this->error) > 0){ - return true; - }else{ - return false; - } - } - - function splitResponse($response) { - $r = array(); - $response = explode("\n", $response); - foreach($response as $line) { - $line = trim($line); - if ($line != "") { - list($key, $value) = explode(":", $line, 2); - $r[trim($key)] = trim($value); - } - } - return $r; - } - - function OpenID_Standarize($openid_identity = null){ - if ($openid_identity === null) - $openid_identity = $this->openid_url_identity; - - $u = parse_url(strtolower(trim($openid_identity))); - - if (!isset($u['path']) || ($u['path'] == '/')) { - $u['path'] = ''; - } - if(substr($u['path'],-1,1) == '/'){ - $u['path'] = substr($u['path'], 0, strlen($u['path'])-1); - } - if (isset($u['query'])){ // If there is a query string, then use identity as is - return $u['host'] . $u['path'] . '?' . $u['query']; - }else{ - return $u['host'] . $u['path']; - } - } - - function array2url($arr){ // converts associated array to URL Query String - if (!is_array($arr)){ - return false; - } - $query = ''; - foreach($arr as $key => $value){ - $query .= $key . "=" . $value . "&"; - } - return $query; - } - function FSOCK_Request($url, $method="GET", $params = ""){ - $fp = fsockopen("ssl://www.myopenid.com", 443, $errno, $errstr, 3); // Connection timeout is 3 seconds - if (!$fp) { - $this->ErrorStore('OPENID_SOCKETERROR', $errstr); - return false; - } else { - $request = $method . " /server HTTP/1.0\r\n"; - $request .= "User-Agent: Simple OpenID PHP Class (http://www.phpclasses.org/simple_openid)\r\n"; - $request .= "Connection: close\r\n\r\n"; - fwrite($fp, $request); - stream_set_timeout($fp, 4); // Connection response timeout is 4 seconds - $res = fread($fp, 2000); - $info = stream_get_meta_data($fp); - fclose($fp); - - if ($info['timed_out']) { - $this->ErrorStore('OPENID_SOCKETTIMEOUT'); - } else { - return $res; - } - } - } - function CURL_Request($url, $method="GET", $params = "") { // Remember, SSL MUST BE SUPPORTED - if (is_array($params)) $params = $this->array2url($params); - $curl = curl_init($url . ($method == "GET" && $params != "" ? "?" . $params : "")); - curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($curl, CURLOPT_HEADER, false); - curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); - curl_setopt($curl, CURLOPT_HTTPGET, ($method == "GET")); - curl_setopt($curl, CURLOPT_POST, ($method == "POST")); - if ($method == "POST") curl_setopt($curl, CURLOPT_POSTFIELDS, $params); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); - $response = curl_exec($curl); - - if (curl_errno($curl) == 0){ - $response; - }else{ - $this->ErrorStore('OPENID_CURL', curl_error($curl)); - } - return $response; - } - - function HTML2OpenIDServer($content) { - $get = array(); - - // Get details of their OpenID server and (optional) delegate - preg_match_all('/<link[^>]*rel=[\'"](openid2.provider )?openid.server[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1); - preg_match_all('/<link[^>]*href=\'"([^\'"]+)[\'"][^>]*rel=[\'"](openid2.provider )?openid.server[\'"][^>]*\/?>/i', $content, $matches2); - $servers = array_merge($matches1[2], $matches2[1]); - - preg_match_all('/<link[^>]*rel=[\'"]openid.delegate[\'"][^>]*href=[\'"]([^\'"]+)[\'"][^>]*\/?>/i', $content, $matches1); - - preg_match_all('/<link[^>]*href=[\'"]([^\'"]+)[\'"][^>]*rel=[\'"]openid.delegate[\'"][^>]*\/?>/i', $content, $matches2); - - $delegates = array_merge($matches1[1], $matches2[1]); - - $ret = array($servers, $delegates); - return $ret; - } - - function GetOpenIDServer(){ - $response = $this->CURL_Request($this->openid_url_identity); - list($servers, $delegates) = $this->HTML2OpenIDServer($response); - if (count($servers) == 0){ - $this->ErrorStore('OPENID_NOSERVERSFOUND'); - return false; - } - if (isset($delegates[0]) - && ($delegates[0] != "")){ - $this->SetIdentity($delegates[0]); - } - $this->SetOpenIDServer($servers[0]); - return $servers[0]; - } - - function GetRedirectURL(){ - $params = array(); - $params['openid.return_to'] = urlencode($this->URLs['approved']); - $params['openid.mode'] = 'checkid_setup'; - $params['openid.identity'] = urlencode($this->openid_url_identity); - $params['openid.trust_root'] = urlencode($this->URLs['trust_root']); - - if (isset($this->fields['required']) - && (count($this->fields['required']) > 0)) { - $params['openid.sreg.required'] = implode(',',$this->fields['required']); - } - if (isset($this->fields['optional']) - && (count($this->fields['optional']) > 0)) { - $params['openid.sreg.optional'] = implode(',',$this->fields['optional']); - } - return $this->URLs['openid_server'] . "?". $this->array2url($params); - } - - function Redirect(){ - $redirect_to = $this->GetRedirectURL(); - if (headers_sent()){ // Use JavaScript to redirect if content has been previously sent (not recommended, but safe) - echo '<script language="JavaScript" type="text/javascript">window.location=\''; - echo $redirect_to; - echo '\';</script>'; - }else{ // Default Header Redirect - header('Location: ' . $redirect_to); - } - } - - function ValidateWithServer(){ - $params = array( - 'openid.assoc_handle' => urlencode($_GET['openid_assoc_handle']), - 'openid.signed' => urlencode($_GET['openid_signed']), - 'openid.sig' => urlencode($_GET['openid_sig']) - ); - // Send only required parameters to confirm validity - $arr_signed = explode(",",str_replace('sreg.','sreg_',$_GET['openid_signed'])); - for ($i=0; $i<count($arr_signed); $i++){ - $s = str_replace('sreg_','sreg.', $arr_signed[$i]); - $c = $_GET['openid_' . $arr_signed[$i]]; - // if ($c != ""){ - $params['openid.' . $s] = urlencode($c); - // } - } - $params['openid.mode'] = "check_authentication"; - - $openid_server = $this->GetOpenIDServer(); - if ($openid_server == false){ - return false; - } - $response = $this->CURL_Request($openid_server,'POST',$params); - $data = $this->splitResponse($response); - - if ($data['is_valid'] == "true") { - return true; - }else{ - return false; - } - } -} diff --git a/apps/user_openid/phpmyid.php b/apps/user_openid/phpmyid.php deleted file mode 100644 index 13fd31c47ca..00000000000 --- a/apps/user_openid/phpmyid.php +++ /dev/null @@ -1,1707 +0,0 @@ -<?php -// PLEASE DO NOT EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING! - -/** - * phpMyID - A standalone, single user, OpenID Identity Provider - * - * @package phpMyID - * @author CJ Niemira <siege (at) siege (dot) org> - * @copyright 2006-2008 - * @license http://www.gnu.org/licenses/gpl.html GNU Public License - * @url http://siege.org/projects/phpMyID - * @version 0.9 - */ - -/** - * Set a constant to indicate that phpMyID is running - */ -define('PHPMYID_STARTED', true); - -/** - * List the known types and modes - * @name $known - * @global array $GLOBALS['known'] - */ -$GLOBALS['known'] = array( - 'assoc_types' => array('HMAC-SHA1'), - - 'openid_modes' => array('accept', - 'associate', - 'authorize', - 'cancel', - 'checkid_immediate', - 'checkid_setup', - 'check_authentication', - 'error', - 'id_res', - 'login', - 'logout', - 'test'), - - 'session_types' => array('', - 'DH-SHA1'), - - 'bigmath_types' => array('DH-SHA1'), -); - -/** - * Defined by OpenID spec - * @name $g - * @global integer $GLOBALS['g'] - */ -$GLOBALS['g'] = 2; - -/** - * Defined by OpenID spec - * @name $p - * @global integer $GLOBALS['p'] - */ -$GLOBALS['p'] = '155172898181473697471232257763715539915724801966915404479707' . -'7953140576293785419175806512274236981889937278161526466314385615958256881888' . -'8995127215884267541995034125870655654980358010487053768147672651325574704076' . -'5857479291291572334510643245094715007229621094194349783925984760375594985848' . -'253359305585439638443'; - - -// Runmode functions - -/** - * Allow the user to accept trust on a URL - * @global array $profile - */ -function accept_mode () { - global $profile; - - // this is a user session - user_session(); - - // the user needs refresh urls in their session to access this mode - if (! isset($_SESSION['post_accept_url']) || ! isset($_SESSION['cancel_accept_url']) || ! isset($_SESSION['unaccepted_url'])) - error_500('You may not access this mode directly.'); - - // has the user accepted the trust_root? - $accepted = @strlen($_REQUEST['accepted']) - ? $_REQUEST['accepted'] - : null; - - // if so, refresh back to post_accept_url - if ($accepted === 'yes') { - $_SESSION['accepted_url'] = $_SESSION['unaccepted_url']; - wrap_redirect($_SESSION['post_accept_url']); - - // if they rejected it, return to the client - } elseif ($accepted === 'no') { - wrap_redirect($_SESSION['cancel_accept_url']); - } - - // if neither, offer the trust request - $q = strpos($profile['req_url'], '?') ? '&' : '?'; - $yes = $profile['req_url'] . $q . 'accepted=yes'; - $no = $profile['req_url'] . $q . 'accepted=no'; - - wrap_html('The client site you are attempting to log into has requested that you trust the following URL:<br/><b>' . $_SESSION['unaccepted_url'] . '</b><br/><br/>Do you wish to continue?<br/><a href="' . $yes . '">Yes</a> | <a href="' . $no . '">No</a>'); -} - -/** * Perform an association with a consumer - * @global array $known - * @global array $profile - * @global integer $g - * @global integer $p - */ -function associate_mode () { - global $g, $known, $p, $profile; - - // Validate the request - if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'associate') - error_400(); - - // Get the request options, using defaults as necessary - $assoc_type = (@strlen($_REQUEST['openid_assoc_type']) - && in_array($_REQUEST['openid_assoc_type'], $known['assoc_types'])) - ? $_REQUEST['openid_assoc_type'] - : 'HMAC-SHA1'; - - $session_type = (@strlen($_REQUEST['openid_session_type']) - && in_array($_REQUEST['openid_session_type'], $known['session_types'])) - ? $_REQUEST['openid_session_type'] - : ''; - - $dh_modulus = (@strlen($_REQUEST['openid_dh_modulus'])) - ? long(base64_decode($_REQUEST['openid_dh_modulus'])) - : ($session_type == 'DH-SHA1' - ? $p - : null); - - $dh_gen = (@strlen($_REQUEST['openid_dh_gen'])) - ? long(base64_decode($_REQUEST['openid_dh_gen'])) - : ($session_type == 'DH-SHA1' - ? $g - : null); - - $dh_consumer_public = (@strlen($_REQUEST['openid_dh_consumer_public'])) - ? $_REQUEST['openid_dh_consumer_public'] - : ($session_type == 'DH-SHA1' - ? error_post('dh_consumer_public was not specified') - : null); - - $lifetime = time() + $profile['lifetime']; - - // Create standard keys - $keys = array( - 'assoc_type' => $assoc_type, - 'expires_in' => $profile['lifetime'] - ); - - // If I can't handle bigmath, default to plaintext sessions - if (in_array($session_type, $known['bigmath_types']) && $profile['use_bigmath'] === false) - $session_type = null; - - // Add response keys based on the session type - switch ($session_type) { - case 'DH-SHA1': - // Create the associate id and shared secret now - list ($assoc_handle, $shared_secret) = new_assoc($lifetime); - - // Compute the Diffie-Hellman stuff - $private_key = random($dh_modulus); - $public_key = bmpowmod($dh_gen, $private_key, $dh_modulus); - $remote_key = long(base64_decode($dh_consumer_public)); - $ss = bmpowmod($remote_key, $private_key, $dh_modulus); - - $keys['assoc_handle'] = $assoc_handle; - $keys['session_type'] = $session_type; - $keys['dh_server_public'] = base64_encode(bin($public_key)); - $keys['enc_mac_key'] = base64_encode(x_or(sha1_20(bin($ss)), $shared_secret)); - - break; - - default: - // Create the associate id and shared secret now - list ($assoc_handle, $shared_secret) = new_assoc($lifetime); - - $keys['assoc_handle'] = $assoc_handle; - $keys['mac_key'] = base64_encode($shared_secret); - } - - // Return the keys - wrap_kv($keys); -} - - -/** - * Perform a user authorization - * @global array $profile - */ -function authorize_mode () { - global $profile; - global $USERNAME; - global $IDENTITY; - - // this is a user session - - // the user needs refresh urls in their session to access this mode - if (! isset($_SESSION['post_auth_url']) || ! isset($_SESSION['cancel_auth_url'])) - error_500('You may not access this mode directly.'); - - $profile['idp_url']=$IDENTITY; - if (isset($_SERVER['PHP_AUTH_USER']) && $profile['authorized'] === false && $_SERVER['PHP_AUTH_USER']==$USERNAME) { - if (OCP\User::checkPassword($USERNAME, $_SERVER['PHP_AUTH_PW'])) {// successful login! - // return to the refresh url if they get in - $_SESSION['openid_auth']=true; - $_SESSION['openid_user']=$USERNAME; - wrap_redirect($_SESSION['post_auth_url']); - - // failed login - } else { - $_SESSION['failures']++; - debug('Login failed'); - debug('Fail count: ' . $_SESSION['failures']); - } - - } - - // if we get this far the user is not authorized, so send the headers - $uid = uniqid(mt_rand(1,9)); - $_SESSION['uniqid'] = $uid; - -// debug('Prompting user to log in. Stale? ' . $stale); - header('HTTP/1.0 401 Unauthorized'); -// header(sprintf('WWW-Authenticate: Digest qop="auth-int, auth", realm="%s", domain="%s", nonce="%s", opaque="%s", stale="%s", algorithm="MD5"', $profile['auth_realm'], $profile['auth_domain'], $uid, md5($profile['auth_realm']), $stale ? 'true' : 'false')); - header('WWW-Authenticate: Basic realm="ownCloud"'); - $q = strpos($_SESSION['cancel_auth_url'], '?') ? '&' : '?'; - wrap_refresh($_SESSION['cancel_auth_url'] . $q . 'openid.mode=cancel'); -// die('401 Unauthorized'); -} - - -/** - * Handle a consumer's request for cancellation. - */ -function cancel_mode () { - wrap_html('Request cancelled.'); -} - - -/** - * Handle a consumer's request to see if the user is authenticated - */ -function check_authentication_mode () { - // Validate the request - if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'check_authentication') - error_400(); - - $assoc_handle = @strlen($_REQUEST['openid_assoc_handle']) - ? $_REQUEST['openid_assoc_handle'] - : error_post('Missing assoc_handle'); - - $sig = @strlen($_REQUEST['openid_sig']) - ? $_REQUEST['openid_sig'] - : error_post('Missing sig'); - - $signed = @strlen($_REQUEST['openid_signed']) - ? $_REQUEST['openid_signed'] - : error_post('Missing signed'); - - // Prepare the return keys - $keys = array( - 'openid.mode' => 'id_res' - ); - - // Invalidate the assoc handle if we need to - if (@strlen($_REQUEST['openid_invalidate_handle'])) { - destroy_assoc_handle($_REQUEST['openid_invalidate_handle']); - - $keys['invalidate_handle'] = $_REQUEST['openid_invalidate_handle']; - } - - // Validate the sig by recreating the kv pair and signing - $_REQUEST['openid_mode'] = 'id_res'; - $tokens = ''; - foreach (explode(',', $signed) as $param) { - $post = preg_replace('/\./', '_', $param); - $tokens .= sprintf("%s:%s\n", $param, $_REQUEST['openid_' . $post]); - } - - // Add the sreg stuff, if we've got it - if (isset($sreg_required)) { - foreach (explode(',', $sreg_required) as $key) { - if (! isset($sreg[$key])) - continue; - $skey = 'sreg.' . $key; - - $tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]); - $keys[$skey] = $sreg[$key]; - $fields[] = $skey; - } - } - - // Look up the consumer's shared_secret and timeout - list ($shared_secret, $expires) = secret($assoc_handle); - - // if I can't verify the assoc_handle, or if it's expired - if ($shared_secret == false || (is_numeric($expires) && $expires < time())) { - $keys['is_valid'] = 'false'; - - } else { - $ok = base64_encode(hmac($shared_secret, $tokens)); - $keys['is_valid'] = ($sig == $ok) ? 'true' : 'false'; - } - - // Return the keys - wrap_kv($keys); -} - - -/** - * Handle a consumer's request to see if the end user is logged in - * @global array $known - * @global array $profile - * @global array $sreg - */ -function checkid ( $wait ) { - global $known, $profile, $sreg; - global $USERNAME; - - // This is a user session - user_session(); - - // Get the options, use defaults as necessary - $return_to = isset($_REQUEST['openid_return_to']) - ? $_REQUEST['openid_return_to'] - : error_400('Missing return_to'); - - $identity = isset($_REQUEST['openid_identity']) - ? $_REQUEST['openid_identity'] - : error_get($return_to, 'Missing identity'); - - $assoc_handle = isset($_REQUEST['openid_assoc_handle']) - ? $_REQUEST['openid_assoc_handle'] - : null; - - $trust_root = isset($_REQUEST['openid_trust_root']) - ? $_REQUEST['openid_trust_root'] - : $return_to; - - $sreg_required = isset($_REQUEST['openid_sreg_required']) - ? $_REQUEST['openid_sreg.required'] - : ''; - - $sreg_optional = isset($_REQUEST['openid_sreg_optional']) - ? $_REQUEST['openid_sreg.optional'] - : ''; - - // determine the cancel url - $q = strpos($return_to, '?') ? '&' : '?'; - $cancel_url = $return_to . $q . 'openid.mode=cancel'; - - // required and optional make no difference to us - $sreg_required .= ',' . $sreg_optional; - // do the trust_root analysis - if ($trust_root != $return_to) { - // the urls are not the same, be sure return decends from trust - if (! url_descends($return_to, $trust_root)) - error_500('Invalid trust_root: "' . $trust_root . '"'); - - } - - // transfer the user to the url accept mode if they're paranoid - if ($wait == 1 && isset($profile['paranoid']) && $profile['paranoid'] === true && (! isset($_SESSION['accepted_url']) || $_SESSION['accepted_url'] != $trust_root)) { - $_SESSION['cancel_accept_url'] = $cancel_url; - $_SESSION['post_accept_url'] = $profile['req_url']; - $_SESSION['unaccepted_url'] = $trust_root; - - debug('Transferring to acceptance mode.'); - debug('Cancel URL: ' . $_SESSION['cancel_accept_url']); - debug('Post URL: ' . $_SESSION['post_accept_url']); - - $q = strpos($profile['idp_url'], '?') ? '&' : '?'; - wrap_redirect($profile['idp_url'] . $q . 'openid.mode=accept'); - } - - // make sure i am this identifier -// if ($identity != $profile['idp_url']) { -// debug("Invalid identity: $identity"); -// debug("IdP URL: " . $profile['idp_url']); -// error_get($return_to, "Invalid identity: '$identity'"); -// } - - // begin setting up return keys - $keys = array( - 'mode' => 'id_res' - ); - - // if the user is not logged in, transfer to the authorization mode - if ($USERNAME=='' || $_SESSION['openid_auth'] === false || $USERNAME != $_SESSION['openid_user']) { - // users can only be logged in to one url at a time - $_SESSION['openid_user'] = null; - $_SESSION['auth_url'] = null; - - if ($wait) { - unset($_SESSION['uniqid']); - - $_SESSION['cancel_auth_url'] = $cancel_url; - $_SESSION['post_auth_url'] = $profile['req_url']; - - debug('Transferring to authorization mode.'); - debug('Cancel URL: ' . $_SESSION['cancel_auth_url']); - debug('Post URL: ' . $_SESSION['post_auth_url']); - - $q = strpos($profile['idp_url'], '?') ? '&' : '?'; - wrap_redirect($profile['idp_url'] . $q . 'openid.mode=authorize'); - } else { - $keys['user_setup_url'] = $profile['idp_url']; - } - - // the user is logged in - } else { - // remove the refresh URLs if set - unset($_SESSION['cancel_auth_url']); - unset($_SESSION['post_auth_url']); - - // check the assoc handle - list($shared_secret, $expires) = secret($assoc_handle); - - // if I can't verify the assoc_handle, or if it's expired - if ($shared_secret == false || (is_numeric($expires) && $expires < time())) { - debug("Session expired or missing key: $expires < " . time()); - if ($assoc_handle != null) { - $keys['invalidate_handle'] = $assoc_handle; - destroy_assoc_handle($assoc_handle); - } - - $lifetime = time() + $profile['lifetime']; - list ($assoc_handle, $shared_secret) = new_assoc($lifetime); - } - - $keys['identity'] = $profile['idp_url']; - $keys['assoc_handle'] = $assoc_handle; - $keys['return_to'] = $return_to; - - $fields = array_keys($keys); - $tokens = ''; - foreach ($fields as $key) - $tokens .= sprintf("%s:%s\n", $key, $keys[$key]); - - // add sreg keys - foreach (explode(',', $sreg_required) as $key) { - if (! isset($sreg[$key])) - continue; - $skey = 'sreg.' . $key; - - $tokens .= sprintf("%s:%s\n", $skey, $sreg[$key]); - $keys[$skey] = $sreg[$key]; - $fields[] = $skey; - } - - $keys['signed'] = implode(',', $fields); - $keys['sig'] = base64_encode(hmac($shared_secret, $tokens)); - } - - wrap_keyed_redirect($return_to, $keys); -} - - -/** - * Handle a consumer's request to see if the user is already logged in - */ -function checkid_immediate_mode () { - if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_immediate') - error_500(); - - checkid(false); -} - - -/** - * Handle a consumer's request to see if the user is logged in, but be willing - * to wait for them to perform a login if they're not - */ -function checkid_setup_mode () { - if (! isset($_REQUEST['openid_mode']) || $_REQUEST['openid_mode'] != 'checkid_setup') - error_500(); - - checkid(true); -} - - -/** - * Handle errors - */ -function error_mode () { - isset($_REQUEST['openid_error']) - ? wrap_html($_REQUEST['openid_error']) - : error_500(); -} - - -/** - * Show a user if they are logged in or not - * @global array $profile - */ -function id_res_mode () { - global $profile; - - user_session(); - - if ($profile['authorized']) - wrap_html('You are logged in as ' . $_SESSION['auth_username']); - - wrap_html('You are not logged in'); -} - - -/** - * Allow a user to perform a static login - * @global array $profile - */ -function login_mode () { - global $profile; - - user_session(); - - if ($profile['authorized']) - id_res_mode(); - - $keys = array( - 'mode' => 'checkid_setup', - 'identity' => $profile['idp_url'], - 'return_to' => $profile['idp_url'] - ); - - wrap_keyed_redirect($profile['idp_url'], $keys); -} - - -/** - * Allow a user to perform a static logout - * @global array $profile - */ -function logout_mode () { - global $profile; - - user_session(); - - if (! $profile['authorized']) - wrap_html('You were not logged in'); - - $_SESSION = array(); - session_destroy(); - debug('User session destroyed.'); - - header('HTTP/1.0 401 Unauthorized'); - wrap_redirect($profile['idp_url']); -} - - -/** - * The default information screen - * @global array $profile - */ -function no_mode () { - global $USERNAME, $profile; - $tmpl = new OCP\Template( 'user_openid', 'nomode', 'guest' ); - if(substr($profile['req_url'],-1,1)!=='/'){//the identity should always end with a / - $profile['req_url'].='/'; - } - $tmpl->addHeader('link',array('rel'=>'openid.server', 'href'=>$profile['req_url'])); - $tmpl->addHeader('link',array('rel'=>'openid.delegate', 'href'=>$profile['idp_url'])); - $tmpl->assign('user',$USERNAME); - $tmpl->printPage(); -} - - -/** - * Testing for setup - * @global array $profile - */ -function test_mode () { - global $profile, $p, $g; - - if ($profile['allow_test'] != true) - error_403(); - - @ini_set('max_execution_time', 180); - - $test_expire = time() + 120; - $test_ss_enc = 'W7hvmld2yEYdDb0fHfSkKhQX+PM='; - $test_ss = base64_decode($test_ss_enc); - $test_token = "alpha:bravo\ncharlie:delta\necho:foxtrot"; - $test_server_private = '11263846781670293092494395517924811173145217135753406847875706165886322533899689335716152496005807017390233667003995430954419468996805220211293016296351031812246187748601293733816011832462964410766956326501185504714561648498549481477143603650090931135412673422192550825523386522507656442905243832471167330268'; - $test_client_public = base64_decode('AL63zqI5a5p8HdXZF5hFu8p+P9GOb816HcHuvNOhqrgkKdA3fO4XEzmldlb37nv3+xqMBgWj6gxT7vfuFerEZLBvuWyVvR7IOGZmx0BAByoq3fxYd3Fpe2Coxngs015vK37otmH8e83YyyGo5Qua/NAf13yz1PVuJ5Ctk7E+YdVc'); - - $res = array(); - - // bcmath - $res['bcmath'] = extension_loaded('bcmath') - ? 'pass' : 'warn - not loaded'; - - // gmp - if ($profile['allow_gmp']) { - $res['gmp'] = extension_loaded('gmp') - ? 'pass' : 'warn - not loaded'; - } else { - $res['gmp'] = 'pass - n/a'; - } - - // get_temp_dir - $res['logfile'] = is_writable($profile['logfile']) - ? 'pass' : "warn - log is not writable"; - - // session & new_assoc - user_session(); - list($test_assoc, $test_new_ss) = new_assoc($test_expire); - $res['session'] = ($test_assoc != session_id()) - ? 'pass' : 'fail'; - - // secret - @session_unregister('shared_secret'); - list($check, $check2) = secret($test_assoc); - $res['secret'] = ($check == $test_new_ss) - ? 'pass' : 'fail'; - - // expire - $res['expire'] = ($check2 <= $test_expire) - ? 'pass' : 'fail'; - - // base64 - $res['base64'] = (base64_encode($test_ss) == $test_ss_enc) - ? 'pass' : 'fail'; - - // hmac - $test_sig = base64_decode('/VXgHvZAOdoz/OTa5+XJXzSGhjs='); - $check = hmac($test_ss, $test_token); - $res['hmac'] = ($check == $test_sig) - ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); - - if ($profile['use_bigmath']) { - // bigmath powmod - $test_server_public = '102773334773637418574009974502372885384288396853657336911033649141556441102566075470916498748591002884433213640712303846640842555822818660704173387461364443541327856226098159843042567251113889701110175072389560896826887426539315893475252988846151505416694218615764823146765717947374855806613410142231092856731'; - $check = bmpowmod($g, $test_server_private, $p); - $res['bmpowmod-1'] = ($check == $test_server_public) - ? 'pass' : sprintf("fail - '%s'", $check); - - // long - $test_client_long = '133926731803116519408547886573524294471756220428015419404483437186057383311250738749035616354107518232016420809434801736658109316293127101479053449990587221774635063166689561125137927607200322073086097478667514042144489248048756916881344442393090205172004842481037581607299263456852036730858519133859409417564'; - $res['long'] = (long($test_client_public) == $test_client_long) - ? 'pass' : 'fail'; - - // bigmath powmod 2 - $test_client_share = '19333275433742428703546496981182797556056709274486796259858099992516081822015362253491867310832140733686713353304595602619444380387600756677924791671971324290032515367930532292542300647858206600215875069588627551090223949962823532134061941805446571307168890255137575975911397744471376862555181588554632928402'; - $check = bmpowmod($test_client_long, $test_server_private, $p); - $res['bmpowmod-2'] = ($check == $test_client_share) - ? 'pass' : sprintf("fail - '%s'", $check); - - // bin - $test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI='); - $check = bin($test_client_share); - $res['bin'] = ($check == $test_client_mac_s1) - ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); - - } else { - $res['bigmath'] = 'fail - big math functions are not available.'; - } - - // sha1_20 - $test_client_mac_s1 = base64_decode('G4gQQkYM6QmAzhKbVKSBahFesPL0nL3F2MREVwEtnVRRYI0ifl9zmPklwTcvURt3QTiGBd+9Dn3ESLk5qka6IO5xnILcIoBT8nnGVPiOZvTygfuzKp4tQ2mXuIATJoa7oXRGmBWtlSdFapH5Zt6NJj4B83XF/jzZiRwdYuK4HJI='); - $test_client_mac_s2 = base64_decode('0Mb2t9d/HvAZyuhbARJPYdx3+v4='); - $check = sha1_20($test_client_mac_s1); - $res['sha1_20'] = ($check == $test_client_mac_s2) - ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); - - // x_or - $test_client_mac_s3 = base64_decode('i36ZLYAJ1rYEx1VEHObrS8hgAg0='); - $check = x_or($test_client_mac_s2, $test_ss); - $res['x_or'] = ($check == $test_client_mac_s3) - ? 'pass' : sprintf("fail - '%s'", base64_encode($check)); - - $out = "<table border=1 cellpadding=4>\n"; - foreach ($res as $test => $stat) { - $code = substr($stat, 0, 4); - $color = ($code == 'pass') ? '#9f9' - : (($code == 'warn') ? '#ff9' : '#f99'); - $out .= sprintf("<tr><th>%s</th><td style='background:%s'>%s</td></tr>\n", $test, $color, $stat); - } - $out .= "</table>"; - - wrap_html( $out ); -} - - -// Support functions - -/** - * Prefix the keys of an array with 'openid.' - * @param array $array - * @return array - */ -function append_openid ($array) { - $keys = array_keys($array); - $vals = array_values($array); - - $r = array(); - for ($i=0; $i<sizeof($keys); $i++) - $r['openid.' . $keys[$i]] = $vals[$i]; - return $r; -} - -/** - * Create a big math addition function - * @param string $l - * @param string $r - * @return string - * @url http://www.icosaedro.it/bigint Inspired by - */ -function bmadd($l, $r) { - if (function_exists('bcadd')) - return bcadd($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_add($l, $r)); - - $l = strval($l); $r = strval($r); - $ll = strlen($l); $rl = strlen($r); - if ($ll < $rl) { - $l = str_repeat("0", $rl-$ll) . $l; - $o = $rl; - - } elseif ( $ll > $rl ) { - $r = str_repeat("0", $ll-$rl) . $r; - $o = $ll; - - } else { - $o = $ll; - } - - $v = ''; - $carry = 0; - - for ($i = $o-1; $i >= 0; $i--) { - $d = (int)$l[$i] + (int)$r[$i] + $carry; - if ($d <= 9) { - $carry = 0; - - } else { - $carry = 1; - $d -= 10; - } - $v = (string) $d . $v; - } - - if ($carry > 0) - $v = "1" . $v; - - return $v; -} - -/** - * Create a big math comparison function - * @param string $l - * @param string $r - * @return string - */ -function bmcomp($l, $r) { - if (function_exists('bccomp')) - return bccomp($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_cmp($l, $r)); - - $l = strval($l); $r = strval($r); - $ll = strlen($l); $lr = strlen($r); - if ($ll != $lr) - return ($ll > $lr) ? 1 : -1; - - return strcmp($l, $r); -} - -/** - * Create a big math division function - * @param string $l - * @param string $r - * @param int $z - * @return string - * @url http://www.icosaedro.it/bigint Inspired by - */ -function bmdiv($l, $r, $z = 0) { - if (function_exists('bcdiv')) - return ($z == 0) ? bcdiv($l, $r) : bcmod($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(($z == 0) ? gmp_div_q($l, $r) : gmp_mod($l, $r)); - - $l = strval($l); $r = strval($r); - $v = '0'; - - while (true) { - if( bmcomp($l, $r) < 0 ) - break; - - $delta = strlen($l) - strlen($r); - if ($delta >= 1) { - $zeroes = str_repeat("0", $delta); - $r2 = $r . $zeroes; - - if (strcmp($l, $r2) >= 0) { - $v = bmadd($v, "1" . $zeroes); - $l = bmsub($l, $r2); - - } else { - $zeroes = str_repeat("0", $delta - 1); - $v = bmadd($v, "1" . $zeroes); - $l = bmsub($l, $r . $zeroes); - } - - } else { - $l = bmsub($l, $r); - $v = bmadd($v, "1"); - } - } - - return ($z == 0) ? $v : $l; -} - -/** - * Create a big math multiplication function - * @param string $l - * @param string $r - * @return string - * @url http://www.icosaedro.it/bigint Inspired by - */ -function bmmul($l, $r) { - if (function_exists('bcmul')) - return bcmul($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_mul($l, $r)); - - $l = strval($l); $r = strval($r); - - $v = '0'; - $z = ''; - - for( $i = strlen($r)-1; $i >= 0; $i-- ){ - $bd = (int) $r[$i]; - $carry = 0; - $p = ""; - for( $j = strlen($l)-1; $j >= 0; $j-- ){ - $ad = (int) $l[$j]; - $pd = $ad * $bd + $carry; - if( $pd <= 9 ){ - $carry = 0; - } else { - $carry = (int) ($pd / 10); - $pd = $pd % 10; - } - $p = (string) $pd . $p; - } - if( $carry > 0 ) - $p = (string) $carry . $p; - $p = $p . $z; - $z .= "0"; - $v = bmadd($v, $p); - } - - return $v; -} - -/** - * Create a big math modulus function - * @param string $value - * @param string $mod - * @return string - */ -function bmmod( $value, $mod ) { - if (function_exists('bcmod')) - return bcmod($value, $mod); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_mod($value, $mod)); - - $r = bmdiv($value, $mod, 1); - return $r; -} - -/** - * Create a big math power function - * @param string $value - * @param string $exponent - * @return string - */ -function bmpow ($value, $exponent) { - if (function_exists('bcpow')) - return bcpow($value, $exponent); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_pow($value, $exponent)); - - $r = '1'; - while ($exponent) { - $r = bmmul($r, $value, 100); - $exponent--; - } - return (string)rtrim($r, '0.'); -} - -/** - * Create a big math 'powmod' function - * @param string $value - * @param string $exponent - * @param string $mod - * @return string - * @url http://php.net/manual/en/function.bcpowmod.php#72704 Borrowed from - */ -function bmpowmod ($value, $exponent, $mod) { - if (function_exists('bcpowmod')) - return bcpowmod($value, $exponent, $mod); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_powm($value, $exponent, $mod)); - - $r = ''; - while ($exponent != '0') { - $t = bmmod($exponent, '4096'); - $r = substr("000000000000" . decbin(intval($t)), -12) . $r; - $exponent = bmdiv($exponent, '4096'); - } - - $r = preg_replace("!^0+!","",$r); - - if ($r == '') - $r = '0'; - $value = bmmod($value, $mod); - $erb = strrev($r); - $q = '1'; - $a[0] = $value; - - for ($i = 1; $i < strlen($erb); $i++) { - $a[$i] = bmmod( bmmul($a[$i-1], $a[$i-1]), $mod ); - } - - for ($i = 0; $i < strlen($erb); $i++) { - if ($erb[$i] == "1") { - $q = bmmod( bmmul($q, $a[$i]), $mod ); - } - } - - return($q); -} - -/** - * Create a big math subtraction function - * @param string $l - * @param string $r - * @return string - * @url http://www.icosaedro.it/bigint Inspired by - */ -function bmsub($l, $r) { - if (function_exists('bcsub')) - return bcsub($l, $r); - - global $profile; - if ($profile['use_gmp']) - return gmp_strval(gmp_sub($l, $r)); - - - $l = strval($l); $r = strval($r); - $ll = strlen($l); $rl = strlen($r); - - if ($ll < $rl) { - $l = str_repeat("0", $rl-$ll) . $l; - $o = $rl; - } elseif ( $ll > $rl ) { - $r = str_repeat("0", $ll-$rl) . (string)$r; - $o = $ll; - } else { - $o = $ll; - } - - if (strcmp($l, $r) >= 0) { - $sign = ''; - } else { - $x = $l; $l = $r; $r = $x; - $sign = '-'; - } - - $v = ''; - $carry = 0; - - for ($i = $o-1; $i >= 0; $i--) { - $d = ($l[$i] - $r[$i]) - $carry; - if ($d < 0) { - $carry = 1; - $d += 10; - } else { - $carry = 0; - } - $v = (string) $d . $v; - } - - return $sign . ltrim($v, '0'); -} - - -/** - * Get a binary value - * @param integer $n - * @return string - * @url http://openidenabled.com Borrowed from PHP-OpenID - */ -function bin ($n) { - $bytes = array(); - while (bmcomp($n, 0) > 0) { - array_unshift($bytes, bmmod($n, 256)); - $n = bmdiv($n, bmpow(2,8)); - } - - if ($bytes && ($bytes[0] > 127)) - array_unshift($bytes, 0); - - $b = ''; - foreach ($bytes as $byte) - $b .= pack('C', $byte); - - return $b; -} - - -/** - * Debug logging - * @param mixed $x - * @param string $m - */ -function debug ($x, $m = null) { - global $profile; - - if (! isset($profile['debug']) || $profile['debug'] === false) - return true; - - if (! is_writable(dirname($profile['logfile'])) &! is_writable($profile['logfile'])) - error_500('Cannot write to debug log: ' . $profile['logfile']); - - if (is_array($x)) { - ob_start(); - print_r($x); - $x = $m . ($m != null ? "\n" : '') . ob_get_clean(); - - } else { - $x .= "\n"; - } -} - - -/** - * Destroy a consumer's assoc handle - * @param string $id - */ -function destroy_assoc_handle ( $id ) { - debug("Destroying session: $id"); - - $sid = session_id(); - session_write_close(); - - session_id($id); - if (OCP\Config::getSystemValue( "forcessl", false )) { - ini_set("session.cookie_secure", "on"); - } - session_start(); - session_destroy(); - - session_id($sid); - session_start(); -} - - -/** - * Return an error message to the user - * @param string $message - */ -function error_400 ( $message = 'Bad Request' ) { - header("HTTP/1.1 400 Bad Request"); - wrap_html($message); -} - - -/** - * Return an error message to the user - * @param string $message - */ -function error_403 ( $message = 'Forbidden' ) { - header("HTTP/1.1 403 Forbidden"); - wrap_html($message); -} - - -/** - * Return an error message to the user - * @param string $message - */ -function error_500 ( $message = 'Internal Server Error' ) { - header("HTTP/1.1 500 Internal Server Error"); - wrap_html($message); -} - - -/** - * Return an error message to the consumer - * @param string $message - */ -function error_get ( $url, $message = 'Bad Request') { - wrap_keyed_redirect($url, array('mode' => 'error', 'error' => $message)); -} - - -/** - * Return an error message to the consumer - * @param string $message - */ -function error_post ( $message = 'Bad Request' ) { - header("HTTP/1.1 400 Bad Request"); - echo ('error:' . $message); - exit(0); -} - - -/** - * Do an HMAC - * @param string $key - * @param string $data - * @param string $hash - * @return string - * @url http://php.net/manual/en/function.sha1.php#39492 Borrowed from - */ -function hmac($key, $data, $hash = 'sha1_20') { - $blocksize=64; - - if (strlen($key) > $blocksize) - $key = $hash($key); - - $key = str_pad($key, $blocksize,chr(0x00)); - $ipad = str_repeat(chr(0x36),$blocksize); - $opad = str_repeat(chr(0x5c),$blocksize); - - $h1 = $hash(($key ^ $ipad) . $data); - $hmac = $hash(($key ^ $opad) . $h1); - return $hmac; -} - - -if (! function_exists('http_build_query')) { -/** - * Create function if missing - * @param array $array - * @return string - */ -function http_build_query ($array) { - $r = array(); - foreach ($array as $key => $val) - $r[] = sprintf('%s=%s', urlencode($key), urlencode($val)); - return implode('&', $r); -}} - - -/** - * Turn a binary back into a long - * @param string $b - * @return integer - * @url http://openidenabled.com Borrowed from PHP-OpenID - */ -function long($b) { - $bytes = array_merge(unpack('C*', $b)); - $n = 0; - foreach ($bytes as $byte) { - $n = bmmul($n, bmpow(2,8)); - $n = bmadd($n, $byte); - } - return $n; -} - - -/** - * Create a new consumer association - * @param integer $expiration - * @return array - */ -function new_assoc ( $expiration ) { - if (isset($_SESSION) && is_array($_SESSION)) { - $sid = session_id(); - $dat = session_encode(); - session_write_close(); - } - - if (OCP\Config::getSystemValue( "forcessl", false )) { - ini_set("session.cookie_secure", "on"); - } - session_start(); - session_regenerate_id('false'); - - $id = session_id(); - $shared_secret = new_secret(); - debug('Started new assoc session: ' . $id); - - $_SESSION = array(); - $_SESSION['expiration'] = $expiration; - $_SESSION['shared_secret'] = base64_encode($shared_secret); - - session_write_close(); - - if (isset($sid)) { - session_id($sid); - session_start(); - $_SESSION = array(); - session_decode($dat); - } - - return array($id, $shared_secret); -} - - -/** - * Create a new shared secret - * @return string - */ -function new_secret () { - $r = ''; - for($i=0; $i<20; $i++) - $r .= chr(mt_rand(0, 255)); - - debug("Generated new key: hash = '" . md5($r) . "', length = '" . strlen($r) . "'"); - return $r; -} - - -/** - * Random number generation - * @param integer max - * @return integer - */ -function random ( $max ) { - if (strlen($max) < 4) - return mt_rand(1, $max - 1); - - $r = ''; - for($i=1; $i<strlen($max) - 1; $i++) - $r .= mt_rand(0,9); - $r .= mt_rand(1,9); - - return $r; -} - -/** - * Get the shared secret and expiration time for the specified assoc_handle - * @param string $handle assoc_handle to look up - * @return array (shared_secret, expiration_time) - */ -function secret ( $handle ) { - if (! preg_match('/^\w+$/', $handle)) - return array(false, 0); - - if (isset($_SESSION) && is_array($_SESSION)) { - $sid = session_id(); - $dat = session_encode(); - session_write_close(); - } - - session_id($handle); - if (OCP\Config::getSystemValue( "forcessl", false )) { - ini_set("session.cookie_secure", "on"); - } - session_start(); - debug('Started session to acquire key: ' . session_id()); - - $secret = isset($_SESSION['shared_secret']) - ? base64_decode($_SESSION['shared_secret']) - : false; - - $expiration = isset($_SESSION['expiration']) - ? $_SESSION['expiration'] - : null; - - session_write_close(); - - if (isset($sid)) { - session_id($sid); - session_start(); - $_SESSION = array(); - session_decode($dat); - } - - debug("Found key: hash = '" . md5($secret) . "', length = '" . strlen($secret) . "', expiration = '$expiration'"); - return array($secret, $expiration); -} - - -/** - * Do an internal self check - * @global array $profile - * @global array $sreg - */ -function self_check () { - global $profile, $sreg; - -// if (! isset($profile) || ! is_array($profile)) -// error_500('No configuration found, you shouldn\'t access this file directly.'); - - if (version_compare(phpversion(), '4.2.0', 'lt')) - error_500('The required minimum version of PHP is 4.2.0, you are running ' . phpversion()); - - $extension_r = array('session', 'pcre'); - foreach ($extension_r as $x) { - if (! extension_loaded($x)) - @dl($x); - if (! extension_loaded($x)) - error_500("Required extension '$x' is missing."); - } - -// $extension_b = array('suhosin'); -// foreach ($extension_b as $x) { -// if (extension_loaded($x) &! $profile["allow_$x"]) -// error_500("phpMyID is not compatible with '$x'"); -// } -// -// $keys = array('auth_username', 'auth_password'); -// foreach ($keys as $key) { -// if (! array_key_exists($key, $profile)) -// error_500("'$key' is missing from your profile."); -// } - - if (! isset($sreg) || ! is_array($sreg)) - $sreg = array(); -} - - -/** - * Do SHA1 20 byte encryption - * @param string $v - * @return string - * @url http://openidenabled.com Borrowed from PHP-OpenID - */ -function sha1_20 ($v) { - if (version_compare(phpversion(), '5.0.0', 'ge')) - return sha1($v, true); - - $hex = sha1($v); - $r = ''; - for ($i = 0; $i < 40; $i += 2) { - $hexcode = substr($hex, $i, 2); - $charcode = base_convert($hexcode, 16, 10); - $r .= chr($charcode); - } - return $r; -} - - -/** - * Look for the point of differentiation in two strings - * @param string $a - * @param string $b - * @return int - */ -function str_diff_at ($a, $b) { - if ($a == $b) - return -1; - $n = min(strlen($a), strlen($b)); - for ($i = 0; $i < $n; $i++) - if ($a[$i] != $b[$i]) - return $i; - return $n; -} - -/** - * Determine if a child URL actually decends from the parent, and that the - * parent is a good URL. - * THIS IS EXPERIMENTAL - * @param string $parent - * @param string $child - * @return bool - */ -function url_descends ( $child, $parent ) { - if ($child == $parent) - return true; - - $keys = array(); - $parts = array(); - - $req = array('scheme', 'host'); - $bad = array('fragment', 'pass', 'user'); - - foreach (array('parent', 'child') as $name) { - $parts[$name] = @parse_url($$name); - if ($parts[$name] === false) - return false; - - $keys[$name] = array_keys($parts[$name]); - - if (array_intersect($keys[$name], $req) != $req) - return false; - - if (array_intersect($keys[$name], $bad) != array()) - return false; - - if (! preg_match('/^https?$/i', strtolower($parts[$name]['scheme']))) - return false; - - if (! array_key_exists('port', $parts[$name])) - $parts[$name]['port'] = (strtolower($parts[$name]['scheme']) == 'https') ? 443 : 80; - - if (! array_key_exists('path', $parts[$name])) - $parts[$name]['path'] = '/'; - } - - // port and scheme must match - if ($parts['parent']['scheme'] != $parts['child']['scheme'] || - $parts['parent']['port'] != $parts['child']['port']) - return false; - - // compare the hosts by reversing the strings - $cr_host = strtolower(strrev($parts['child']['host'])); - $pr_host = strtolower(strrev($parts['parent']['host'])); - - $break = str_diff_at($cr_host, $pr_host); - if ($break >= 0 && ($pr_host[$break] != '*' || substr_count(substr($pr_host, 0, $break), '.') < 2)) - return false; - - // now compare the paths - $break = str_diff_at($parts['child']['path'], $parts['parent']['path']); - if ($break >= 0 - && ($break < strlen($parts['parent']['path']) && $parts['parent']['path'][$break] != '*') - || ($break > strlen($parts['child']['path']))) - return false; - - return true; -} - - -/** - * Create a user session - * @global array $profile - * @global array $proto - */ -function user_session () { - global $proto, $profile; - - session_name('phpMyID_Server'); - if (OCP\Config::getSystemValue( "forcessl", false )) { - ini_set("session.cookie_secure", "on"); - } - @session_start(); - - $profile['authorized'] = (isset($_SESSION['auth_username']) - && $_SESSION['auth_username'] == $profile['auth_username']) - ? true - : false; - - debug('Started user session: ' . session_id() . ' Auth? ' . $profile['authorized']); -} - - -/** - * Return HTML - * @global string $charset - * @param string $message - */ -function wrap_html ( $message ) { - global $charset, $profile; - header('Content-Type: text/html; charset=' . $charset); - $html= '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html> -<head> -<title>phpMyID</title> -<link rel="openid.server" href="' . $profile['req_url'] . '" /> -<link rel="openid.delegate" href="' . $profile['idp_url'] . '" /> -' . implode("\n", $profile['opt_headers']) . ' -<meta name="charset" content="' . $charset . '" /> -<meta name="robots" content="noindex,nofollow" /> -</head> -<body> -<p>' . $message . '</p> -</body> -</html> -'; - echo $html; - exit(0); -} - - -/** - * Return a key-value pair in plain text - * @global string $charset - * @param array $keys - */ -function wrap_kv ( $keys ) { - global $charset; - - debug($keys, 'Wrapped key/vals'); - header('Content-Type: text/plain; charset=' . $charset); - foreach ($keys as $key => $value) - printf("%s:%s\n", $key, $value); - - exit(0); -} - - -/** - * Redirect, with OpenID keys - * @param string $url - * @param array @keys - */ -function wrap_keyed_redirect ($url, $keys) { - $keys = append_openid($keys); - debug($keys, 'Location keys'); - - $q = strpos($url, '?') ? '&' : '?'; - wrap_redirect($url . $q . http_build_query($keys)); -} - - -/** - * Redirect the browser - * @global string $charset - * @param string $url - */ -function wrap_redirect ($url) { - header('HTTP/1.1 302 Found'); - header('Location: ' . $url); - debug('Location: ' . $url); - exit(0); -} - -/** - * Return an HTML refresh - * @global string $charset - * @param string $url - */ -function wrap_refresh ($url) { - global $charset; - - header('Content-Type: text/html; charset=' . $charset); - echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html> -<head> -<title>phpMyID</title> -<meta http-equiv="refresh" content="0;url=' . $url . '"> -</head> -<body> -<p>Redirecting to <a href="' . $url . '">' . $url . '</a></p> -</body> -</html> -'; - - debug('Refresh: ' . $url); - exit(0); -} - - -/** - * Implement binary x_or - * @param string $a - * @param string $b - * @return string - */ -function x_or ($a, $b) { - $r = ""; - - for ($i = 0; $i < strlen($b); $i++) - $r .= $a[$i] ^ $b[$i]; - debug("Xor size: " . strlen($r)); - return $r; -} - - - -/* - * App Initialization - */ -// Determine the charset to use -$GLOBALS['charset'] = 'iso-8859-1'; - -// Set the internal encoding -if (function_exists('mb_internal_encoding')) - mb_internal_encoding($charset); - -// Avoid problems with non-default arg_separator.output settings -// Credit for this goes to user 'prelog' on the forums -ini_set('arg_separator.output', '&'); - -// Do a check to be sure everything is set up correctly -self_check(); - - -/** - * Determine the HTTP request port - * @name $port - * @global integer $GLOBALS['port'] - */ -$GLOBALS['port'] = ((isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on' && $_SERVER['SERVER_PORT'] == 443) - || $_SERVER['SERVER_PORT'] == 80) - ? '' - : ':' . $_SERVER['SERVER_PORT']; - - -/** - * Determine the HTTP request protocol - * @name $proto - * @global string $GLOBALS['proto'] - */ -$GLOBALS['proto'] = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http'; - -// Set the authorization state - DO NOT OVERRIDE -$profile['authorized'] = false; - -global $IDENTITY; -global $USERNAME; - -// Set a default IDP URL -if (! array_key_exists('idp_url', $profile)) - $profile['idp_url'] = $IDENTITY; - -//Determine the requested URL - DO NOT OVERRIDE -$profile['req_url'] = sprintf("%s://%s%s", - $proto, - OCP\Util::getServerHost(), -// $port,//host already includes the path - $_SERVER["REQUEST_URI"]); - - -// Set the default allowance for testing -if (! array_key_exists('allow_test', $profile)) - $profile['allow_test'] = false; - -// Set the default allowance for gmp -if (! array_key_exists('allow_gmp', $profile)) - $profile['allow_gmp'] = false; - -// Set the default force bigmath - BAD IDEA to override this -if (! array_key_exists('force_bigmath', $profile)) - $profile['force_bigmath'] = false; - -// Determine if GMP is usable -$profile['use_gmp'] = (extension_loaded('gmp') && $profile['allow_gmp']) ? true : false; - -// Determine if I can perform big math functions -$profile['use_bigmath'] = (extension_loaded('bcmath') || $profile['use_gmp'] || $profile['force_bigmath']) ? true : false; - -// Set a default authentication domain -if (! array_key_exists('auth_domain', $profile)) - $profile['auth_domain'] = $profile['req_url'] . ' ' . $profile['idp_url']; - -// Set a default authentication realm -if (! array_key_exists('auth_realm', $profile)) - $profile['auth_realm'] = 'ownCloud'; - -// Determine the realm for digest authentication - DO NOT OVERRIDE -$profile['php_realm'] = $profile['auth_realm'] . (ini_get('safe_mode') ? '-' . getmyuid() : ''); - -// Set a default lifetime - the lesser of GC and cache time -if (! array_key_exists('lifetime', $profile)) { - $sce = session_cache_expire() * 60; - $gcm = ini_get('session.gc_maxlifetime'); - $profile['lifetime'] = $sce < $gcm ? $sce : $gcm; -} - -// Set a default log file -if (! array_key_exists('logfile', $profile)) - $profile['logfile'] = get_temp_dir() . DIRECTORY_SEPARATOR . $profile['auth_realm'] . '.debug.log'; - - -/* - * Optional Initialization - */ -// Setup optional headers -$profile['opt_headers'] = array(); - -// Determine if I should add microid stuff -if (array_key_exists('microid', $profile)) { - $hash = sha1($profile['idp_url']); - $values = is_array($profile['microid']) ? $profile['microid'] : array($profile['microid']); - - foreach ($values as $microid) { - preg_match('/^([a-z]+)/i', $microid, $mtx); - $profile['opt_headers'][] = sprintf('<meta name="microid" content="%s+%s:sha1:%s" />', $mtx[1], $proto, sha1(sha1($microid) . $hash)); - } -} - -// Determine if I should add pavatar stuff -if (array_key_exists('pavatar', $profile)) - $profile['opt_headers'][] = sprintf('<link rel="pavatar" href="%s" />', $profile['pavatar']); - - -/* - * Do it - */ -// Decide which runmode, based on user request or default -$run_mode = (isset($_REQUEST['openid_mode']) - && in_array($_REQUEST['openid_mode'], $known['openid_modes'])) - ? $_REQUEST['openid_mode'] - : 'no'; - -// Run in the determined runmode -debug("Run mode: $run_mode at: " . time()); -debug($_REQUEST, 'Request params'); -call_user_func($run_mode . '_mode'); diff --git a/apps/user_openid/user.php b/apps/user_openid/user.php index d25b95259e0..88571ba618e 100644 --- a/apps/user_openid/user.php +++ b/apps/user_openid/user.php @@ -44,4 +44,4 @@ if(!OCP\User::userExists($USERNAME)){ } $IDENTITY=OCP\Util::linkToAbsolute( "user_openid", "user.php" ).'/'.$USERNAME; -require_once 'phpmyid.php'; +require_once 'openid/phpmyid.php'; diff --git a/apps/user_openid/user_openid.php b/apps/user_openid/user_openid.php index 70b193a30b1..19f2f719b06 100644 --- a/apps/user_openid/user_openid.php +++ b/apps/user_openid/user_openid.php @@ -21,7 +21,7 @@ * */ -require_once('class.openid.v3.php'); +require_once('openid/class.openid.v3.php'); /** * Class for user OpenId backend |