-
t('Importing calendar'); ?>
-
-
-
t('Calendar imported successfully'); ?>
-
+
\ 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 be924b5db4..0000000000
--- a/apps/contacts/ajax/loadphoto.php
+++ /dev/null
@@ -1,46 +0,0 @@
-
- *
- * 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
.
- *
- */
-
-// 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 927e730980..ddae27da21 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 337f51839d..4c6c8bf3d9 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 = $('
'+Contacts.UI.Card.fn+' ');
- 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);
- if($('.contacts li').length > 0) { // Load first in list.
+ 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) {
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('
');
$('#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 + '
' + adrarray[2].strip_tags() + ' ';
}
- 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 + '
' + adrarray[5].strip_tags() + ' ' + adrarray[3].strip_tags() + ' ';
}
if(adrarray[4] && adrarray[4].length > 0) {
@@ -965,7 +949,7 @@ Contacts={
if(adrarray[6] && adrarray[6].length > 0) {
adrtxt = adrtxt + '
' + adrarray[6].strip_tags() + ' ';
}
- $('#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 + '
' + adr[6] + ' ';
}
- $(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 577ad10306..5bca0ab723 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 2a5478e6c4..820104b777 100644
--- a/apps/contacts/l10n/it.php
+++ b/apps/contacts/l10n/it.php
@@ -1,10 +1,13 @@
"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 0000000000..5713ede7b0
--- /dev/null
+++ b/apps/contacts/l10n/vi.php
@@ -0,0 +1,48 @@
+ "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 1bc4a19553..b2dde12684 100644
--- a/apps/contacts/templates/index.php
+++ b/apps/contacts/templates/index.php
@@ -32,6 +32,38 @@
echo $this->inc('part.no_contacts');
}
?>
+
+
+
+
+
t('Keyboard shortcuts'); ?>
+
+
t('Navigation'); ?>
+
+ j/Down/Space
+ t('Next contact in list'); ?>
+ k/Up/Shift-Space
+ t('Previous contact in list'); ?>
+ o/Enter
+ t('Expand/collapse current addressbook'); ?>
+ n/p
+ t('Next/previous addressbook'); ?>
+
+
+
+
t('Actions'); ?>
+
+ r
+ t('Refresh contacts list'); ?>
+ a
+ t('Add new contact'); ?>
+ Shift-a
+ t('Add new addressbook'); ?>
+ Shift-Delete
+ t('Delete current contact'); ?>
+
+
+
diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php
index 7236deb65c..cc9208ad08 100644
--- a/apps/files/ajax/newfile.php
+++ b/apps/files/ajax/newfile.php
@@ -1,16 +1,25 @@
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 6fcf97688c..eef3885851 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 f9953ba4de..5514aed197 100644
--- a/apps/files/appinfo/update.php
+++ b/apps/files/appinfo/update.php
@@ -1,5 +1,16 @@
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 9c1218c201..1b87bcd0b0 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 86c5185bf7..a4e2361fee 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 67bfb4702e..506218815b 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 82871826c1..0bf113eba1 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 0000000000..c2284d5feb
--- /dev/null
+++ b/apps/files/l10n/vi.php
@@ -0,0 +1,31 @@
+ "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 a1db7b6198..e5493cd939 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 e5ee2ae359..260f2c2d46 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("
")[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('
The requested content cannot be loaded. Please try again later.
');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('
').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='
';V="";B.each(H.swf,function(ac,ad){aa+=' ';V+=" "+ac+'="'+ad+'"'});aa+='";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('
');H.width=L.width();H.height=L.height();E()},F=function(){H.width=o.width;H.height=o.height;B("
").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'
'}return'
'+V+"
"}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('
').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
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&&V1){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('
'),T=B(''),Q=B('
'),M=B('
'));d=B('
').append('
').appendTo(M);d.append(m=B('
'),J=B(' '),A=B('
'),O=B(' '),z=B(' '));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('').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("
")[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('The requested content cannot be loaded. Please try again later.
');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('
').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=' ';V="";B.each(H.swf,function(ac,ad){aa+=' ';V+=" "+ac+'="'+ad+'"'});aa+=' ";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('
');H.width=L.width();H.height=L.height();E()},F=function(){H.width=o.width;H.height=o.height;B(" ").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''}return''+V+"
"}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('').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;Wj.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&&V1){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('
'),T=B(''),Q=B('
'),M=B('
'));d=B('
').append('
').appendTo(M);d.append(m=B('
'),J=B(' '),A=B('
'),O=B(' '),z=B(' '));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('').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 32fd212442..fc0d272b54 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 6c3d9fc738..cd580cf303 100644
--- a/apps/gallery/l10n/de.php
+++ b/apps/gallery/l10n/de.php
@@ -1,9 +1,9 @@
"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 03e8d6a456..aa425a0bd0 100644
--- a/apps/gallery/l10n/es.php
+++ b/apps/gallery/l10n/es.php
@@ -1,9 +1,9 @@
"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 e21a1d6524..ef8d596e7e 100644
--- a/apps/gallery/l10n/it.php
+++ b/apps/gallery/l10n/it.php
@@ -1,9 +1,9 @@
"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 0000000000..d1d7fc64fc
--- /dev/null
+++ b/apps/gallery/l10n/vi.php
@@ -0,0 +1,11 @@
+ "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 0000000000..01942ba173
--- /dev/null
+++ b/apps/media/l10n/vi.php
@@ -0,0 +1,14 @@
+ "Â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 330574c1d4..3c6da47d71 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 d438c7d84d..b9f4bdf199 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 0000000000..19122b34c7
--- /dev/null
+++ b/apps/user_ldap/lib/access.php
@@ -0,0 +1,597 @@
+.
+ *
+ */
+
+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 0000000000..e54a8e2b24
--- /dev/null
+++ b/apps/user_ldap/lib/connection.php
@@ -0,0 +1,255 @@
+.
+ *
+ */
+
+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 08b09304d7..0000000000
--- a/apps/user_ldap/lib_ldap.php
+++ /dev/null
@@ -1,721 +0,0 @@
-.
- *
- */
-
-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 2be6b46fb2..106459580f 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 b51d9a55cc..a4a8921d08 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 1718abe9e0..bce5fb2d7c 100644
--- a/apps/user_migrate/templates/settings.php
+++ b/apps/user_migrate/templates/settings.php
@@ -12,7 +12,7 @@
t('Import user account');?>
- t('ownCloud User Zip');?>
+
t('ownCloud User Zip');?>
diff --git a/apps/user_openid/user.php b/apps/user_openid/user.php
index d25b95259e..88571ba618 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 70b193a30b..19f2f719b0 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
diff --git a/core/ajax/appconfig.php b/core/ajax/appconfig.php
index f815d71063..84e0710c74 100644
--- a/core/ajax/appconfig.php
+++ b/core/ajax/appconfig.php
@@ -6,7 +6,8 @@
*/
require_once ("../../lib/base.php");
-OC_JSON::checkLoggedIn();
+OC_Util::checkAdminUser();
+
$action=isset($_POST['action'])?$_POST['action']:$_GET['action'];
$result=false;
switch($action){
diff --git a/core/ajax/userlist.php b/core/ajax/userlist.php
deleted file mode 100644
index 85ca004ae6..0000000000
--- a/core/ajax/userlist.php
+++ /dev/null
@@ -1,46 +0,0 @@
-.
-*
-*/
-
-$RUNTIME_NOAPPS = TRUE; //no apps, yet
-require_once('../../lib/base.php');
-
-if(!OC_User::isLoggedIn()){
- if(!isset($_SERVER['PHP_AUTH_USER'])){
- header('WWW-Authenticate: Basic realm="ownCloud Server"');
- header('HTTP/1.0 401 Unauthorized');
- echo 'Valid credentials must be supplied';
- exit();
- } else {
- if(!OC_User::checkPassword($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])){
- exit();
- }
- }
-}
-
-$users = array();
-
-foreach( OC_User::getUsers() as $i ){
- $users[] = array( "username" => $i, "groups" => join( ", ", OC_Group::getUserGroups( $i ) ));
-}
-
-OC_JSON::encodedPrint($users);
diff --git a/core/ajax/validateuser.php b/core/ajax/validateuser.php
deleted file mode 100644
index 78ec451fac..0000000000
--- a/core/ajax/validateuser.php
+++ /dev/null
@@ -1,38 +0,0 @@
-.
-*
-*/
-
-$RUNTIME_NOAPPS = TRUE; //no apps, yet
-require_once('../../lib/base.php');
-
-if(!isset($_SERVER['PHP_AUTH_USER'])){
- header('WWW-Authenticate: Basic realm="ownCloud Server"');
- header('HTTP/1.0 401 Unauthorized');
- echo 'Valid credentials must be supplied';
- exit();
-} else {
- if(OC_User::checkPassword($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"])){
- OC_JSON::encodedPrint(array("username" => $_SERVER["PHP_AUTH_USER"], "user_valid" => "true"));
- } else {
- OC_JSON::encodedPrint(array("username" => $_SERVER["PHP_AUTH_USER"], "user_valid" => "false"));
- }
-}
diff --git a/core/js/eventsource.js b/core/js/eventsource.js
index 08259e02ca..e3ad7e3a67 100644
--- a/core/js/eventsource.js
+++ b/core/js/eventsource.js
@@ -40,6 +40,7 @@ OC.EventSource=function(src,data){
dataStr+=name+'='+encodeURIComponent(data[name])+'&';
}
}
+ dataStr+='requesttoken='+OC.EventSource.requesttoken;
if(!this.useFallBack && typeof EventSource !='undefined'){
this.source=new EventSource(src+'?'+dataStr);
this.source.onmessage=function(e){
diff --git a/core/l10n/vi.php b/core/l10n/vi.php
new file mode 100644
index 0000000000..b0b750303a
--- /dev/null
+++ b/core/l10n/vi.php
@@ -0,0 +1,64 @@
+ "Tên ứng dụng không tồn tại",
+"No category to add?" => "Không có danh mục được thêm?",
+"This category already exists: " => "Danh mục này đã được tạo :",
+"ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=" => "ui-datepicker-group';if(i[1]>1)switch(G){case 0:y+=",
+"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",
+"Cancel" => "Hủy",
+"No" => "No",
+"Yes" => "Yes",
+"Ok" => "Ok",
+"No categories selected for deletion." => "Không có thể loại nào được chọn để xóa.",
+"Error" => "Lỗi",
+"ownCloud password reset" => "Khôi phục mật khẩu Owncloud ",
+"Use the following link to reset your password: {link}" => "Dùng đường dẫn sau để khôi phục lại mật khẩu : {link}",
+"You will receive a link to reset your password via Email." => "Vui lòng kiểm tra Email để khôi phục lại mật khẩu.",
+"Requested" => "Yêu cầu",
+"Login failed!" => "Bạn đã nhập sai mật khẩu hay tên người dùng !",
+"Username" => "Tên người dùng",
+"Request reset" => "Yêu cầu thiết lập lại ",
+"Your password was reset" => "Mật khẩu của bạn đã được khôi phục",
+"To login page" => "Trang đăng nhập",
+"New password" => "Mật khẩu mới",
+"Reset password" => "Khôi phục mật khẩu",
+"Personal" => "Cá nhân",
+"Users" => "Người sử dụng",
+"Apps" => "Ứng dụng",
+"Admin" => "Quản trị",
+"Help" => "Giúp đỡ",
+"Access forbidden" => "Truy cập bị cấm ",
+"Cloud not found" => "Không tìm thấy Clound",
+"Edit categories" => "Sửa thể loại",
+"Add" => "Thêm",
+"Create an admin account " => "Tạo một tài khoản quản trị ",
+"Password" => "Mật khẩu",
+"Advanced" => "Nâng cao",
+"Data folder" => "Thư mục dữ liệu",
+"Configure the database" => "Cấu hình Cơ Sở Dữ Liệu",
+"will be used" => "được sử dụng",
+"Database user" => "Người dùng cơ sở dữ liệu",
+"Database password" => "Mật khẩu cơ sở dữ liệu",
+"Database name" => "Tên cơ sở dữ liệu",
+"Database host" => "Database host",
+"Finish setup" => "Cài đặt hoàn tất",
+"web services under your control" => "các dịch vụ web dưới sự kiểm soát của bạn",
+"Log out" => "Đăng xuất",
+"Settings" => "Cài đặt",
+"Lost your password?" => "Bạn quên mật khẩu ?",
+"remember" => "Nhớ",
+"Log in" => "Đăng nhập",
+"You are logged out." => "Bạn đã đăng xuất.",
+"prev" => "Lùi lại",
+"next" => "Kế tiếp"
+);
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 7e98fdedc2..dc303ffc1a 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -33,6 +33,7 @@