"
+ },
+
+ _create: function() {
+ this._tabify( true );
+ },
+
+ _setOption: function( key, value ) {
+ if ( key == "selected" ) {
+ if (this.options.collapsible && value == this.options.selected ) {
+ return;
+ }
+ this.select( value );
+ } else {
+ this.options[ key ] = value;
+ this._tabify();
+ }
+ },
+
+ _tabId: function( a ) {
+ return a.title && a.title.replace( /\s/g, "_" ).replace( /[^\w\u00c0-\uFFFF-]/g, "" ) ||
+ this.options.idPrefix + getNextTabId();
+ },
+
+ _sanitizeSelector: function( hash ) {
+ // we need this because an id may contain a ":"
+ return hash.replace( /:/g, "\\:" );
+ },
+
+ _cookie: function() {
+ var cookie = this.cookie ||
+ ( this.cookie = this.options.cookie.name || "ui-tabs-" + getNextListId() );
+ return $.cookie.apply( null, [ cookie ].concat( $.makeArray( arguments ) ) );
+ },
+
+ _ui: function( tab, panel ) {
+ return {
+ tab: tab,
+ panel: panel,
+ index: this.anchors.index( tab )
+ };
+ },
+
+ _cleanup: function() {
+ // restore all former loading tabs labels
+ this.lis.filter( ".ui-state-processing" )
+ .removeClass( "ui-state-processing" )
+ .find( "span:data(label.tabs)" )
+ .each(function() {
+ var el = $( this );
+ el.html( el.data( "label.tabs" ) ).removeData( "label.tabs" );
+ });
+ },
+
+ _tabify: function( init ) {
+ var self = this,
+ o = this.options,
+ fragmentId = /^#.+/; // Safari 2 reports '#' for an empty hash
+
+ this.list = this.element.find( "ol,ul" ).eq( 0 );
+ this.lis = $( " > li:has(a[href])", this.list );
+ this.anchors = this.lis.map(function() {
+ return $( "a", this )[ 0 ];
+ });
+ this.panels = $( [] );
+
+ this.anchors.each(function( i, a ) {
+ var href = $( a ).attr( "href" );
+ // For dynamically created HTML that contains a hash as href IE < 8 expands
+ // such href to the full page url with hash and then misinterprets tab as ajax.
+ // Same consideration applies for an added tab with a fragment identifier
+ // since a[href=#fragment-identifier] does unexpectedly not match.
+ // Thus normalize href attribute...
+ var hrefBase = href.split( "#" )[ 0 ],
+ baseEl;
+ if ( hrefBase && ( hrefBase === location.toString().split( "#" )[ 0 ] ||
+ ( baseEl = $( "base" )[ 0 ]) && hrefBase === baseEl.href ) ) {
+ href = a.hash;
+ a.href = href;
+ }
+
+ // inline tab
+ if ( fragmentId.test( href ) ) {
+ self.panels = self.panels.add( self.element.find( self._sanitizeSelector( href ) ) );
+ // remote tab
+ // prevent loading the page itself if href is just "#"
+ } else if ( href && href !== "#" ) {
+ // required for restore on destroy
+ $.data( a, "href.tabs", href );
+
+ // TODO until #3808 is fixed strip fragment identifier from url
+ // (IE fails to load from such url)
+ $.data( a, "load.tabs", href.replace( /#.*$/, "" ) );
+
+ var id = self._tabId( a );
+ a.href = "#" + id;
+ var $panel = self.element.find( "#" + id );
+ if ( !$panel.length ) {
+ $panel = $( o.panelTemplate )
+ .attr( "id", id )
+ .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
+ .insertAfter( self.panels[ i - 1 ] || self.list );
+ $panel.data( "destroy.tabs", true );
+ }
+ self.panels = self.panels.add( $panel );
+ // invalid tab href
+ } else {
+ o.disabled.push( i );
+ }
+ });
+
+ // initialization from scratch
+ if ( init ) {
+ // attach necessary classes for styling
+ this.element.addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" );
+ this.list.addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
+ this.lis.addClass( "ui-state-default ui-corner-top" );
+ this.panels.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" );
+
+ // Selected tab
+ // use "selected" option or try to retrieve:
+ // 1. from fragment identifier in url
+ // 2. from cookie
+ // 3. from selected class attribute on
+ if ( o.selected === undefined ) {
+ if ( location.hash ) {
+ this.anchors.each(function( i, a ) {
+ if ( a.hash == location.hash ) {
+ o.selected = i;
+ return false;
+ }
+ });
+ }
+ if ( typeof o.selected !== "number" && o.cookie ) {
+ o.selected = parseInt( self._cookie(), 10 );
+ }
+ if ( typeof o.selected !== "number" && this.lis.filter( ".ui-tabs-selected" ).length ) {
+ o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
+ }
+ o.selected = o.selected || ( this.lis.length ? 0 : -1 );
+ } else if ( o.selected === null ) { // usage of null is deprecated, TODO remove in next release
+ o.selected = -1;
+ }
+
+ // sanity check - default to first tab...
+ o.selected = ( ( o.selected >= 0 && this.anchors[ o.selected ] ) || o.selected < 0 )
+ ? o.selected
+ : 0;
+
+ // Take disabling tabs via class attribute from HTML
+ // into account and update option properly.
+ // A selected tab cannot become disabled.
+ o.disabled = $.unique( o.disabled.concat(
+ $.map( this.lis.filter( ".ui-state-disabled" ), function( n, i ) {
+ return self.lis.index( n );
+ })
+ ) ).sort();
+
+ if ( $.inArray( o.selected, o.disabled ) != -1 ) {
+ o.disabled.splice( $.inArray( o.selected, o.disabled ), 1 );
+ }
+
+ // highlight selected tab
+ this.panels.addClass( "ui-tabs-hide" );
+ this.lis.removeClass( "ui-tabs-selected ui-state-active" );
+ // check for length avoids error when initializing empty list
+ if ( o.selected >= 0 && this.anchors.length ) {
+ self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) ).removeClass( "ui-tabs-hide" );
+ this.lis.eq( o.selected ).addClass( "ui-tabs-selected ui-state-active" );
+
+ // seems to be expected behavior that the show callback is fired
+ self.element.queue( "tabs", function() {
+ self._trigger( "show", null,
+ self._ui( self.anchors[ o.selected ], self.element.find( self._sanitizeSelector( self.anchors[ o.selected ].hash ) )[ 0 ] ) );
+ });
+
+ this.load( o.selected );
+ }
+
+ // clean up to avoid memory leaks in certain versions of IE 6
+ // TODO: namespace this event
+ $( window ).bind( "unload", function() {
+ self.lis.add( self.anchors ).unbind( ".tabs" );
+ self.lis = self.anchors = self.panels = null;
+ });
+ // update selected after add/remove
+ } else {
+ o.selected = this.lis.index( this.lis.filter( ".ui-tabs-selected" ) );
+ }
+
+ // update collapsible
+ // TODO: use .toggleClass()
+ this.element[ o.collapsible ? "addClass" : "removeClass" ]( "ui-tabs-collapsible" );
+
+ // set or update cookie after init and add/remove respectively
+ if ( o.cookie ) {
+ this._cookie( o.selected, o.cookie );
+ }
+
+ // disable tabs
+ for ( var i = 0, li; ( li = this.lis[ i ] ); i++ ) {
+ $( li )[ $.inArray( i, o.disabled ) != -1 &&
+ // TODO: use .toggleClass()
+ !$( li ).hasClass( "ui-tabs-selected" ) ? "addClass" : "removeClass" ]( "ui-state-disabled" );
+ }
+
+ // reset cache if switching from cached to not cached
+ if ( o.cache === false ) {
+ this.anchors.removeData( "cache.tabs" );
+ }
+
+ // remove all handlers before, tabify may run on existing tabs after add or option change
+ this.lis.add( this.anchors ).unbind( ".tabs" );
+
+ if ( o.event !== "mouseover" ) {
+ var addState = function( state, el ) {
+ if ( el.is( ":not(.ui-state-disabled)" ) ) {
+ el.addClass( "ui-state-" + state );
+ }
+ };
+ var removeState = function( state, el ) {
+ el.removeClass( "ui-state-" + state );
+ };
+ this.lis.bind( "mouseover.tabs" , function() {
+ addState( "hover", $( this ) );
+ });
+ this.lis.bind( "mouseout.tabs", function() {
+ removeState( "hover", $( this ) );
+ });
+ this.anchors.bind( "focus.tabs", function() {
+ addState( "focus", $( this ).closest( "li" ) );
+ });
+ this.anchors.bind( "blur.tabs", function() {
+ removeState( "focus", $( this ).closest( "li" ) );
+ });
+ }
+
+ // set up animations
+ var hideFx, showFx;
+ if ( o.fx ) {
+ if ( $.isArray( o.fx ) ) {
+ hideFx = o.fx[ 0 ];
+ showFx = o.fx[ 1 ];
+ } else {
+ hideFx = showFx = o.fx;
+ }
+ }
+
+ // Reset certain styles left over from animation
+ // and prevent IE's ClearType bug...
+ function resetStyle( $el, fx ) {
+ $el.css( "display", "" );
+ if ( !$.support.opacity && fx.opacity ) {
+ $el[ 0 ].style.removeAttribute( "filter" );
+ }
+ }
+
+ // Show a tab...
+ var showTab = showFx
+ ? function( clicked, $show ) {
+ $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
+ $show.hide().removeClass( "ui-tabs-hide" ) // avoid flicker that way
+ .animate( showFx, showFx.duration || "normal", function() {
+ resetStyle( $show, showFx );
+ self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
+ });
+ }
+ : function( clicked, $show ) {
+ $( clicked ).closest( "li" ).addClass( "ui-tabs-selected ui-state-active" );
+ $show.removeClass( "ui-tabs-hide" );
+ self._trigger( "show", null, self._ui( clicked, $show[ 0 ] ) );
+ };
+
+ // Hide a tab, $show is optional...
+ var hideTab = hideFx
+ ? function( clicked, $hide ) {
+ $hide.animate( hideFx, hideFx.duration || "normal", function() {
+ self.lis.removeClass( "ui-tabs-selected ui-state-active" );
+ $hide.addClass( "ui-tabs-hide" );
+ resetStyle( $hide, hideFx );
+ self.element.dequeue( "tabs" );
+ });
+ }
+ : function( clicked, $hide, $show ) {
+ self.lis.removeClass( "ui-tabs-selected ui-state-active" );
+ $hide.addClass( "ui-tabs-hide" );
+ self.element.dequeue( "tabs" );
+ };
+
+ // attach tab event handler, unbind to avoid duplicates from former tabifying...
+ this.anchors.bind( o.event + ".tabs", function() {
+ var el = this,
+ $li = $(el).closest( "li" ),
+ $hide = self.panels.filter( ":not(.ui-tabs-hide)" ),
+ $show = self.element.find( self._sanitizeSelector( el.hash ) );
+
+ // If tab is already selected and not collapsible or tab disabled or
+ // or is already loading or click callback returns false stop here.
+ // Check if click handler returns false last so that it is not executed
+ // for a disabled or loading tab!
+ if ( ( $li.hasClass( "ui-tabs-selected" ) && !o.collapsible) ||
+ $li.hasClass( "ui-state-disabled" ) ||
+ $li.hasClass( "ui-state-processing" ) ||
+ self.panels.filter( ":animated" ).length ||
+ self._trigger( "select", null, self._ui( this, $show[ 0 ] ) ) === false ) {
+ this.blur();
+ return false;
+ }
+
+ o.selected = self.anchors.index( this );
+
+ self.abort();
+
+ // if tab may be closed
+ if ( o.collapsible ) {
+ if ( $li.hasClass( "ui-tabs-selected" ) ) {
+ o.selected = -1;
+
+ if ( o.cookie ) {
+ self._cookie( o.selected, o.cookie );
+ }
+
+ self.element.queue( "tabs", function() {
+ hideTab( el, $hide );
+ }).dequeue( "tabs" );
+
+ this.blur();
+ return false;
+ } else if ( !$hide.length ) {
+ if ( o.cookie ) {
+ self._cookie( o.selected, o.cookie );
+ }
+
+ self.element.queue( "tabs", function() {
+ showTab( el, $show );
+ });
+
+ // TODO make passing in node possible, see also http://dev.jqueryui.com/ticket/3171
+ self.load( self.anchors.index( this ) );
+
+ this.blur();
+ return false;
+ }
+ }
+
+ if ( o.cookie ) {
+ self._cookie( o.selected, o.cookie );
+ }
+
+ // show new tab
+ if ( $show.length ) {
+ if ( $hide.length ) {
+ self.element.queue( "tabs", function() {
+ hideTab( el, $hide );
+ });
+ }
+ self.element.queue( "tabs", function() {
+ showTab( el, $show );
+ });
+
+ self.load( self.anchors.index( this ) );
+ } else {
+ throw "jQuery UI Tabs: Mismatching fragment identifier.";
+ }
+
+ // Prevent IE from keeping other link focussed when using the back button
+ // and remove dotted border from clicked link. This is controlled via CSS
+ // in modern browsers; blur() removes focus from address bar in Firefox
+ // which can become a usability and annoying problem with tabs('rotate').
+ if ( $.browser.msie ) {
+ this.blur();
+ }
+ });
+
+ // disable click in any case
+ this.anchors.bind( "click.tabs", function(){
+ return false;
+ });
+ },
+
+ _getIndex: function( index ) {
+ // meta-function to give users option to provide a href string instead of a numerical index.
+ // also sanitizes numerical indexes to valid values.
+ if ( typeof index == "string" ) {
+ index = this.anchors.index( this.anchors.filter( "[href$=" + index + "]" ) );
+ }
+
+ return index;
+ },
+
+ destroy: function() {
+ var o = this.options;
+
+ this.abort();
+
+ this.element
+ .unbind( ".tabs" )
+ .removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" )
+ .removeData( "tabs" );
+
+ this.list.removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" );
+
+ this.anchors.each(function() {
+ var href = $.data( this, "href.tabs" );
+ if ( href ) {
+ this.href = href;
+ }
+ var $this = $( this ).unbind( ".tabs" );
+ $.each( [ "href", "load", "cache" ], function( i, prefix ) {
+ $this.removeData( prefix + ".tabs" );
+ });
+ });
+
+ this.lis.unbind( ".tabs" ).add( this.panels ).each(function() {
+ if ( $.data( this, "destroy.tabs" ) ) {
+ $( this ).remove();
+ } else {
+ $( this ).removeClass([
+ "ui-state-default",
+ "ui-corner-top",
+ "ui-tabs-selected",
+ "ui-state-active",
+ "ui-state-hover",
+ "ui-state-focus",
+ "ui-state-disabled",
+ "ui-tabs-panel",
+ "ui-widget-content",
+ "ui-corner-bottom",
+ "ui-tabs-hide"
+ ].join( " " ) );
+ }
+ });
+
+ if ( o.cookie ) {
+ this._cookie( null, o.cookie );
+ }
+
+ return this;
+ },
+
+ add: function( url, label, index ) {
+ if ( index === undefined ) {
+ index = this.anchors.length;
+ }
+
+ var self = this,
+ o = this.options,
+ $li = $( o.tabTemplate.replace( /#\{href\}/g, url ).replace( /#\{label\}/g, label ) ),
+ id = !url.indexOf( "#" ) ? url.replace( "#", "" ) : this._tabId( $( "a", $li )[ 0 ] );
+
+ $li.addClass( "ui-state-default ui-corner-top" ).data( "destroy.tabs", true );
+
+ // try to find an existing element before creating a new one
+ var $panel = self.element.find( "#" + id );
+ if ( !$panel.length ) {
+ $panel = $( o.panelTemplate )
+ .attr( "id", id )
+ .data( "destroy.tabs", true );
+ }
+ $panel.addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide" );
+
+ if ( index >= this.lis.length ) {
+ $li.appendTo( this.list );
+ $panel.appendTo( this.list[ 0 ].parentNode );
+ } else {
+ $li.insertBefore( this.lis[ index ] );
+ $panel.insertBefore( this.panels[ index ] );
+ }
+
+ o.disabled = $.map( o.disabled, function( n, i ) {
+ return n >= index ? ++n : n;
+ });
+
+ this._tabify();
+
+ if ( this.anchors.length == 1 ) {
+ o.selected = 0;
+ $li.addClass( "ui-tabs-selected ui-state-active" );
+ $panel.removeClass( "ui-tabs-hide" );
+ this.element.queue( "tabs", function() {
+ self._trigger( "show", null, self._ui( self.anchors[ 0 ], self.panels[ 0 ] ) );
+ });
+
+ this.load( 0 );
+ }
+
+ this._trigger( "add", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
+ return this;
+ },
+
+ remove: function( index ) {
+ index = this._getIndex( index );
+ var o = this.options,
+ $li = this.lis.eq( index ).remove(),
+ $panel = this.panels.eq( index ).remove();
+
+ // If selected tab was removed focus tab to the right or
+ // in case the last tab was removed the tab to the left.
+ if ( $li.hasClass( "ui-tabs-selected" ) && this.anchors.length > 1) {
+ this.select( index + ( index + 1 < this.anchors.length ? 1 : -1 ) );
+ }
+
+ o.disabled = $.map(
+ $.grep( o.disabled, function(n, i) {
+ return n != index;
+ }),
+ function( n, i ) {
+ return n >= index ? --n : n;
+ });
+
+ this._tabify();
+
+ this._trigger( "remove", null, this._ui( $li.find( "a" )[ 0 ], $panel[ 0 ] ) );
+ return this;
+ },
+
+ enable: function( index ) {
+ index = this._getIndex( index );
+ var o = this.options;
+ if ( $.inArray( index, o.disabled ) == -1 ) {
+ return;
+ }
+
+ this.lis.eq( index ).removeClass( "ui-state-disabled" );
+ o.disabled = $.grep( o.disabled, function( n, i ) {
+ return n != index;
+ });
+
+ this._trigger( "enable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
+ return this;
+ },
+
+ disable: function( index ) {
+ index = this._getIndex( index );
+ var self = this, o = this.options;
+ // cannot disable already selected tab
+ if ( index != o.selected ) {
+ this.lis.eq( index ).addClass( "ui-state-disabled" );
+
+ o.disabled.push( index );
+ o.disabled.sort();
+
+ this._trigger( "disable", null, this._ui( this.anchors[ index ], this.panels[ index ] ) );
+ }
+
+ return this;
+ },
+
+ select: function( index ) {
+ index = this._getIndex( index );
+ if ( index == -1 ) {
+ if ( this.options.collapsible && this.options.selected != -1 ) {
+ index = this.options.selected;
+ } else {
+ return this;
+ }
+ }
+ this.anchors.eq( index ).trigger( this.options.event + ".tabs" );
+ return this;
+ },
+
+ load: function( index ) {
+ index = this._getIndex( index );
+ var self = this,
+ o = this.options,
+ a = this.anchors.eq( index )[ 0 ],
+ url = $.data( a, "load.tabs" );
+
+ this.abort();
+
+ // not remote or from cache
+ if ( !url || this.element.queue( "tabs" ).length !== 0 && $.data( a, "cache.tabs" ) ) {
+ this.element.dequeue( "tabs" );
+ return;
+ }
+
+ // load remote from here on
+ this.lis.eq( index ).addClass( "ui-state-processing" );
+
+ if ( o.spinner ) {
+ var span = $( "span", a );
+ span.data( "label.tabs", span.html() ).html( o.spinner );
+ }
+
+ this.xhr = $.ajax( $.extend( {}, o.ajaxOptions, {
+ url: url,
+ success: function( r, s ) {
+ self.element.find( self._sanitizeSelector( a.hash ) ).html( r );
+
+ // take care of tab labels
+ self._cleanup();
+
+ if ( o.cache ) {
+ $.data( a, "cache.tabs", true );
+ }
+
+ self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
+ try {
+ o.ajaxOptions.success( r, s );
+ }
+ catch ( e ) {}
+ },
+ error: function( xhr, s, e ) {
+ // take care of tab labels
+ self._cleanup();
+
+ self._trigger( "load", null, self._ui( self.anchors[ index ], self.panels[ index ] ) );
+ try {
+ // Passing index avoid a race condition when this method is
+ // called after the user has selected another tab.
+ // Pass the anchor that initiated this request allows
+ // loadError to manipulate the tab content panel via $(a.hash)
+ o.ajaxOptions.error( xhr, s, index, a );
+ }
+ catch ( e ) {}
+ }
+ } ) );
+
+ // last, so that load event is fired before show...
+ self.element.dequeue( "tabs" );
+
+ return this;
+ },
+
+ abort: function() {
+ // stop possibly running animations
+ this.element.queue( [] );
+ this.panels.stop( false, true );
+
+ // "tabs" queue must not contain more than two elements,
+ // which are the callbacks for the latest clicked tab...
+ this.element.queue( "tabs", this.element.queue( "tabs" ).splice( -2, 2 ) );
+
+ // terminate pending requests from other tabs
+ if ( this.xhr ) {
+ this.xhr.abort();
+ delete this.xhr;
+ }
+
+ // take care of tab labels
+ this._cleanup();
+ return this;
+ },
+
+ url: function( index, url ) {
+ this.anchors.eq( index ).removeData( "cache.tabs" ).data( "load.tabs", url );
+ return this;
+ },
+
+ length: function() {
+ return this.anchors.length;
+ }
+});
+
+$.extend( $.ui.tabs, {
+ version: "1.8.10"
+});
+
+/*
+ * Tabs Extensions
+ */
+
+/*
+ * Rotate
+ */
+$.extend( $.ui.tabs.prototype, {
+ rotation: null,
+ rotate: function( ms, continuing ) {
+ var self = this,
+ o = this.options;
+
+ var rotate = self._rotate || ( self._rotate = function( e ) {
+ clearTimeout( self.rotation );
+ self.rotation = setTimeout(function() {
+ var t = o.selected;
+ self.select( ++t < self.anchors.length ? t : 0 );
+ }, ms );
+
+ if ( e ) {
+ e.stopPropagation();
+ }
+ });
+
+ var stop = self._unrotate || ( self._unrotate = !continuing
+ ? function(e) {
+ if (e.clientX) { // in case of a true click
+ self.rotate(null);
+ }
+ }
+ : function( e ) {
+ t = o.selected;
+ rotate();
+ });
+
+ // start rotation
+ if ( ms ) {
+ this.element.bind( "tabsshow", rotate );
+ this.anchors.bind( o.event + ".tabs", stop );
+ rotate();
+ // stop rotation
+ } else {
+ clearTimeout( self.rotation );
+ this.element.unbind( "tabsshow", rotate );
+ this.anchors.unbind( o.event + ".tabs", stop );
+ delete this._rotate;
+ delete this._unrotate;
+ }
+
+ return this;
+ }
+});
+
+})( jQuery );
diff --git a/js/ui/jquery.ui.widget.js b/js/ui/jquery.ui.widget.js
new file mode 100644
index 0000000000..e8f4e9854b
--- /dev/null
+++ b/js/ui/jquery.ui.widget.js
@@ -0,0 +1,262 @@
+/*!
+ * jQuery UI Widget 1.8.10
+ *
+ * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * http://docs.jquery.com/UI/Widget
+ */
+(function( $, undefined ) {
+
+// jQuery 1.4+
+if ( $.cleanData ) {
+ var _cleanData = $.cleanData;
+ $.cleanData = function( elems ) {
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
+ $( elem ).triggerHandler( "remove" );
+ }
+ _cleanData( elems );
+ };
+} else {
+ var _remove = $.fn.remove;
+ $.fn.remove = function( selector, keepData ) {
+ return this.each(function() {
+ if ( !keepData ) {
+ if ( !selector || $.filter( selector, [ this ] ).length ) {
+ $( "*", this ).add( [ this ] ).each(function() {
+ $( this ).triggerHandler( "remove" );
+ });
+ }
+ }
+ return _remove.call( $(this), selector, keepData );
+ });
+ };
+}
+
+$.widget = function( name, base, prototype ) {
+ var namespace = name.split( "." )[ 0 ],
+ fullName;
+ name = name.split( "." )[ 1 ];
+ fullName = namespace + "-" + name;
+
+ if ( !prototype ) {
+ prototype = base;
+ base = $.Widget;
+ }
+
+ // create selector for plugin
+ $.expr[ ":" ][ fullName ] = function( elem ) {
+ return !!$.data( elem, name );
+ };
+
+ $[ namespace ] = $[ namespace ] || {};
+ $[ namespace ][ name ] = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+ };
+
+ var basePrototype = new base();
+ // we need to make the options hash a property directly on the new instance
+ // otherwise we'll modify the options hash on the prototype that we're
+ // inheriting from
+// $.each( basePrototype, function( key, val ) {
+// if ( $.isPlainObject(val) ) {
+// basePrototype[ key ] = $.extend( {}, val );
+// }
+// });
+ basePrototype.options = $.extend( true, {}, basePrototype.options );
+ $[ namespace ][ name ].prototype = $.extend( true, basePrototype, {
+ namespace: namespace,
+ widgetName: name,
+ widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name,
+ widgetBaseClass: fullName
+ }, prototype );
+
+ $.widget.bridge( name, $[ namespace ][ name ] );
+};
+
+$.widget.bridge = function( name, object ) {
+ $.fn[ name ] = function( options ) {
+ var isMethodCall = typeof options === "string",
+ args = Array.prototype.slice.call( arguments, 1 ),
+ returnValue = this;
+
+ // allow multiple hashes to be passed on init
+ options = !isMethodCall && args.length ?
+ $.extend.apply( null, [ true, options ].concat(args) ) :
+ options;
+
+ // prevent calls to internal methods
+ if ( isMethodCall && options.charAt( 0 ) === "_" ) {
+ return returnValue;
+ }
+
+ if ( isMethodCall ) {
+ this.each(function() {
+ var instance = $.data( this, name ),
+ methodValue = instance && $.isFunction( instance[options] ) ?
+ instance[ options ].apply( instance, args ) :
+ instance;
+ // TODO: add this back in 1.9 and use $.error() (see #5972)
+// if ( !instance ) {
+// throw "cannot call methods on " + name + " prior to initialization; " +
+// "attempted to call method '" + options + "'";
+// }
+// if ( !$.isFunction( instance[options] ) ) {
+// throw "no such method '" + options + "' for " + name + " widget instance";
+// }
+// var methodValue = instance[ options ].apply( instance, args );
+ if ( methodValue !== instance && methodValue !== undefined ) {
+ returnValue = methodValue;
+ return false;
+ }
+ });
+ } else {
+ this.each(function() {
+ var instance = $.data( this, name );
+ if ( instance ) {
+ instance.option( options || {} )._init();
+ } else {
+ $.data( this, name, new object( options, this ) );
+ }
+ });
+ }
+
+ return returnValue;
+ };
+};
+
+$.Widget = function( options, element ) {
+ // allow instantiation without initializing for simple inheritance
+ if ( arguments.length ) {
+ this._createWidget( options, element );
+ }
+};
+
+$.Widget.prototype = {
+ widgetName: "widget",
+ widgetEventPrefix: "",
+ options: {
+ disabled: false
+ },
+ _createWidget: function( options, element ) {
+ // $.widget.bridge stores the plugin instance, but we do it anyway
+ // so that it's stored even before the _create function runs
+ $.data( element, this.widgetName, this );
+ this.element = $( element );
+ this.options = $.extend( true, {},
+ this.options,
+ this._getCreateOptions(),
+ options );
+
+ var self = this;
+ this.element.bind( "remove." + this.widgetName, function() {
+ self.destroy();
+ });
+
+ this._create();
+ this._trigger( "create" );
+ this._init();
+ },
+ _getCreateOptions: function() {
+ return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ];
+ },
+ _create: function() {},
+ _init: function() {},
+
+ destroy: function() {
+ this.element
+ .unbind( "." + this.widgetName )
+ .removeData( this.widgetName );
+ this.widget()
+ .unbind( "." + this.widgetName )
+ .removeAttr( "aria-disabled" )
+ .removeClass(
+ this.widgetBaseClass + "-disabled " +
+ "ui-state-disabled" );
+ },
+
+ widget: function() {
+ return this.element;
+ },
+
+ option: function( key, value ) {
+ var options = key;
+
+ if ( arguments.length === 0 ) {
+ // don't return a reference to the internal hash
+ return $.extend( {}, this.options );
+ }
+
+ if (typeof key === "string" ) {
+ if ( value === undefined ) {
+ return this.options[ key ];
+ }
+ options = {};
+ options[ key ] = value;
+ }
+
+ this._setOptions( options );
+
+ return this;
+ },
+ _setOptions: function( options ) {
+ var self = this;
+ $.each( options, function( key, value ) {
+ self._setOption( key, value );
+ });
+
+ return this;
+ },
+ _setOption: function( key, value ) {
+ this.options[ key ] = value;
+
+ if ( key === "disabled" ) {
+ this.widget()
+ [ value ? "addClass" : "removeClass"](
+ this.widgetBaseClass + "-disabled" + " " +
+ "ui-state-disabled" )
+ .attr( "aria-disabled", value );
+ }
+
+ return this;
+ },
+
+ enable: function() {
+ return this._setOption( "disabled", false );
+ },
+ disable: function() {
+ return this._setOption( "disabled", true );
+ },
+
+ _trigger: function( type, event, data ) {
+ var callback = this.options[ type ];
+
+ event = $.Event( event );
+ event.type = ( type === this.widgetEventPrefix ?
+ type :
+ this.widgetEventPrefix + type ).toLowerCase();
+ data = data || {};
+
+ // copy original event properties over to the new event
+ // this would happen if we could call $.event.fix instead of $.Event
+ // but we don't have a way to force an event to be fixed multiple times
+ if ( event.originalEvent ) {
+ for ( var i = $.event.props.length, prop; i; ) {
+ prop = $.event.props[ --i ];
+ event[ prop ] = event.originalEvent[ prop ];
+ }
+ }
+
+ this.element.trigger( event, data );
+
+ return !( $.isFunction(callback) &&
+ callback.call( this.element[0], event, data ) === false ||
+ event.isDefaultPrevented() );
+ }
+};
+
+})( jQuery );
diff --git a/js/util.js b/js/util.js
index 56ecdbcb83..31e574a3a8 100644
--- a/js/util.js
+++ b/js/util.js
@@ -31,7 +31,8 @@ var SN = { // StatusNet
CounterBlackout: false,
MaxLength: 140,
PatternUsername: /^[0-9a-zA-Z\-_.]*$/,
- HTTP20x30x: [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307]
+ HTTP20x30x: [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307],
+ NoticeFormMaster: null // to be cloned from the one at top
},
/**
@@ -50,17 +51,6 @@ var SN = { // StatusNet
Processing: 'processing',
CommandResult: 'command_result',
FormNotice: 'form_notice',
- NoticeDataText: 'notice_data-text',
- NoticeTextCount: 'notice_text-count',
- NoticeInReplyTo: 'notice_in-reply-to',
- NoticeDataAttach: 'notice_data-attach',
- NoticeDataAttachSelected: 'notice_data-attach_selected',
- NoticeActionSubmit: 'notice_action-submit',
- NoticeLat: 'notice_data-lat',
- NoticeLon: 'notice_data-lon',
- NoticeLocationId: 'notice_data-location_id',
- NoticeLocationNs: 'notice_data-location_ns',
- NoticeGeoName: 'notice_data-geo_name',
NoticeDataGeo: 'notice_data-geo',
NoticeDataGeoCookie: 'NoticeDataGeo',
NoticeDataGeoSelected: 'notice_data-geo_selected',
@@ -106,7 +96,7 @@ var SN = { // StatusNet
*/
FormNoticeEnhancements: function(form) {
if (jQuery.data(form[0], 'ElementData') === undefined) {
- MaxLength = form.find('#'+SN.C.S.NoticeTextCount).text();
+ MaxLength = form.find('.count').text();
if (typeof(MaxLength) == 'undefined') {
MaxLength = SN.C.I.MaxLength;
}
@@ -114,7 +104,7 @@ var SN = { // StatusNet
SN.U.Counter(form);
- NDT = form.find('#'+SN.C.S.NoticeDataText);
+ NDT = form.find('.notice_data-text:first');
NDT.bind('keyup', function(e) {
SN.U.Counter(form);
@@ -132,41 +122,10 @@ var SN = { // StatusNet
// Note there's still no event for mouse-triggered 'delete'.
NDT.bind('cut', delayedUpdate)
.bind('paste', delayedUpdate);
-
- NDT.bind('keydown', function(e) {
- SN.U.SubmitOnReturn(e, form);
- });
}
else {
- form.find('#'+SN.C.S.NoticeTextCount).text(jQuery.data(form[0], 'ElementData').MaxLength);
+ form.find('.count').text(jQuery.data(form[0], 'ElementData').MaxLength);
}
-
- if ($('body')[0].id != 'conversation' && window.location.hash.length === 0 && $(window).scrollTop() == 0) {
- form.find('textarea').focus();
- }
- },
-
- /**
- * To be called from keydown event handler on the notice import form.
- * Checks if return or enter key was pressed, and if so attempts to
- * submit the form and cancel standard processing of the enter key.
- *
- * @param {Event} event
- * @param {jQuery} el: jQuery object whose first element is the notice posting form
- *
- * @return {boolean} whether to cancel the event? Does this actually pass through?
- * @access private
- */
- SubmitOnReturn: function(event, el) {
- if (event.keyCode == 13 || event.keyCode == 10) {
- el.submit();
- event.preventDefault();
- event.stopPropagation();
- $('#'+el[0].id+' #'+SN.C.S.NoticeDataText).blur();
- $('body').focus();
- return false;
- }
- return true;
},
/**
@@ -193,7 +152,7 @@ var SN = { // StatusNet
}
var remaining = MaxLength - SN.U.CharacterCount(form);
- var counter = form.find('#'+SN.C.S.NoticeTextCount);
+ var counter = form.find('.count');
if (remaining.toString() != counter.text()) {
if (!SN.C.I.CounterBlackout || remaining === 0) {
@@ -224,7 +183,7 @@ var SN = { // StatusNet
* @return number of chars
*/
CharacterCount: function(form) {
- return form.find('#'+SN.C.S.NoticeDataText).val().length;
+ return form.find('.notice_data-text:first').val().length;
},
/**
@@ -269,6 +228,9 @@ var SN = { // StatusNet
* will be extracted and copied in, replacing the original form.
* If there's no form, the first paragraph will be used.
*
+ * This will automatically be applied on the 'submit' event for
+ * any form with the 'ajax' class.
+ *
* @fixme can sometimes explode confusingly if returnd data is bogus
* @fixme error handling is pretty vague
* @fixme can't submit file uploads
@@ -291,16 +253,34 @@ var SN = { // StatusNet
.attr(SN.C.S.Disabled, SN.C.S.Disabled);
},
error: function (xhr, textStatus, errorThrown) {
- alert(errorThrown || textStatus);
+ // If the server end reported an error from StatusNet,
+ // find it -- otherwise we'll see what was reported
+ // from the browser.
+ var errorReported = null;
+ if (xhr.responseXML) {
+ errorReported = $('#error', xhr.responseXML).text();
+ }
+ alert(errorReported || errorThrown || textStatus);
+
+ // Restore the form to original state.
+ // Hopefully. :D
+ form
+ .removeClass(SN.C.S.Processing)
+ .find('.submit')
+ .removeClass(SN.C.S.Disabled)
+ .removeAttr(SN.C.S.Disabled);
},
success: function(data, textStatus) {
if (typeof($('form', data)[0]) != 'undefined') {
form_new = document._importNode($('form', data)[0], true);
form.replaceWith(form_new);
}
- else {
+ else if (typeof($('p', data)[0]) != 'undefined') {
form.replaceWith(document._importNode($('p', data)[0], true));
}
+ else {
+ alert('Unknown error.');
+ }
}
});
},
@@ -365,46 +345,24 @@ var SN = { // StatusNet
dataType: 'xml',
timeout: '60000',
beforeSend: function(formData) {
- if (form.find('#'+SN.C.S.NoticeDataText)[0].value.length === 0) {
+ if (form.find('.notice_data-text:first').val() == '') {
form.addClass(SN.C.S.Warning);
return false;
}
form
.addClass(SN.C.S.Processing)
- .find('#'+SN.C.S.NoticeActionSubmit)
+ .find('.submit')
.addClass(SN.C.S.Disabled)
.attr(SN.C.S.Disabled, SN.C.S.Disabled);
- SN.C.I.NoticeDataGeo.NLat = $('#'+SN.C.S.NoticeLat).val();
- SN.C.I.NoticeDataGeo.NLon = $('#'+SN.C.S.NoticeLon).val();
- SN.C.I.NoticeDataGeo.NLNS = $('#'+SN.C.S.NoticeLocationNs).val();
- SN.C.I.NoticeDataGeo.NLID = $('#'+SN.C.S.NoticeLocationId).val();
- SN.C.I.NoticeDataGeo.NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked');
-
- cookieValue = $.cookie(SN.C.S.NoticeDataGeoCookie);
-
- if (cookieValue !== null && cookieValue != 'disabled') {
- cookieValue = JSON.parse(cookieValue);
- SN.C.I.NoticeDataGeo.NLat = $('#'+SN.C.S.NoticeLat).val(cookieValue.NLat).val();
- SN.C.I.NoticeDataGeo.NLon = $('#'+SN.C.S.NoticeLon).val(cookieValue.NLon).val();
- if ($('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS)) {
- SN.C.I.NoticeDataGeo.NLNS = $('#'+SN.C.S.NoticeLocationNs).val(cookieValue.NLNS).val();
- SN.C.I.NoticeDataGeo.NLID = $('#'+SN.C.S.NoticeLocationId).val(cookieValue.NLID).val();
- }
- }
- if (cookieValue == 'disabled') {
- SN.C.I.NoticeDataGeo.NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', false).attr('checked');
- }
- else {
- SN.C.I.NoticeDataGeo.NDG = $('#'+SN.C.S.NoticeDataGeo).attr('checked', true).attr('checked');
- }
+ SN.U.normalizeGeoData(form);
return true;
},
error: function (xhr, textStatus, errorThrown) {
form
.removeClass(SN.C.S.Processing)
- .find('#'+SN.C.S.NoticeActionSubmit)
+ .find('.submit')
.removeClass(SN.C.S.Disabled)
.removeAttr(SN.C.S.Disabled, SN.C.S.Disabled);
removeFeedback();
@@ -421,7 +379,7 @@ var SN = { // StatusNet
if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
form
.resetForm()
- .find('#'+SN.C.S.NoticeDataAttachSelected).remove();
+ .find('.attach-status').remove();
SN.U.FormNoticeEnhancements(form);
}
else {
@@ -450,10 +408,25 @@ var SN = { // StatusNet
else {
// New notice post was successful. If on our timeline, show it!
var notice = document._importNode($('li', data)[0], true);
- var notices = $('#notices_primary .notices');
- if (notices.length > 0 && SN.U.belongsOnTimeline(notice)) {
+ var notices = $('#notices_primary .notices:first');
+ var replyItem = form.closest('li.notice-reply');
+
+ if (replyItem.length > 0) {
+ // If this is an inline reply, insert it in place.
+ var id = $(notice).attr('id');
+ if ($("#"+id).length == 0) {
+ var parentNotice = replyItem.closest('li.notice');
+ replyItem.replaceWith(notice);
+ SN.U.NoticeInlineReplyPlaceholder(parentNotice);
+ } else {
+ // Realtime came through before us...
+ replyItem.remove();
+ }
+ } else if (notices.length > 0 && SN.U.belongsOnTimeline(notice)) {
+ // Not a reply. If on our timeline, show it at the top!
+
if ($('#'+notice.id).length === 0) {
- var notice_irt_value = $('#'+SN.C.S.NoticeInReplyTo).val();
+ var notice_irt_value = form.find('[name=inreplyto]').val();
var notice_irt = '#notices_primary #notice-'+notice_irt_value;
if($('body')[0].id == 'conversation') {
if(notice_irt_value.length > 0 && $(notice_irt+' .notices').length < 1) {
@@ -468,39 +441,66 @@ var SN = { // StatusNet
.css({display:'none'})
.fadeIn(2500);
SN.U.NoticeWithAttachment($('#'+notice.id));
- SN.U.NoticeReplyTo($('#'+notice.id));
+ SN.U.switchInputFormTab("placeholder");
}
- }
- else {
+ } else {
// Not on a timeline that this belongs on?
// Just show a success message.
+ // @fixme inline
showFeedback('success', $('title', data).text());
}
}
form.resetForm();
- form.find('#'+SN.C.S.NoticeInReplyTo).val('');
- form.find('#'+SN.C.S.NoticeDataAttachSelected).remove();
+ form.find('[name=inreplyto]').val('');
+ form.find('.attach-status').remove();
SN.U.FormNoticeEnhancements(form);
}
},
complete: function(xhr, textStatus) {
form
.removeClass(SN.C.S.Processing)
- .find('#'+SN.C.S.NoticeActionSubmit)
+ .find('.submit')
.removeAttr(SN.C.S.Disabled)
.removeClass(SN.C.S.Disabled);
- $('#'+SN.C.S.NoticeLat).val(SN.C.I.NoticeDataGeo.NLat);
- $('#'+SN.C.S.NoticeLon).val(SN.C.I.NoticeDataGeo.NLon);
- if ($('#'+SN.C.S.NoticeLocationNs)) {
- $('#'+SN.C.S.NoticeLocationNs).val(SN.C.I.NoticeDataGeo.NLNS);
- $('#'+SN.C.S.NoticeLocationId).val(SN.C.I.NoticeDataGeo.NLID);
- }
- $('#'+SN.C.S.NoticeDataGeo).attr('checked', SN.C.I.NoticeDataGeo.NDG);
+ form.find('[name=lat]').val(SN.C.I.NoticeDataGeo.NLat);
+ form.find('[name=lon]').val(SN.C.I.NoticeDataGeo.NLon);
+ form.find('[name=location_ns]').val(SN.C.I.NoticeDataGeo.NLNS);
+ form.find('[name=location_id]').val(SN.C.I.NoticeDataGeo.NLID);
+ form.find('[name=notice_data-geo]').attr('checked', SN.C.I.NoticeDataGeo.NDG);
}
});
},
+ normalizeGeoData: function(form) {
+ SN.C.I.NoticeDataGeo.NLat = form.find('[name=lat]').val();
+ SN.C.I.NoticeDataGeo.NLon = form.find('[name=lon]').val();
+ SN.C.I.NoticeDataGeo.NLNS = form.find('[name=location_ns]').val();
+ SN.C.I.NoticeDataGeo.NLID = form.find('[name=location_id]').val();
+ SN.C.I.NoticeDataGeo.NDG = form.find('[name=notice_data-geo]').attr('checked'); // @fixme
+
+ var cookieValue = $.cookie(SN.C.S.NoticeDataGeoCookie);
+
+ if (cookieValue !== null && cookieValue != 'disabled') {
+ cookieValue = JSON.parse(cookieValue);
+ SN.C.I.NoticeDataGeo.NLat = form.find('[name=lat]').val(cookieValue.NLat).val();
+ SN.C.I.NoticeDataGeo.NLon = form.find('[name=lon]').val(cookieValue.NLon).val();
+ if (cookieValue.NLNS) {
+ SN.C.I.NoticeDataGeo.NLNS = form.find('[name=location_ns]').val(cookieValue.NLNS).val();
+ SN.C.I.NoticeDataGeo.NLID = form.find('[name=location_id]').val(cookieValue.NLID).val();
+ } else {
+ form.find('[name=location_ns]').val('');
+ form.find('[name=location_id]').val('');
+ }
+ }
+ if (cookieValue == 'disabled') {
+ SN.C.I.NoticeDataGeo.NDG = form.find('[name=notice_data-geo]').attr('checked', false).attr('checked');
+ }
+ else {
+ SN.C.I.NoticeDataGeo.NDG = form.find('[name=notice_data-geo]').attr('checked', true).attr('checked');
+ }
+
+ },
/**
* Fetch an XML DOM from an XHR's response data.
*
@@ -533,69 +533,135 @@ var SN = { // StatusNet
* @access private
*/
NoticeReply: function() {
- if ($('#'+SN.C.S.NoticeDataText).length > 0 && $('#content .notice_reply').length > 0) {
- $('#content .notice').each(function() { SN.U.NoticeReplyTo($(this)); });
- }
- },
-
- /**
- * Setup function -- DOES NOT trigger actions immediately.
- *
- * Sets up event handlers on the given notice's reply button to
- * tweak the new-notice form with needed variables and focus it
- * when pushed.
- *
- * (This replaces the default reply button behavior to submit
- * directly to a form which comes back with a specialized page
- * with the form data prefilled.)
- *
- * @param {jQuery} notice: jQuery object containing one or more notices
- * @access private
- */
- NoticeReplyTo: function(notice) {
- notice.find('.notice_reply').live('click', function() {
+ $('#content .notice_reply').live('click', function(e) {
+ e.preventDefault();
+ var notice = $(this).closest('li.notice');
var nickname = ($('.author .nickname', notice).length > 0) ? $($('.author .nickname', notice)[0]) : $('.author .nickname.uid');
- SN.U.NoticeReplySet(nickname.text(), $($('.notice_id', notice)[0]).text());
+ SN.U.NoticeInlineReplyTrigger(notice, '@' + nickname.text());
return false;
});
},
/**
- * Updates the new notice posting form with bits for replying to the
- * given user. Adds replyto parameter to the form, and a "@foo" to the
- * text area.
- *
- * @fixme replyto is a global variable, but probably shouldn't be
- *
- * @param {String} nick
- * @param {String} id
+ * Stub -- kept for compat with plugins for now.
+ * @access private
*/
- NoticeReplySet: function(nick,id) {
- if (nick.match(SN.C.I.PatternUsername)) {
- var text = $('#'+SN.C.S.NoticeDataText);
- if (text.length > 0) {
- replyto = '@' + nick + ' ';
- text.val(replyto + text.val().replace(RegExp(replyto, 'i'), ''));
- $('#'+SN.C.S.FormNotice+' #'+SN.C.S.NoticeInReplyTo).val(id);
+ NoticeReplyTo: function(notice) {
+ },
- text[0].focus();
- if (text[0].setSelectionRange) {
- var len = text.val().length;
- text[0].setSelectionRange(len,len);
+ /**
+ * Open up a notice's inline reply box.
+ *
+ * @param {jQuery} notice: jQuery object containing one notice
+ * @param {String} initialText
+ */
+ NoticeInlineReplyTrigger: function(notice, initialText) {
+ // Find the notice we're replying to...
+ var id = $($('.notice_id', notice)[0]).text();
+ var parentNotice = notice;
+
+ // Find the threaded replies view we'll be adding to...
+ var list = notice.closest('.notices');
+ if (list.hasClass('threaded-replies')) {
+ // We're replying to a reply; use reply form on the end of this list.
+ // We'll add our form at the end of this; grab the root notice.
+ parentNotice = list.closest('.notice');
+ } else {
+ // We're replying to a parent notice; pull its threaded list
+ // and we'll add on the end of it. Will add if needed.
+ list = $('ul.threaded-replies', notice);
+ if (list.length == 0) {
+ list = $('
');
+ notice.append(list);
+ }
+ }
+
+ // See if the form's already open...
+ var replyForm = $('.notice-reply-form', list);
+
+ var nextStep = function() {
+ // Override...?
+ replyForm.find('input[name=inreplyto]').val(id);
+
+ // Set focus...
+ var text = replyForm.find('textarea');
+ if (text.length == 0) {
+ throw "No textarea";
+ }
+ var replyto = '';
+ if (initialText) {
+ replyto = initialText + ' ';
+ }
+ text.val(replyto + text.val().replace(RegExp(replyto, 'i'), ''));
+ text.data('initialText', $.trim(initialText + ''));
+ text.focus();
+ if (text[0].setSelectionRange) {
+ var len = text.val().length;
+ text[0].setSelectionRange(len,len);
+ }
+ };
+ if (replyForm.length > 0) {
+ // Update the existing form...
+ nextStep();
+ } else {
+ // Remove placeholder if any
+ list.find('li.notice-reply-placeholder').remove();
+
+ // Create the reply form entry at the end
+ var replyItem = $('li.notice-reply', list);
+ if (replyItem.length == 0) {
+ replyItem = $('
');
+
+ var intermediateStep = function(formMaster) {
+ var formEl = document._importNode(formMaster, true);
+ replyItem.append(formEl);
+ list.append(replyItem);
+
+ var form = replyForm = $(formEl);
+ SN.Init.NoticeFormSetup(form);
+
+ nextStep();
+ };
+ if (SN.C.I.NoticeFormMaster) {
+ // We've already saved a master copy of the form.
+ // Clone it in!
+ intermediateStep(SN.C.I.NoticeFormMaster);
+ } else {
+ // Fetch a fresh copy of the notice form over AJAX.
+ // Warning: this can have a delay, which looks bad.
+ // @fixme this fallback may or may not work
+ var url = $('#form_notice').attr('action');
+ $.get(url, {ajax: 1}, function(data, textStatus, xhr) {
+ intermediateStep($('form', data)[0]);
+ });
}
}
}
},
+ NoticeInlineReplyPlaceholder: function(notice) {
+ var list = notice.find('ul.threaded-replies');
+ var placeholder = $('
' +
+ '' +
+ '
');
+ placeholder.find('input')
+ .val(SN.msg('reply_placeholder'));
+ list.append(placeholder);
+ },
+
/**
* Setup function -- DOES NOT apply immediately.
*
- * Sets up event handlers for favor/disfavor forms to submit via XHR.
+ * Sets up event handlers for inline reply mini-form placeholders.
* Uses 'live' rather than 'bind', so applies to future as well as present items.
*/
- NoticeFavor: function() {
- $('.form_favor').live('click', function() { SN.U.FormXHR($(this)); return false; });
- $('.form_disfavor').live('click', function() { SN.U.FormXHR($(this)); return false; });
+ NoticeInlineReplySetup: function() {
+ $('li.notice-reply-placeholder input')
+ .live('focus', function() {
+ var notice = $(this).closest('li.notice');
+ SN.U.NoticeInlineReplyTrigger(notice);
+ return false;
+ });
},
/**
@@ -715,36 +781,34 @@ var SN = { // StatusNet
*
* This preview box will also allow removing the attachment
* prior to posting.
+ *
+ * @param {jQuery} form
*/
- NoticeDataAttach: function() {
- NDA = $('#'+SN.C.S.NoticeDataAttach);
+ NoticeDataAttach: function(form) {
+ var NDA = form.find('input[type=file]');
NDA.change(function(event) {
+ form.find('.attach-status').remove();
+
var filename = $(this).val();
if (!filename) {
// No file -- we've been tricked!
- $('#'+SN.C.S.NoticeDataAttachSelected).remove();
return false;
}
- // @fixme appending filename straight in is potentially unsafe
- S = '
';NDAS=$("#"+SN.C.S.NoticeDataAttachSelected);if(NDAS.length>0){NDAS.replaceWith(S)}else{$("#"+SN.C.S.FormNotice).append(S)}$("#"+SN.C.S.NoticeDataAttachSelected+" button").click(function(){$("#"+SN.C.S.NoticeDataAttachSelected).remove();NDA.val("");return false});if(typeof this.files=="object"){for(var b=0;bf){e=false}if(e){g(c,function(i){var h=$("").attr("title",d).attr("alt",d).attr("src",i).attr("style","height: 120px");$("#"+SN.C.S.NoticeDataAttachSelected).append(h)})}else{var b=$("").text(d);$("#"+SN.C.S.NoticeDataAttachSelected).append(b)}},NoticeLocationAttach:function(){var c=$("#"+SN.C.S.NoticeLat).val();var h=$("#"+SN.C.S.NoticeLon).val();var d=$("#"+SN.C.S.NoticeLocationNs).val();var i=$("#"+SN.C.S.NoticeLocationId).val();var a=$("#"+SN.C.S.NoticeGeoName).text();var b=$("#"+SN.C.S.NoticeDataGeo);function e(){$("label[for="+SN.C.S.NoticeDataGeo+"]").attr("title",jQuery.trim($("label[for="+SN.C.S.NoticeDataGeo+"]").text())).removeClass("checked");$("#"+SN.C.S.NoticeLat).val("");$("#"+SN.C.S.NoticeLon).val("");$("#"+SN.C.S.NoticeLocationNs).val("");$("#"+SN.C.S.NoticeLocationId).val("");$("#"+SN.C.S.NoticeDataGeo).attr("checked",false);$.cookie(SN.C.S.NoticeDataGeoCookie,"disabled",{path:"/"})}function j(k,l){$.getJSON(k,l,function(m){var n,o;if(typeof(m.location_ns)!="undefined"){$("#"+SN.C.S.NoticeLocationNs).val(m.location_ns);n=m.location_ns}if(typeof(m.location_id)!="undefined"){$("#"+SN.C.S.NoticeLocationId).val(m.location_id);o=m.location_id}if(typeof(m.name)=="undefined"){NLN_text=l.lat+";"+l.lon}else{NLN_text=m.name}$("label[for="+SN.C.S.NoticeDataGeo+"]").attr("title",NoticeDataGeo_text.ShareDisable+" ("+NLN_text+")");$("#"+SN.C.S.NoticeLat).val(l.lat);$("#"+SN.C.S.NoticeLon).val(l.lon);$("#"+SN.C.S.NoticeLocationNs).val(n);$("#"+SN.C.S.NoticeLocationId).val(o);$("#"+SN.C.S.NoticeDataGeo).attr("checked",true);var p={NLat:l.lat,NLon:l.lon,NLNS:n,NLID:o,NLN:NLN_text,NLNU:m.url,NDG:true};$.cookie(SN.C.S.NoticeDataGeoCookie,JSON.stringify(p),{path:"/"})})}if(b.length>0){if($.cookie(SN.C.S.NoticeDataGeoCookie)=="disabled"){b.attr("checked",false)}else{b.attr("checked",true)}var f=$("#notice_data-geo_wrap");var g=f.attr("title");f.removeAttr("title");$("label[for="+SN.C.S.NoticeDataGeo+"]").attr("title",jQuery.trim($("label[for="+SN.C.S.NoticeDataGeo+"]").text()));b.change(function(){if($("#"+SN.C.S.NoticeDataGeo).attr("checked")===true||$.cookie(SN.C.S.NoticeDataGeoCookie)===null){$("label[for="+SN.C.S.NoticeDataGeo+"]").attr("title",NoticeDataGeo_text.ShareDisable).addClass("checked");if($.cookie(SN.C.S.NoticeDataGeoCookie)===null||$.cookie(SN.C.S.NoticeDataGeoCookie)=="disabled"){if(navigator.geolocation){navigator.geolocation.getCurrentPosition(function(m){$("#"+SN.C.S.NoticeLat).val(m.coords.latitude);$("#"+SN.C.S.NoticeLon).val(m.coords.longitude);var n={lat:m.coords.latitude,lon:m.coords.longitude,token:$("#token").val()};j(g,n)},function(m){switch(m.code){case m.PERMISSION_DENIED:e();break;case m.TIMEOUT:$("#"+SN.C.S.NoticeDataGeo).attr("checked",false);break}},{timeout:10000})}else{if(c.length>0&&h.length>0){var k={lat:c,lon:h,token:$("#token").val()};j(g,k)}else{e();$("#"+SN.C.S.NoticeDataGeo).remove();$("label[for="+SN.C.S.NoticeDataGeo+"]").remove()}}}else{var l=JSON.parse($.cookie(SN.C.S.NoticeDataGeoCookie));$("#"+SN.C.S.NoticeLat).val(l.NLat);$("#"+SN.C.S.NoticeLon).val(l.NLon);$("#"+SN.C.S.NoticeLocationNs).val(l.NLNS);$("#"+SN.C.S.NoticeLocationId).val(l.NLID);$("#"+SN.C.S.NoticeDataGeo).attr("checked",l.NDG);$("label[for="+SN.C.S.NoticeDataGeo+"]").attr("title",NoticeDataGeo_text.ShareDisable+" ("+l.NLN+")").addClass("checked")}}else{e()}}).change()}},NewDirectMessage:function(){NDM=$(".entity_send-a-message a");NDM.attr({href:NDM.attr("href")+"&ajax=1"});NDM.bind("click",function(){var a=$(".entity_send-a-message form");if(a.length===0){$(this).addClass(SN.C.S.Processing);$.get(NDM.attr("href"),null,function(b){$(".entity_send-a-message").append(document._importNode($("form",b)[0],true));a=$(".entity_send-a-message .form_notice");SN.U.FormNoticeXHR(a);SN.U.FormNoticeEnhancements(a);a.append('');$(".entity_send-a-message button").click(function(){a.hide();return false});NDM.removeClass(SN.C.S.Processing)})}else{a.show();$(".entity_send-a-message textarea").focus()}return false})},GetFullYear:function(c,d,a){var b=new Date();b.setFullYear(c,d,a);return b},StatusNetInstance:{Set:function(b){var a=SN.U.StatusNetInstance.Get();if(a!==null){b=$.extend(a,b)}$.cookie(SN.C.S.StatusNetInstance,JSON.stringify(b),{path:"/",expires:SN.U.GetFullYear(2029,0,1)})},Get:function(){var a=$.cookie(SN.C.S.StatusNetInstance);if(a!==null){return JSON.parse(a)}return null},Delete:function(){$.cookie(SN.C.S.StatusNetInstance,null)}},belongsOnTimeline:function(b){var a=$("body").attr("id");if(a=="public"){return true}var c=$("#nav_profile a").attr("href");if(c){var d=$(b).find(".entry-title .author a.url").attr("href");if(d==c){if(a=="all"||a=="showstream"){return true}}}return false}},Init:{NoticeForm:function(){if($("body.user_in").length>0){SN.U.NoticeLocationAttach();$("."+SN.C.S.FormNotice).each(function(){SN.U.FormNoticeXHR($(this));SN.U.FormNoticeEnhancements($(this))});SN.U.NoticeDataAttach()}},Notices:function(){if($("body.user_in").length>0){SN.U.NoticeFavor();SN.U.NoticeRepeat();SN.U.NoticeReply()}SN.U.NoticeAttachments()},EntityActions:function(){if($("body.user_in").length>0){$(".form_user_subscribe").live("click",function(){SN.U.FormXHR($(this));return false});$(".form_user_unsubscribe").live("click",function(){SN.U.FormXHR($(this));return false});$(".form_group_join").live("click",function(){SN.U.FormXHR($(this));return false});$(".form_group_leave").live("click",function(){SN.U.FormXHR($(this));return false});$(".form_user_nudge").live("click",function(){SN.U.FormXHR($(this));return false});SN.U.NewDirectMessage()}},Login:function(){if(SN.U.StatusNetInstance.Get()!==null){var a=SN.U.StatusNetInstance.Get().Nickname;if(a!==null){$("#form_login #nickname").val(a)}}$("#form_login").bind("submit",function(){SN.U.StatusNetInstance.Set({Nickname:$("#form_login #nickname").val()});return true})},UploadForms:function(){$("input[type=file]").change(function(d){if(typeof this.files=="object"&&this.files.length>0){var c=0;for(var b=0;b0&&c>a){var e="File too large: maximum upload size is %d bytes.";alert(e.replace("%d",a));$(this).val("");d.preventDefault();return false}}})}}};$(document).ready(function(){SN.Init.UploadForms();if($("."+SN.C.S.FormNotice).length>0){SN.Init.NoticeForm()}if($("#content .notices").length>0){SN.Init.Notices()}if($("#content .entity_actions").length>0){SN.Init.EntityActions()}if($("#form_login").length>0){SN.Init.Login()}});if(!document.ELEMENT_NODE){document.ELEMENT_NODE=1;document.ATTRIBUTE_NODE=2;document.TEXT_NODE=3;document.CDATA_SECTION_NODE=4;document.ENTITY_REFERENCE_NODE=5;document.ENTITY_NODE=6;document.PROCESSING_INSTRUCTION_NODE=7;document.COMMENT_NODE=8;document.DOCUMENT_NODE=9;document.DOCUMENT_TYPE_NODE=10;document.DOCUMENT_FRAGMENT_NODE=11;document.NOTATION_NODE=12}document._importNode=function(e,a){switch(e.nodeType){case document.ELEMENT_NODE:var d=document.createElement(e.nodeName);if(e.attributes&&e.attributes.length>0){for(var c=0,b=e.attributes.length;c0){for(var c=0,b=e.childNodes.length;c0){var j=c.pop();j()}}};window._google_loader_apiLoaded=function(){f()};var d=function(){return(window.google&&google.loader)};var g=function(j){if(d()){return true}h(j);e();return false};e();return{shim:true,type:"ClientLocation",lastPosition:null,getCurrentPosition:function(k,n,o){var m=this;if(!g(function(){m.getCurrentPosition(k,n,o)})){return}if(google.loader.ClientLocation){var l=google.loader.ClientLocation;var j={coords:{latitude:l.latitude,longitude:l.longitude,altitude:null,accuracy:43000,altitudeAccuracy:null,heading:null,speed:null},address:{city:l.address.city,country:l.address.country,country_code:l.address.country_code,region:l.address.region},timestamp:new Date()};k(j);this.lastPosition=j}else{if(n==="function"){n({code:3,message:"Using the Google ClientLocation API and it is not able to calculate a location."})}}},watchPosition:function(j,l,m){this.getCurrentPosition(j,l,m);var k=this;var n=setInterval(function(){k.getCurrentPosition(j,l,m)},10000);return n},clearWatch:function(j){clearInterval(j)},getPermission:function(l,j,k){return true}}});navigator.geolocation=(window.google&&google.gears)?a():b()})()};
\ No newline at end of file
+var SN={C:{I:{CounterBlackout:false,MaxLength:140,PatternUsername:/^[0-9a-zA-Z\-_.]*$/,HTTP20x30x:[200,201,202,203,204,205,206,300,301,302,303,304,305,306,307],NoticeFormMaster:null},S:{Disabled:"disabled",Warning:"warning",Error:"error",Success:"success",Processing:"processing",CommandResult:"command_result",FormNotice:"form_notice",NoticeDataGeo:"notice_data-geo",NoticeDataGeoCookie:"NoticeDataGeo",NoticeDataGeoSelected:"notice_data-geo_selected",StatusNetInstance:"StatusNetInstance"}},messages:{},msg:function(a){if(typeof SN.messages[a]=="undefined"){return"["+a+"]"}else{return SN.messages[a]}},U:{FormNoticeEnhancements:function(b){if(jQuery.data(b[0],"ElementData")===undefined){MaxLength=b.find(".count").text();if(typeof(MaxLength)=="undefined"){MaxLength=SN.C.I.MaxLength}jQuery.data(b[0],"ElementData",{MaxLength:MaxLength});SN.U.Counter(b);NDT=b.find(".notice_data-text:first");NDT.bind("keyup",function(c){SN.U.Counter(b)});var a=function(c){window.setTimeout(function(){SN.U.Counter(b)},50)};NDT.bind("cut",a).bind("paste",a)}else{b.find(".count").text(jQuery.data(b[0],"ElementData").MaxLength)}},Counter:function(d){SN.C.I.FormNoticeCurrent=d;var b=jQuery.data(d[0],"ElementData").MaxLength;if(b<=0){return}var c=b-SN.U.CharacterCount(d);var a=d.find(".count");if(c.toString()!=a.text()){if(!SN.C.I.CounterBlackout||c===0){if(a.text()!=String(c)){a.text(c)}if(c<0){d.addClass(SN.C.S.Warning)}else{d.removeClass(SN.C.S.Warning)}if(!SN.C.I.CounterBlackout){SN.C.I.CounterBlackout=true;SN.C.I.FormNoticeCurrent=d;window.setTimeout("SN.U.ClearCounterBlackout(SN.C.I.FormNoticeCurrent);",500)}}}},CharacterCount:function(a){return a.find(".notice_data-text:first").val().length},ClearCounterBlackout:function(a){SN.C.I.CounterBlackout=false;SN.U.Counter(a)},RewriteAjaxAction:function(a){if(document.location.protocol=="https:"&&a.substr(0,5)=="http:"){return a.replace(/^http:\/\/[^:\/]+/,"https://"+document.location.host)}else{return a}},FormXHR:function(a){$.ajax({type:"POST",dataType:"xml",url:SN.U.RewriteAjaxAction(a.attr("action")),data:a.serialize()+"&ajax=1",beforeSend:function(b){a.addClass(SN.C.S.Processing).find(".submit").addClass(SN.C.S.Disabled).attr(SN.C.S.Disabled,SN.C.S.Disabled)},error:function(d,e,c){var b=null;if(d.responseXML){b=$("#error",d.responseXML).text()}alert(b||c||e);a.removeClass(SN.C.S.Processing).find(".submit").removeClass(SN.C.S.Disabled).removeAttr(SN.C.S.Disabled)},success:function(b,c){if(typeof($("form",b)[0])!="undefined"){form_new=document._importNode($("form",b)[0],true);a.replaceWith(form_new)}else{if(typeof($("p",b)[0])!="undefined"){a.replaceWith(document._importNode($("p",b)[0],true))}else{alert("Unknown error.")}}}})},FormNoticeXHR:function(b){SN.C.I.NoticeDataGeo={};b.append('');b.attr("action",SN.U.RewriteAjaxAction(b.attr("action")));var c=function(d,e){b.append($('').addClass(d).text(e))};var a=function(){b.find(".form_response").remove()};b.ajaxForm({dataType:"xml",timeout:"60000",beforeSend:function(d){if(b.find(".notice_data-text:first").val()==""){b.addClass(SN.C.S.Warning);return false}b.addClass(SN.C.S.Processing).find(".submit").addClass(SN.C.S.Disabled).attr(SN.C.S.Disabled,SN.C.S.Disabled);SN.U.normalizeGeoData(b);return true},error:function(f,g,e){b.removeClass(SN.C.S.Processing).find(".submit").removeClass(SN.C.S.Disabled).removeAttr(SN.C.S.Disabled,SN.C.S.Disabled);a();if(g=="timeout"){c("error","Sorry! We had trouble sending your notice. The servers are overloaded. Please try again, and contact the site administrator if this problem persists.")}else{var d=SN.U.GetResponseXML(f);if($("."+SN.C.S.Error,d).length>0){b.append(document._importNode($("."+SN.C.S.Error,d)[0],true))}else{if(parseInt(f.status)===0||jQuery.inArray(parseInt(f.status),SN.C.I.HTTP20x30x)>=0){b.resetForm().find(".attach-status").remove();SN.U.FormNoticeEnhancements(b)}else{c("error","(Sorry! We had trouble sending your notice ("+f.status+" "+f.statusText+"). Please report the problem to the site administrator if this happens again.")}}}},success:function(i,f){a();var n=$("#"+SN.C.S.Error,i);if(n.length>0){c("error",n.text())}else{if($("body")[0].id=="bookmarklet"){self.close()}var d=$("#"+SN.C.S.CommandResult,i);if(d.length>0){c("success",d.text())}else{var m=document._importNode($("li",i)[0],true);var k=$("#notices_primary .notices:first");var l=b.closest("li.notice-reply");if(l.length>0){var e=$(m).attr("id");if($("#"+e).length==0){var j=l.closest("li.notice");l.replaceWith(m);SN.U.NoticeInlineReplyPlaceholder(j)}else{l.remove()}}else{if(k.length>0&&SN.U.belongsOnTimeline(m)){if($("#"+m.id).length===0){var h=b.find("[name=inreplyto]").val();var g="#notices_primary #notice-"+h;if($("body")[0].id=="conversation"){if(h.length>0&&$(g+" .notices").length<1){$(g).append('
');h.find("button.close").click(function(){e.find("[name=notice_data-geo]").removeAttr("checked").change();return false});e.append(h)}var b;if(c){b=$("").attr("href",c)}else{b=$("")}b.text(a);if(f||g){var d=f+";"+g;b.attr("title",d);if(!a){b.text(d)}}h.find(".geo_status").empty().append(b)},NewDirectMessage:function(){NDM=$(".entity_send-a-message a");NDM.attr({href:NDM.attr("href")+"&ajax=1"});NDM.bind("click",function(){var a=$(".entity_send-a-message form");if(a.length===0){$(this).addClass(SN.C.S.Processing);$.get(NDM.attr("href"),null,function(b){$(".entity_send-a-message").append(document._importNode($("form",b)[0],true));a=$(".entity_send-a-message .form_notice");SN.U.FormNoticeXHR(a);SN.U.FormNoticeEnhancements(a);a.append('');$(".entity_send-a-message button").click(function(){a.hide();return false});NDM.removeClass(SN.C.S.Processing)})}else{a.show();$(".entity_send-a-message textarea").focus()}return false})},GetFullYear:function(c,d,a){var b=new Date();b.setFullYear(c,d,a);return b},StatusNetInstance:{Set:function(b){var a=SN.U.StatusNetInstance.Get();if(a!==null){b=$.extend(a,b)}$.cookie(SN.C.S.StatusNetInstance,JSON.stringify(b),{path:"/",expires:SN.U.GetFullYear(2029,0,1)})},Get:function(){var a=$.cookie(SN.C.S.StatusNetInstance);if(a!==null){return JSON.parse(a)}return null},Delete:function(){$.cookie(SN.C.S.StatusNetInstance,null)}},belongsOnTimeline:function(b){var a=$("body").attr("id");if(a=="public"){return true}var c=$("#nav_profile a").attr("href");if(c){var d=$(b).find(".vcard.author a.url").attr("href");if(d==c){if(a=="all"||a=="showstream"){return true}}}return false},switchInputFormTab:function(a){$(".input_form_nav_tab.current").removeClass("current");if(a=="placeholder"){$("#input_form_nav_status").addClass("current")}else{$("#input_form_nav_"+a).addClass("current")}$(".input_form.current").removeClass("current");$("#input_form_"+a).addClass("current").find(".ajax-notice").each(function(){var b=$(this);SN.Init.NoticeFormSetup(b)}).find("textarea:first").focus()}},Init:{NoticeForm:function(){if($("body.user_in").length>0){$("#input_form_placeholder input.placeholder").focus(function(){SN.U.switchInputFormTab("status")});$("body").bind("click",function(g){var d=$("#content .input_forms div.current");if(d.length>0){if($("#content .input_forms").has(g.target).length==0){var a=d.find('textarea, input[type=text], input[type=""]');var c=false;a.each(function(){c=c||$(this).val()});if(!c){SN.U.switchInputFormTab("placeholder")}}}var b=$("li.notice-reply");if(b.length>0){var f=$(g.target);b.each(function(){var j=$(this);if(j.has(g.target).length==0){var h=j.find(".notice_data-text:first");var i=$.trim(h.val());if(i==""||i==h.data("initialText")){var e=j.closest("li.notice");j.remove();SN.U.NoticeInlineReplyPlaceholder(e)}}})}})}},NoticeFormSetup:function(a){if(!a.data("NoticeFormSetup")){SN.U.NoticeLocationAttach(a);SN.U.FormNoticeXHR(a);SN.U.FormNoticeEnhancements(a);SN.U.NoticeDataAttach(a);a.data("NoticeFormSetup",true)}},Notices:function(){if($("body.user_in").length>0){var a=$(".form_notice:first");if(a.length>0){SN.C.I.NoticeFormMaster=document._importNode(a[0],true)}SN.U.NoticeRepeat();SN.U.NoticeReply();SN.U.NoticeInlineReplySetup()}SN.U.NoticeAttachments()},EntityActions:function(){if($("body.user_in").length>0){SN.U.NewDirectMessage()}},Login:function(){if(SN.U.StatusNetInstance.Get()!==null){var a=SN.U.StatusNetInstance.Get().Nickname;if(a!==null){$("#form_login #nickname").val(a)}}$("#form_login").bind("submit",function(){SN.U.StatusNetInstance.Set({Nickname:$("#form_login #nickname").val()});return true})},AjaxForms:function(){$("form.ajax").live("submit",function(){SN.U.FormXHR($(this));return false})},UploadForms:function(){$("input[type=file]").change(function(d){if(typeof this.files=="object"&&this.files.length>0){var c=0;for(var b=0;b0&&c>a){var e="File too large: maximum upload size is %d bytes.";alert(e.replace("%d",a));$(this).val("");d.preventDefault();return false}}})}}};$(document).ready(function(){SN.Init.AjaxForms();SN.Init.UploadForms();if($("."+SN.C.S.FormNotice).length>0){SN.Init.NoticeForm()}if($("#content .notices").length>0){SN.Init.Notices()}if($("#content .entity_actions").length>0){SN.Init.EntityActions()}if($("#form_login").length>0){SN.Init.Login()}});if(!document.ELEMENT_NODE){document.ELEMENT_NODE=1;document.ATTRIBUTE_NODE=2;document.TEXT_NODE=3;document.CDATA_SECTION_NODE=4;document.ENTITY_REFERENCE_NODE=5;document.ENTITY_NODE=6;document.PROCESSING_INSTRUCTION_NODE=7;document.COMMENT_NODE=8;document.DOCUMENT_NODE=9;document.DOCUMENT_TYPE_NODE=10;document.DOCUMENT_FRAGMENT_NODE=11;document.NOTATION_NODE=12}document._importNode=function(e,a){switch(e.nodeType){case document.ELEMENT_NODE:var d=document.createElement(e.nodeName);if(e.attributes&&e.attributes.length>0){for(var c=0,b=e.attributes.length;c0){for(var c=0,b=e.childNodes.length;c0){var j=c.pop();j()}}};window._google_loader_apiLoaded=function(){f()};var d=function(){return(window.google&&google.loader)};var g=function(j){if(d()){return true}h(j);e();return false};e();return{shim:true,type:"ClientLocation",lastPosition:null,getCurrentPosition:function(k,n,o){var m=this;if(!g(function(){m.getCurrentPosition(k,n,o)})){return}if(google.loader.ClientLocation){var l=google.loader.ClientLocation;var j={coords:{latitude:l.latitude,longitude:l.longitude,altitude:null,accuracy:43000,altitudeAccuracy:null,heading:null,speed:null},address:{city:l.address.city,country:l.address.country,country_code:l.address.country_code,region:l.address.region},timestamp:new Date()};k(j);this.lastPosition=j}else{if(n==="function"){n({code:3,message:"Using the Google ClientLocation API and it is not able to calculate a location."})}}},watchPosition:function(j,l,m){this.getCurrentPosition(j,l,m);var k=this;var n=setInterval(function(){k.getCurrentPosition(j,l,m)},10000);return n},clearWatch:function(j){clearInterval(j)},getPermission:function(l,j,k){return true}}});navigator.geolocation=(window.google&&google.gears)?a():b()})()};
\ No newline at end of file
diff --git a/lib/userprofile.php b/lib/accountprofileblock.php
similarity index 52%
rename from lib/userprofile.php
rename to lib/accountprofileblock.php
index 8bd68ae3d7..a8bdb4715b 100644
--- a/lib/userprofile.php
+++ b/lib/accountprofileblock.php
@@ -1,12 +1,13 @@
.
*
- * @category Action
+ * @category Widget
* @package StatusNet
* @author Evan Prodromou
- * @author Sarven Capadisli
- * @copyright 2008 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
exit(1);
}
-require_once INSTALLDIR.'/lib/widget.php';
-
/**
- * Profile of a user
+ * Profile block to show for an account
*
- * Shows profile information about a particular user
- *
- * @category Output
- * @package StatusNet
- * @author Evan Prodromou
- * @author Sarven Capadisli
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- *
- * @see HTMLOutputter
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
*/
-class UserProfile extends Widget
+
+class AccountProfileBlock extends ProfileBlock
{
- var $user = null;
- var $profile = null;
+ protected $profile = null;
+ protected $user = null;
- function __construct($action=null, $user=null, $profile=null)
+ function __construct($out, $profile)
{
- parent::__construct($action);
- $this->user = $user;
+ parent::__construct($out);
$this->profile = $profile;
+ $this->user = User::staticGet('id', $profile->id);
}
- function show()
+ function avatar()
{
- $this->showProfileData();
- $this->showEntityActions();
- }
-
- function showProfileData()
- {
- if (Event::handle('StartProfilePageProfileSection', array(&$this->out, $this->profile))) {
-
- $this->out->elementStart('div', array('id' => 'i',
- 'class' => 'entity_profile vcard author'));
- // TRANS: H2 for user profile information.
- $this->out->element('h2', null, _('User profile'));
-
- if (Event::handle('StartProfilePageProfileElements', array(&$this->out, $this->profile))) {
-
- $this->showAvatar();
- $this->showNickname();
- $this->showFullName();
- $this->showLocation();
- $this->showHomepage();
- $this->showBio();
- $this->showProfileTags();
-
- Event::handle('EndProfilePageProfileElements', array(&$this->out, $this->profile));
- }
-
- $this->out->elementEnd('div');
- Event::handle('EndProfilePageProfileSection', array(&$this->out, $this->profile));
+ $avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE);
+ if (empty($avatar)) {
+ $avatar = $this->profile->getAvatar(73);
}
+ return (!empty($avatar)) ?
+ $avatar->displayUrl() :
+ Avatar::defaultImage(AVATAR_PROFILE_SIZE);
}
- function showAvatar()
+ function name()
{
- if (Event::handle('StartProfilePageAvatar', array($this->out, $this->profile))) {
-
- $avatar = $this->profile->getAvatar(AVATAR_PROFILE_SIZE);
- if (!$avatar) {
- // hack for remote Twitter users: no 96px, but large Twitter size is 73px
- $avatar = $this->profile->getAvatar(73);
- }
-
- $this->out->elementStart('dl', 'entity_depiction');
- // TRANS: DT element in area for user avatar.
- $this->out->element('dt', null, _('Photo'));
- $this->out->elementStart('dd');
- $this->out->element('img', array('src' => ($avatar) ? $avatar->displayUrl() : Avatar::defaultImage(AVATAR_PROFILE_SIZE),
- 'class' => 'photo avatar',
- 'width' => AVATAR_PROFILE_SIZE,
- 'height' => AVATAR_PROFILE_SIZE,
- 'alt' => $this->profile->nickname));
- $this->out->elementEnd('dd');
-
- $cur = common_current_user();
- if ($cur && $cur->id == $this->profile->id) {
- $this->out->elementStart('dd');
- // TRANS: Link text for changeing the avatar of the logged in user.
- $this->out->element('a', array('href' => common_local_url('avatarsettings')), _('Edit Avatar'));
- $this->out->elementEnd('dd');
- }
-
- $this->out->elementEnd('dl');
-
- Event::handle('EndProfilePageAvatar', array($this->out, $this->profile));
- }
+ return $this->profile->getBestName();
}
- function showNickname()
+ function url()
{
- if (Event::handle('StartProfilePageNickname', array($this->out, $this->profile))) {
-
- $this->out->elementStart('dl', 'entity_nickname');
- // TRANS: DT for nick name in a profile.
- $this->out->element('dt', null, _('Nickname'));
- $this->out->elementStart('dd');
- $hasFN = ($this->profile->fullname) ? 'nickname url uid' : 'fn nickname url uid';
- $this->out->element('a', array('href' => $this->profile->profileurl,
- 'rel' => 'me', 'class' => $hasFN),
- $this->profile->nickname);
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
-
- Event::handle('EndProfilePageNickname', array($this->out, $this->profile));
- }
+ return $this->profile->profileurl;
}
- function showFullName()
+ function location()
{
- if (Event::handle('StartProfilePageFullName', array($this->out, $this->profile))) {
- if ($this->profile->fullname) {
- $this->out->elementStart('dl', 'entity_fn');
- // TRANS: DT for full name in a profile.
- $this->out->element('dt', null, _('Full name'));
- $this->out->elementStart('dd');
- $this->out->element('span', 'fn', $this->profile->fullname);
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
- }
- Event::handle('EndProfilePageFullName', array($this->out, $this->profile));
- }
+ return $this->profile->location;
}
- function showLocation()
+ function homepage()
{
- if (Event::handle('StartProfilePageLocation', array($this->out, $this->profile))) {
- if ($this->profile->location) {
- $this->out->elementStart('dl', 'entity_location');
- // TRANS: DT for location in a profile.
- $this->out->element('dt', null, _('Location'));
- $this->out->element('dd', 'label', $this->profile->location);
- $this->out->elementEnd('dl');
- }
- Event::handle('EndProfilePageLocation', array($this->out, $this->profile));
- }
+ return $this->profile->homepage;
}
- function showHomepage()
+ function description()
{
- if (Event::handle('StartProfilePageHomepage', array($this->out, $this->profile))) {
- if ($this->profile->homepage) {
- $this->out->elementStart('dl', 'entity_url');
- // TRANS: DT for URL in a profile.
- $this->out->element('dt', null, _('URL'));
- $this->out->elementStart('dd');
- $this->out->element('a', array('href' => $this->profile->homepage,
- 'rel' => 'me', 'class' => 'url'),
- $this->profile->homepage);
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
- }
- Event::handle('EndProfilePageHomepage', array($this->out, $this->profile));
- }
+ return $this->profile->bio;
}
- function showBio()
+ function showActions()
{
- if (Event::handle('StartProfilePageBio', array($this->out, $this->profile))) {
- if ($this->profile->bio) {
- $this->out->elementStart('dl', 'entity_note');
- // TRANS: DT for note in a profile.
- $this->out->element('dt', null, _('Note'));
- $this->out->element('dd', 'note', $this->profile->bio);
- $this->out->elementEnd('dl');
- }
- Event::handle('EndProfilePageBio', array($this->out, $this->profile));
- }
- }
-
- function showProfileTags()
- {
- if (Event::handle('StartProfilePageProfileTags', array($this->out, $this->profile))) {
- $tags = Profile_tag::getTags($this->profile->id, $this->profile->id);
-
- if (count($tags) > 0) {
- $this->out->elementStart('dl', 'entity_tags');
- // TRANS: DT for tags in a profile.
- $this->out->element('dt', null, _('Tags'));
- $this->out->elementStart('dd');
- $this->out->elementStart('ul', 'tags xoxo');
- foreach ($tags as $tag) {
- $this->out->elementStart('li');
- // Avoid space by using raw output.
- $pt = '#' . $tag . '';
- $this->out->raw($pt);
- $this->out->elementEnd('li');
- }
- $this->out->elementEnd('ul');
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
- }
- Event::handle('EndProfilePageProfileTags', array($this->out, $this->profile));
- }
- }
-
- function showEntityActions()
- {
- if ($this->profile->hasRole(Profile_role::DELETED)) {
- $this->out->elementStart('div', 'entity_actions');
- // TRANS: H2 for user actions in a profile.
- $this->out->element('h2', null, _('User actions'));
- $this->out->elementStart('ul');
- $this->out->elementStart('p', array('class' => 'profile_deleted'));
- // TRANS: Text shown in user profile of not yet compeltely deleted users.
- $this->out->text(_('User deletion in progress...'));
- $this->out->elementEnd('p');
- $this->out->elementEnd('ul');
- $this->out->elementEnd('div');
- return;
- }
if (Event::handle('StartProfilePageActionsSection', array($this->out, $this->profile))) {
+ if ($this->profile->hasRole(Profile_role::DELETED)) {
+ $this->out->elementStart('div', 'entity_actions');
+ // TRANS: H2 for user actions in a profile.
+ $this->out->element('h2', null, _('User actions'));
+ $this->out->elementStart('ul');
+ $this->out->elementStart('p', array('class' => 'profile_deleted'));
+ // TRANS: Text shown in user profile of not yet compeltely deleted users.
+ $this->out->text(_('User deletion in progress...'));
+ $this->out->elementEnd('p');
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('div');
+ return;
+ }
+
$cur = common_current_user();
$this->out->elementStart('div', 'entity_actions');
@@ -430,4 +287,4 @@ class UserProfile extends Widget
// TRANS: Link text for link that will subscribe to a remote profile.
_('Subscribe'));
}
-}
+}
\ No newline at end of file
diff --git a/lib/accountsettingsaction.php b/lib/accountsettingsaction.php
deleted file mode 100644
index b1ea998bfa..0000000000
--- a/lib/accountsettingsaction.php
+++ /dev/null
@@ -1,159 +0,0 @@
-.
- *
- * @category Settings
- * @package StatusNet
- * @author Evan Prodromou
- * @copyright 2008-2009 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/settingsaction.php';
-
-/**
- * Base class for account settings actions
- *
- * @category Settings
- * @package StatusNet
- * @author Evan Prodromou
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- *
- * @see Widget
- */
-class AccountSettingsAction extends SettingsAction
-{
- /**
- * Show the local navigation menu
- *
- * This is the same for all settings, so we show it here.
- *
- * @return void
- */
- function showLocalNav()
- {
- $menu = new AccountSettingsNav($this);
- $menu->show();
- }
-}
-
-/**
- * A widget for showing the settings group local nav menu
- *
- * @category Widget
- * @package StatusNet
- * @author Evan Prodromou
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- *
- * @see HTMLOutputter
- */
-class AccountSettingsNav extends Widget
-{
- var $action = null;
-
- /**
- * Construction
- *
- * @param Action $action current action, used for output
- */
- function __construct($action=null)
- {
- parent::__construct($action);
- $this->action = $action;
- }
-
- /**
- * Show the menu
- *
- * @return void
- */
- function show()
- {
- $action_name = $this->action->trimmed('action');
- $this->action->elementStart('ul', array('class' => 'nav'));
-
- if (Event::handle('StartAccountSettingsNav', array($this->action))) {
- $user = common_current_user();
-
- if(Event::handle('StartAccountSettingsProfileMenuItem', array($this, &$menu))){
- // TRANS: Link title attribute in user account settings menu.
- $title = _('Change your profile settings');
- // TRANS: Link description in user account settings menu.
- $this->showMenuItem('profilesettings',_('Profile'),$title);
- Event::handle('EndAccountSettingsProfileMenuItem', array($this, &$menu));
- }
- if(Event::handle('StartAccountSettingsAvatarMenuItem', array($this, &$menu))){
- // TRANS: Link title attribute in user account settings menu.
- $title = _('Upload an avatar');
- // TRANS: Link description in user account settings menu.
- $this->showMenuItem('avatarsettings',_('Avatar'),$title);
- Event::handle('EndAccountSettingsAvatarMenuItem', array($this, &$menu));
- }
- if(Event::handle('StartAccountSettingsPasswordMenuItem', array($this, &$menu))){
- // TRANS: Link title attribute in user account settings menu.
- $title = _('Change your password');
- // TRANS: Link description in user account settings menu.
- $this->showMenuItem('passwordsettings',_('Password'),$title);
- Event::handle('EndAccountSettingsPasswordMenuItem', array($this, &$menu));
- }
- if(Event::handle('StartAccountSettingsEmailMenuItem', array($this, &$menu))){
- // TRANS: Link title attribute in user account settings menu.
- $title = _('Change email handling');
- // TRANS: Link description in user account settings menu.
- $this->showMenuItem('emailsettings',_('Email'),$title);
- Event::handle('EndAccountSettingsEmailMenuItem', array($this, &$menu));
- }
- if(Event::handle('StartAccountSettingsDesignMenuItem', array($this, &$menu))){
- // TRANS: Link title attribute in user account settings menu.
- $title = _('Design your profile');
- // TRANS: Link description in user account settings menu.
- $this->showMenuItem('userdesignsettings',_('Design'),$title);
- Event::handle('EndAccountSettingsDesignMenuItem', array($this, &$menu));
- }
- if(Event::handle('StartAccountSettingsOtherMenuItem', array($this, &$menu))){
- // TRANS: Link title attribute in user account settings menu.
- $title = _('Other options');
- // TRANS: Link description in user account settings menu.
- $this->showMenuItem('othersettings',_('Other'),$title);
- Event::handle('EndAccountSettingsOtherMenuItem', array($this, &$menu));
- }
-
- Event::handle('EndAccountSettingsNav', array($this->action));
- }
-
- $this->action->elementEnd('ul');
- }
-
- function showMenuItem($menuaction, $desc1, $desc2)
- {
- $action_name = $this->action->trimmed('action');
- $this->action->menuItem(common_local_url($menuaction),
- $desc1,
- $desc2,
- $action_name === $menuaction);
- }
-}
diff --git a/lib/action.php b/lib/action.php
index 173e2c2a58..20de71aec1 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -83,6 +83,11 @@ class Action extends HTMLOutputter // lawsuit
function prepare($argarray)
{
$this->args =& common_copy_args($argarray);
+
+ if ($this->boolean('ajax')) {
+ StatusNet::setAjax(true);
+ }
+
return true;
}
@@ -222,6 +227,8 @@ class Action extends HTMLOutputter // lawsuit
Event::handle('EndShowLaconicaStyles', array($this));
}
+ $this->cssLink(common_path('js/css/smoothness/jquery-ui.css'));
+
if (Event::handle('StartShowUAStyles', array($this))) {
$this->comment('[if IE]>getExternals() as $url) {
+ $this->cssLink($url, $mainTheme, $media);
+ }
+
// If the currently-selected theme has dependencies on other themes,
// we'll need to load their display.css files as well in order.
- $theme = new Theme($mainTheme);
$baseThemes = $theme->getDeps();
foreach ($baseThemes as $baseTheme) {
$this->cssLink('css/display.css', $baseTheme, $media);
@@ -286,16 +300,32 @@ class Action extends HTMLOutputter // lawsuit
{
if (Event::handle('StartShowScripts', array($this))) {
if (Event::handle('StartShowJQueryScripts', array($this))) {
- $this->script('jquery.min.js');
- $this->script('jquery.form.min.js');
- $this->script('jquery.cookie.min.js');
- $this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/json2.min.js').'"); }');
- $this->script('jquery.joverlay.min.js');
+ if (common_config('site', 'minify')) {
+ $this->script('jquery.min.js');
+ $this->script('jquery.form.min.js');
+ $this->script('jquery-ui.min.js');
+ $this->script('jquery.cookie.min.js');
+ $this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/json2.min.js').'"); }');
+ $this->script('jquery.joverlay.min.js');
+ } else {
+ $this->script('jquery.js');
+ $this->script('jquery.form.js');
+ $this->script('jquery-ui.min.js');
+ $this->script('jquery.cookie.js');
+ $this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/json2.js').'"); }');
+ $this->script('jquery.joverlay.js');
+ }
Event::handle('EndShowJQueryScripts', array($this));
}
if (Event::handle('StartShowStatusNetScripts', array($this)) &&
Event::handle('StartShowLaconicaScripts', array($this))) {
- $this->script('util.min.js');
+ if (common_config('site', 'minify')) {
+ $this->script('util.min.js');
+ } else {
+ $this->script('util.js');
+ $this->script('xbImportNode.js');
+ $this->script('geometa.js');
+ }
$this->showScriptMessages();
// Frame-busting code to avoid clickjacking attacks.
$this->inlineScript('if (window.top !== window.self) { window.top.location.href = window.self.location.href; }');
@@ -324,6 +354,12 @@ class Action extends HTMLOutputter // lawsuit
// TRANS: Localized tooltip for '...' expansion button on overlong remote messages.
$messages['showmore_tooltip'] = _m('TOOLTIP', 'Show more');
+ // TRANS: Inline reply form submit button: submits a reply comment.
+ $messages['reply_submit'] = _m('BUTTON', 'Reply');
+
+ // TRANS: Placeholder text for inline reply form. Clicking in this box will turn it into a mini notice form.
+ $messages['reply_placeholder'] = _m('Write a reply...');
+
$messages = array_merge($messages, $this->getScriptMessages());
Event::handle('EndScriptMessages', array($this, &$messages));
@@ -464,14 +500,7 @@ class Action extends HTMLOutputter // lawsuit
Event::handle('EndShowSiteNotice', array($this));
}
- if (common_logged_in()) {
- if (Event::handle('StartShowNoticeForm', array($this))) {
- $this->showNoticeForm();
- Event::handle('EndShowNoticeForm', array($this));
- }
- } else {
- $this->showAnonymousMessage();
- }
+
$this->elementEnd('div');
}
@@ -489,9 +518,13 @@ class Action extends HTMLOutputter // lawsuit
$user = User::singleUser();
$url = common_local_url('showstream',
array('nickname' => $user->nickname));
+ } else if (common_logged_in()) {
+ $cur = common_current_user();
+ $url = common_local_url('all', array('nickname' => $cur->nickname));
} else {
$url = common_local_url('public');
}
+
$this->elementStart('a', array('class' => 'url home bookmark',
'href' => $url));
@@ -526,6 +559,7 @@ class Action extends HTMLOutputter // lawsuit
$this->text(' ');
$this->element('span', array('class' => 'fn org'), common_config('site', 'name'));
$this->elementEnd('a');
+
Event::handle('EndAddressData', array($this));
}
$this->elementEnd('address');
@@ -538,83 +572,10 @@ class Action extends HTMLOutputter // lawsuit
*/
function showPrimaryNav()
{
- $user = common_current_user();
- $this->elementStart('dl', array('id' => 'site_nav_global_primary'));
- // TRANS: DT element for primary navigation menu. String is hidden in default CSS.
- $this->element('dt', null, _('Primary site navigation'));
- $this->elementStart('dd');
- $this->elementStart('ul', array('class' => 'nav'));
- if (Event::handle('StartPrimaryNav', array($this))) {
- if ($user) {
- // TRANS: Tooltip for main menu option "Personal".
- $tooltip = _m('TOOLTIP', 'Personal profile and friends timeline');
- $this->menuItem(common_local_url('all', array('nickname' => $user->nickname)),
- // TRANS: Main menu option when logged in for access to personal profile and friends timeline.
- _m('MENU', 'Personal'), $tooltip, false, 'nav_home');
- // TRANS: Tooltip for main menu option "Account".
- $tooltip = _m('TOOLTIP', 'Change your email, avatar, password, profile');
- $this->menuItem(common_local_url('profilesettings'),
- // TRANS: Main menu option when logged in for access to user settings.
- _('Account'), $tooltip, false, 'nav_account');
- // TRANS: Tooltip for main menu option "Services".
- $tooltip = _m('TOOLTIP', 'Connect to services');
- $this->menuItem(common_local_url('oauthconnectionssettings'),
- // TRANS: Main menu option when logged in and connection are possible for access to options to connect to other services.
- _('Connect'), $tooltip, false, 'nav_connect');
- if ($user->hasRight(Right::CONFIGURESITE)) {
- // TRANS: Tooltip for menu option "Admin".
- $tooltip = _m('TOOLTIP', 'Change site configuration');
- $this->menuItem(common_local_url('siteadminpanel'),
- // TRANS: Main menu option when logged in and site admin for access to site configuration.
- _m('MENU', 'Admin'), $tooltip, false, 'nav_admin');
- }
- if (common_config('invite', 'enabled')) {
- // TRANS: Tooltip for main menu option "Invite".
- $tooltip = _m('TOOLTIP', 'Invite friends and colleagues to join you on %s');
- $this->menuItem(common_local_url('invite'),
- // TRANS: Main menu option when logged in and invitations are allowed for inviting new users.
- _m('MENU', 'Invite'),
- sprintf($tooltip,
- common_config('site', 'name')),
- false, 'nav_invitecontact');
- }
- // TRANS: Tooltip for main menu option "Logout"
- $tooltip = _m('TOOLTIP', 'Logout from the site');
- $this->menuItem(common_local_url('logout'),
- // TRANS: Main menu option when logged in to log out the current user.
- _m('MENU', 'Logout'), $tooltip, false, 'nav_logout');
- }
- else {
- if (!common_config('site', 'closed') && !common_config('site', 'inviteonly')) {
- // TRANS: Tooltip for main menu option "Register".
- $tooltip = _m('TOOLTIP', 'Create an account');
- $this->menuItem(common_local_url('register'),
- // TRANS: Main menu option when not logged in to register a new account.
- _m('MENU', 'Register'), $tooltip, false, 'nav_register');
- }
- // TRANS: Tooltip for main menu option "Login".
- $tooltip = _m('TOOLTIP', 'Login to the site');
- $this->menuItem(common_local_url('login'),
- // TRANS: Main menu option when not logged in to log in.
- _m('MENU', 'Login'), $tooltip, false, 'nav_login');
- }
- // TRANS: Tooltip for main menu option "Help".
- $tooltip = _m('TOOLTIP', 'Help me!');
- $this->menuItem(common_local_url('doc', array('title' => 'help')),
- // TRANS: Main menu option for help on the StatusNet site.
- _m('MENU', 'Help'), $tooltip, false, 'nav_help');
- if ($user || !common_config('site', 'private')) {
- // TRANS: Tooltip for main menu option "Search".
- $tooltip = _m('TOOLTIP', 'Search for people or text');
- $this->menuItem(common_local_url('peoplesearch'),
- // TRANS: Main menu option when logged in or when the StatusNet instance is not private.
- _m('MENU', 'Search'), $tooltip, false, 'nav_search');
- }
- Event::handle('EndPrimaryNav', array($this));
- }
- $this->elementEnd('ul');
- $this->elementEnd('dd');
- $this->elementEnd('dl');
+ $this->elementStart('div', array('id' => 'site_nav_global_primary'));
+ $pn = new PrimaryNav($this);
+ $pn->show();
+ $this->elementEnd('div');
}
/**
@@ -627,14 +588,10 @@ class Action extends HTMLOutputter // lawsuit
// Revist. Should probably do an hAtom pattern here
$text = common_config('site', 'notice');
if ($text) {
- $this->elementStart('dl', array('id' => 'site_notice',
+ $this->elementStart('div', array('id' => 'site_notice',
'class' => 'system_notice'));
- // TRANS: DT element for site notice. String is hidden in default CSS.
- $this->element('dt', null, _('Site notice'));
- $this->elementStart('dd', null);
$this->raw($text);
- $this->elementEnd('dd');
- $this->elementEnd('dl');
+ $this->elementEnd('div');
}
}
@@ -647,8 +604,68 @@ class Action extends HTMLOutputter // lawsuit
*/
function showNoticeForm()
{
- $notice_form = new NoticeForm($this);
- $notice_form->show();
+ $tabs = array('status' => _('Status'));
+
+ $this->elementStart('div', 'input_forms');
+
+ if (Event::handle('StartShowEntryForms', array(&$tabs))) {
+
+ $this->elementStart('ul', array('class' => 'nav',
+ 'id' => 'input_form_nav'));
+
+ foreach ($tabs as $tag => $title) {
+
+ $attrs = array('id' => 'input_form_nav_'.$tag,
+ 'class' => 'input_form_nav_tab');
+
+ if ($tag == 'status') {
+ // We're actually showing the placeholder form,
+ // but we special-case the 'Status' tab as if
+ // it were a small version of it.
+ $attrs['class'] .= ' current';
+ }
+ $this->elementStart('li', $attrs);
+
+ $this->element('a',
+ array('href' => 'javascript:SN.U.switchInputFormTab("'.$tag.'")'),
+ $title);
+ $this->elementEnd('li');
+ }
+
+ $this->elementEnd('ul');
+
+ $attrs = array('class' => 'input_form current',
+ 'id' => 'input_form_placeholder');
+ $this->elementStart('div', $attrs);
+ $form = new NoticePlaceholderForm($this);
+ $form->show();
+ $this->elementEnd('div');
+
+ foreach ($tabs as $tag => $title) {
+
+ $attrs = array('class' => 'input_form',
+ 'id' => 'input_form_'.$tag);
+
+ $this->elementStart('div', $attrs);
+
+ $form = null;
+
+ if (Event::handle('StartMakeEntryForm', array($tag, $this, &$form))) {
+ if ($tag == 'status') {
+ $form = new NoticeForm($this);
+ }
+ Event::handle('EndMakeEntryForm', array($tag, $this, $form));
+ }
+
+ if (!empty($form)) {
+ $form->show();
+ }
+
+ $this->elementEnd('div');
+ }
+ }
+
+ $this->elementEnd('div');
}
/**
@@ -673,6 +690,9 @@ class Action extends HTMLOutputter // lawsuit
function showCore()
{
$this->elementStart('div', array('id' => 'core'));
+ $this->elementStart('div', array('id' => 'aside_primary_wrapper'));
+ $this->elementStart('div', array('id' => 'content_wrapper'));
+ $this->elementStart('div', array('id' => 'site_nav_local_views_wrapper'));
if (Event::handle('StartShowLocalNavBlock', array($this))) {
$this->showLocalNavBlock();
Event::handle('EndShowLocalNavBlock', array($this));
@@ -686,6 +706,9 @@ class Action extends HTMLOutputter // lawsuit
Event::handle('EndShowAside', array($this));
}
$this->elementEnd('div');
+ $this->elementEnd('div');
+ $this->elementEnd('div');
+ $this->elementEnd('div');
}
/**
@@ -695,13 +718,26 @@ class Action extends HTMLOutputter // lawsuit
*/
function showLocalNavBlock()
{
- $this->elementStart('dl', array('id' => 'site_nav_local_views'));
- // TRANS: DT element for local views block. String is hidden in default CSS.
- $this->element('dt', null, _('Local views'));
- $this->elementStart('dd');
+ // Need to have this ID for CSS; I'm too lazy to add it to
+ // all menus
+ $this->elementStart('div', array('id' => 'site_nav_local_views'));
+ // Cheat cheat cheat!
$this->showLocalNav();
- $this->elementEnd('dd');
- $this->elementEnd('dl');
+ $this->elementEnd('div');
+ }
+
+ /**
+ * If there's a logged-in user, show a bit of login context
+ *
+ * @return nothing
+ */
+
+ function showProfileBlock()
+ {
+ if (common_logged_in()) {
+ $block = new DefaultProfileBlock($this);
+ $block->show();
+ }
}
/**
@@ -713,7 +749,43 @@ class Action extends HTMLOutputter // lawsuit
*/
function showLocalNav()
{
- // does nothing by default
+ $nav = new DefaultLocalNav($this);
+ $nav->show();
+ }
+
+ /**
+ * Show menu for an object (group, profile)
+ *
+ * This block will only show if a subclass has overridden
+ * the showObjectNav() method.
+ *
+ * @return nothing
+ */
+ function showObjectNavBlock()
+ {
+ $rmethod = new ReflectionMethod($this, 'showObjectNav');
+ $dclass = $rmethod->getDeclaringClass()->getName();
+
+ if ($dclass != 'Action') {
+ // Need to have this ID for CSS; I'm too lazy to add it to
+ // all menus
+ $this->elementStart('div', array('id' => 'site_nav_object',
+ 'class' => 'section'));
+ $this->showObjectNav();
+ $this->elementEnd('div');
+ }
+ }
+
+ /**
+ * Show object navigation.
+ *
+ * If there are things to do with this object, show it here.
+ *
+ * @return nothing
+ */
+ function showObjectNav()
+ {
+ /* Nothing here. */
}
/**
@@ -724,6 +796,12 @@ class Action extends HTMLOutputter // lawsuit
function showContentBlock()
{
$this->elementStart('div', array('id' => 'content'));
+ if (common_logged_in()) {
+ if (Event::handle('StartShowNoticeForm', array($this))) {
+ $this->showNoticeForm();
+ Event::handle('EndShowNoticeForm', array($this));
+ }
+ }
if (Event::handle('StartShowPageTitle', array($this))) {
$this->showPageTitle();
Event::handle('EndShowPageTitle', array($this));
@@ -764,17 +842,13 @@ class Action extends HTMLOutputter // lawsuit
if ($dclass != 'Action' || Event::hasHandler('StartShowPageNotice')) {
- $this->elementStart('dl', array('id' => 'page_notice',
+ $this->elementStart('div', array('id' => 'page_notice',
'class' => 'system_notice'));
- // TRANS: DT element for page notice. String is hidden in default CSS.
- $this->element('dt', null, _('Page notice'));
- $this->elementStart('dd');
if (Event::handle('StartShowPageNotice', array($this))) {
$this->showPageNotice();
Event::handle('EndShowPageNotice', array($this));
}
- $this->elementEnd('dd');
- $this->elementEnd('dl');
+ $this->elementEnd('div');
}
}
@@ -809,6 +883,11 @@ class Action extends HTMLOutputter // lawsuit
{
$this->elementStart('div', array('id' => 'aside_primary',
'class' => 'aside'));
+ $this->showProfileBlock();
+ if (Event::handle('StartShowObjectNavBlock', array($this))) {
+ $this->showObjectNavBlock();
+ Event::handle('EndShowObjectNavBlock', array($this));
+ }
if (Event::handle('StartShowSections', array($this))) {
$this->showSections();
Event::handle('EndShowSections', array($this));
@@ -869,48 +948,8 @@ class Action extends HTMLOutputter // lawsuit
*/
function showSecondaryNav()
{
- $this->elementStart('dl', array('id' => 'site_nav_global_secondary'));
- // TRANS: DT element for secondary navigation menu. String is hidden in default CSS.
- $this->element('dt', null, _('Secondary site navigation'));
- $this->elementStart('dd', null);
- $this->elementStart('ul', array('class' => 'nav'));
- if (Event::handle('StartSecondaryNav', array($this))) {
- $this->menuItem(common_local_url('doc', array('title' => 'help')),
- // TRANS: Secondary navigation menu option leading to help on StatusNet.
- _('Help'));
- $this->menuItem(common_local_url('doc', array('title' => 'about')),
- // TRANS: Secondary navigation menu option leading to text about StatusNet site.
- _('About'));
- $this->menuItem(common_local_url('doc', array('title' => 'faq')),
- // TRANS: Secondary navigation menu option leading to Frequently Asked Questions.
- _('FAQ'));
- $bb = common_config('site', 'broughtby');
- if (!empty($bb)) {
- $this->menuItem(common_local_url('doc', array('title' => 'tos')),
- // TRANS: Secondary navigation menu option leading to Terms of Service.
- _('TOS'));
- }
- $this->menuItem(common_local_url('doc', array('title' => 'privacy')),
- // TRANS: Secondary navigation menu option leading to privacy policy.
- _('Privacy'));
- $this->menuItem(common_local_url('doc', array('title' => 'source')),
- // TRANS: Secondary navigation menu option. Leads to information about StatusNet and its license.
- _('Source'));
- $this->menuItem(common_local_url('version'),
- // TRANS: Secondary navigation menu option leading to version information on the StatusNet site.
- _('Version'));
- $this->menuItem(common_local_url('doc', array('title' => 'contact')),
- // TRANS: Secondary navigation menu option leading to e-mail contact information on the
- // TRANS: StatusNet site, where to report bugs, ...
- _('Contact'));
- $this->menuItem(common_local_url('doc', array('title' => 'badge')),
- // TRANS: Secondary navigation menu option. Leads to information about embedding a timeline widget.
- _('Badge'));
- Event::handle('EndSecondaryNav', array($this));
- }
- $this->elementEnd('ul');
- $this->elementEnd('dd');
- $this->elementEnd('dl');
+ $sn = new SecondaryNav($this);
+ $sn->show();
}
/**
@@ -920,10 +959,8 @@ class Action extends HTMLOutputter // lawsuit
*/
function showLicenses()
{
- $this->elementStart('dl', array('id' => 'licenses'));
$this->showStatusNetLicense();
$this->showContentLicense();
- $this->elementEnd('dl');
}
/**
@@ -933,9 +970,6 @@ class Action extends HTMLOutputter // lawsuit
*/
function showStatusNetLicense()
{
- // TRANS: DT element for StatusNet software license.
- $this->element('dt', array('id' => 'site_statusnet_license'), _('StatusNet software license'));
- $this->elementStart('dd', null);
if (common_config('site', 'broughtby')) {
// TRANS: First sentence of the StatusNet site license. Used if 'broughtby' is set.
// TRANS: Text between [] is a link description, text between () is the link itself.
@@ -954,7 +988,6 @@ class Action extends HTMLOutputter // lawsuit
$instr .= sprintf(_('It runs the [StatusNet](http://status.net/) microblogging software, version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), STATUSNET_VERSION);
$output = common_markup_to_html($instr);
$this->raw($output);
- $this->elementEnd('dd');
// do it
}
@@ -966,10 +999,6 @@ class Action extends HTMLOutputter // lawsuit
function showContentLicense()
{
if (Event::handle('StartShowContentLicense', array($this))) {
- // TRANS: DT element for StatusNet site content license.
- $this->element('dt', array('id' => 'site_content_license'), _('Site content license'));
- $this->elementStart('dd', array('id' => 'site_content_license_cc'));
-
switch (common_config('license', 'type')) {
case 'private':
// TRANS: Content license displayed when license is set to 'private'.
@@ -1030,7 +1059,6 @@ class Action extends HTMLOutputter // lawsuit
break;
}
- $this->elementEnd('dd');
Event::handle('EndShowContentLicense', array($this));
}
}
@@ -1351,11 +1379,8 @@ class Action extends HTMLOutputter // lawsuit
{
// Does a little before-after block for next/prev page
if ($have_before || $have_after) {
- $this->elementStart('dl', 'pagination');
- // TRANS: DT element for pagination (previous/next, etc.).
- $this->element('dt', null, _('Pagination'));
- $this->elementStart('dd', null);
- $this->elementStart('ul', array('class' => 'nav'));
+ $this->elementStart('ul', array('class' => 'nav',
+ 'id' => 'pagination'));
}
if ($have_before) {
$pargs = array('page' => $page-1);
@@ -1379,8 +1404,6 @@ class Action extends HTMLOutputter // lawsuit
}
if ($have_before || $have_after) {
$this->elementEnd('ul');
- $this->elementEnd('dd');
- $this->elementEnd('dl');
}
}
diff --git a/lib/activityobject.php b/lib/activityobject.php
index a69e1a1b42..241f99564f 100644
--- a/lib/activityobject.php
+++ b/lib/activityobject.php
@@ -422,7 +422,7 @@ class ActivityObject
if (Event::handle('StartActivityObjectFromNotice', array($notice, &$object))) {
- $object->type = ActivityObject::NOTE;
+ $object->type = (empty($notice->object_type)) ? ActivityObject::NOTE : $notice->object_type;
$object->id = $notice->uri;
$object->title = $notice->content;
@@ -533,89 +533,95 @@ class ActivityObject
$xo->elementStart($tag);
}
- $xo->element('activity:object-type', null, $this->type);
+ if (Event::handle('StartActivityObjectOutputAtom', array($this, $xo))) {
+ $xo->element('activity:object-type', null, $this->type);
- // uses URI
+ // uses URI
- if ($tag == 'author') {
- $xo->element(self::URI, null, $this->id);
- } else {
- $xo->element(self::ID, null, $this->id);
- }
-
- if (!empty($this->title)) {
- $name = common_xml_safe_str($this->title);
if ($tag == 'author') {
- // XXX: Backward compatibility hack -- atom:name should contain
- // full name here, instead of nickname, i.e.: $name. Change
- // this in the next version.
- $xo->element(self::NAME, null, $this->poco->preferredUsername);
+ $xo->element(self::URI, null, $this->id);
} else {
- $xo->element(self::TITLE, null, $name);
+ $xo->element(self::ID, null, $this->id);
}
- }
- if (!empty($this->summary)) {
- $xo->element(
- self::SUMMARY,
- null,
- common_xml_safe_str($this->summary)
- );
- }
+ if (!empty($this->title)) {
+ $name = common_xml_safe_str($this->title);
+ if ($tag == 'author') {
+ // XXX: Backward compatibility hack -- atom:name should contain
+ // full name here, instead of nickname, i.e.: $name. Change
+ // this in the next version.
+ $xo->element(self::NAME, null, $this->poco->preferredUsername);
+ } else {
+ $xo->element(self::TITLE, null, $name);
+ }
+ }
- if (!empty($this->content)) {
- // XXX: assuming HTML content here
- $xo->element(
- ActivityUtils::CONTENT,
- array('type' => 'html'),
- common_xml_safe_str($this->content)
- );
- }
-
- if (!empty($this->link)) {
- $xo->element(
- 'link',
- array(
- 'rel' => 'alternate',
- 'type' => 'text/html',
- 'href' => $this->link
- ),
- null
- );
- }
-
- if ($this->type == ActivityObject::PERSON
- || $this->type == ActivityObject::GROUP) {
-
- foreach ($this->avatarLinks as $avatar) {
+ if (!empty($this->summary)) {
$xo->element(
- 'link', array(
- 'rel' => 'avatar',
- 'type' => $avatar->type,
- 'media:width' => $avatar->width,
- 'media:height' => $avatar->height,
- 'href' => $avatar->url
+ self::SUMMARY,
+ null,
+ common_xml_safe_str($this->summary)
+ );
+ }
+
+ if (!empty($this->content)) {
+ // XXX: assuming HTML content here
+ $xo->element(
+ ActivityUtils::CONTENT,
+ array('type' => 'html'),
+ common_xml_safe_str($this->content)
+ );
+ }
+
+ if (!empty($this->link)) {
+ $xo->element(
+ 'link',
+ array(
+ 'rel' => 'alternate',
+ 'type' => 'text/html',
+ 'href' => $this->link
),
null
);
}
- }
- if (!empty($this->geopoint)) {
- $xo->element(
- 'georss:point',
- null,
- $this->geopoint
- );
- }
+ if ($this->type == ActivityObject::PERSON
+ || $this->type == ActivityObject::GROUP) {
- if (!empty($this->poco)) {
- $this->poco->outputTo($xo);
- }
+ foreach ($this->avatarLinks as $avatar) {
+ $xo->element(
+ 'link', array(
+ 'rel' => 'avatar',
+ 'type' => $avatar->type,
+ 'media:width' => $avatar->width,
+ 'media:height' => $avatar->height,
+ 'href' => $avatar->url
+ ),
+ null
+ );
+ }
+ }
- foreach ($this->extra as $el) {
- list($extraTag, $attrs, $content) = $el;
- $xo->element($extraTag, $attrs, $content);
+ if (!empty($this->geopoint)) {
+ $xo->element(
+ 'georss:point',
+ null,
+ $this->geopoint
+ );
+ }
+
+ if (!empty($this->poco)) {
+ $this->poco->outputTo($xo);
+ }
+
+ // @fixme there's no way here to make a tree; elements can only contain plaintext
+ // @fixme these may collide with JSON extensions
+ foreach ($this->extra as $el) {
+ list($extraTag, $attrs, $content) = $el;
+ $xo->element($extraTag, $attrs, $content);
+ }
+
+ Event::handle('EndActivityObjectOutputAtom', array($this, $xo));
}
if (!empty($tag)) {
@@ -645,91 +651,96 @@ class ActivityObject
{
$object = array();
- // XXX: attachedObjects are added by Activity
+ if (Event::handle('StartActivityObjectOutputJson', array($this, &$object))) {
+ // XXX: attachedObjects are added by Activity
- // displayName
- $object['displayName'] = $this->title;
+ // displayName
+ $object['displayName'] = $this->title;
- // TODO: downstreamDuplicates
+ // TODO: downstreamDuplicates
- // embedCode (used for video)
+ // embedCode (used for video)
- // id
- //
- // XXX: Should we use URL here? or a crazy tag URI?
- $object['id'] = $this->id;
+ // id
+ //
+ // XXX: Should we use URL here? or a crazy tag URI?
+ $object['id'] = $this->id;
- if ($this->type == ActivityObject::PERSON
- || $this->type == ActivityObject::GROUP) {
+ if ($this->type == ActivityObject::PERSON
+ || $this->type == ActivityObject::GROUP) {
- // XXX: Not sure what the best avatar is to use for the
- // author's "image". For now, I'm using the large size.
+ // XXX: Not sure what the best avatar is to use for the
+ // author's "image". For now, I'm using the large size.
- $avatarLarge = null;
- $avatarMediaLinks = array();
+ $avatarLarge = null;
+ $avatarMediaLinks = array();
- foreach ($this->avatarLinks as $a) {
+ foreach ($this->avatarLinks as $a) {
- // Make a MediaLink for every other Avatar
- $avatar = new ActivityStreamsMediaLink(
- $a->url,
- $a->width,
- $a->height,
- $a->type,
- 'avatar'
- );
+ // Make a MediaLink for every other Avatar
+ $avatar = new ActivityStreamsMediaLink(
+ $a->url,
+ $a->width,
+ $a->height,
+ $a->type,
+ 'avatar'
+ );
- // Find the big avatar to use as the "image"
- if ($a->height == AVATAR_PROFILE_SIZE) {
- $imgLink = $avatar;
+ // Find the big avatar to use as the "image"
+ if ($a->height == AVATAR_PROFILE_SIZE) {
+ $imgLink = $avatar;
+ }
+
+ $avatarMediaLinks[] = $avatar->asArray();
}
- $avatarMediaLinks[] = $avatar->asArray();
+ $object['avatarLinks'] = $avatarMediaLinks; // extension
+
+ // image
+ $object['image'] = $imgLink->asArray();
}
- $object['avatarLinks'] = $avatarMediaLinks; // extension
+ // objectType
+ //
+ // We can probably use the whole schema URL here but probably the
+ // relative simple name is easier to parse
+ // @fixme this breaks extension URIs
+ $object['type'] = substr($this->type, strrpos($this->type, '/') + 1);
- // image
- $object['image'] = $imgLink->asArray();
+ // summary
+ $object['summary'] = $this->summary;
+
+ // TODO: upstreamDuplicates
+
+ // url (XXX: need to put the right thing here...)
+ $object['url'] = $this->id;
+
+ /* Extensions */
+ // @fixme these may collide with XML extensions
+ // @fixme multiple tags of same name will overwrite each other
+ // @fixme text content from XML extensions will be lost
+ foreach ($this->extra as $e) {
+ list($objectName, $props, $txt) = $e;
+ $object[$objectName] = $props;
+ }
+
+ // GeoJSON
+
+ if (!empty($this->geopoint)) {
+
+ list($lat, $long) = explode(' ', $this->geopoint);
+
+ $object['geopoint'] = array(
+ 'type' => 'Point',
+ 'coordinates' => array($lat, $long)
+ );
+ }
+
+ if (!empty($this->poco)) {
+ $object['contact'] = $this->poco->asArray();
+ }
+ Event::handle('EndActivityObjectOutputJson', array($this, &$object));
}
-
- // objectType
- //
- // We can probably use the whole schema URL here but probably the
- // relative simple name is easier to parse
- $object['type'] = substr($this->type, strrpos($this->type, '/') + 1);
-
- // summary
- $object['summary'] = $this->summary;
-
- // TODO: upstreamDuplicates
-
- // url (XXX: need to put the right thing here...)
- $object['url'] = $this->id;
-
- /* Extensions */
-
- foreach ($this->extra as $e) {
- list($objectName, $props, $txt) = $e;
- $object[$objectName] = $props;
- }
-
- // GeoJSON
-
- if (!empty($this->geopoint)) {
-
- list($lat, $long) = explode(' ', $this->geopoint);
-
- $object['geopoint'] = array(
- 'type' => 'Point',
- 'coordinates' => array($lat, $long)
- );
- }
-
- if (!empty($this->poco)) {
- $object['contact'] = $this->poco->asArray();
- }
-
return array_filter($object);
}
}
diff --git a/lib/activitystreamjsondocument.php b/lib/activitystreamjsondocument.php
index 2b99d19eb7..0c64ebb998 100644
--- a/lib/activitystreamjsondocument.php
+++ b/lib/activitystreamjsondocument.php
@@ -127,7 +127,7 @@ class ActivityStreamJSONDocument
function addLink($url = null, $rel = null, $mediaType = null)
{
$link = new ActivityStreamsLink($url, $rel, $mediaType);
- $this->doc['link'][] = $link->asArray();
+ $this->doc['links'][] = $link->asArray();
}
/*
diff --git a/lib/adminpanelaction.php b/lib/adminpanelaction.php
index fae9f4fa57..5e7e284b5c 100644
--- a/lib/adminpanelaction.php
+++ b/lib/adminpanelaction.php
@@ -290,122 +290,8 @@ class AdminPanelAction extends Action
return $isOK;
}
-}
-/**
- * Menu for public group of actions
- *
- * @category Output
- * @package StatusNet
- * @author Evan Prodromou
- * @author Sarven Capadisli
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- *
- * @see Widget
- */
-class AdminPanelNav extends Widget
-{
- var $action = null;
-
- /**
- * Construction
- *
- * @param Action $action current action, used for output
- */
- function __construct($action=null)
+ function showProfileBlock()
{
- parent::__construct($action);
- $this->action = $action;
- }
-
- /**
- * Show the menu
- *
- * @return void
- */
- function show()
- {
- $action_name = $this->action->trimmed('action');
-
- $this->action->elementStart('ul', array('class' => 'nav'));
-
- if (Event::handle('StartAdminPanelNav', array($this))) {
-
- if (AdminPanelAction::canAdmin('site')) {
- // TRANS: Menu item title/tooltip
- $menu_title = _('Basic site configuration');
- // TRANS: Menu item for site administration
- $this->out->menuItem(common_local_url('siteadminpanel'), _m('MENU', 'Site'),
- $menu_title, $action_name == 'siteadminpanel', 'nav_site_admin_panel');
- }
-
- if (AdminPanelAction::canAdmin('design')) {
- // TRANS: Menu item title/tooltip
- $menu_title = _('Design configuration');
- // TRANS: Menu item for site administration
- $this->out->menuItem(common_local_url('designadminpanel'), _m('MENU', 'Design'),
- $menu_title, $action_name == 'designadminpanel', 'nav_design_admin_panel');
- }
-
- if (AdminPanelAction::canAdmin('user')) {
- // TRANS: Menu item title/tooltip
- $menu_title = _('User configuration');
- // TRANS: Menu item for site administration
- $this->out->menuItem(common_local_url('useradminpanel'), _('User'),
- $menu_title, $action_name == 'useradminpanel', 'nav_user_admin_panel');
- }
-
- if (AdminPanelAction::canAdmin('access')) {
- // TRANS: Menu item title/tooltip
- $menu_title = _('Access configuration');
- // TRANS: Menu item for site administration
- $this->out->menuItem(common_local_url('accessadminpanel'), _('Access'),
- $menu_title, $action_name == 'accessadminpanel', 'nav_access_admin_panel');
- }
-
- if (AdminPanelAction::canAdmin('paths')) {
- // TRANS: Menu item title/tooltip
- $menu_title = _('Paths configuration');
- // TRANS: Menu item for site administration
- $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'),
- $menu_title, $action_name == 'pathsadminpanel', 'nav_paths_admin_panel');
- }
-
- if (AdminPanelAction::canAdmin('sessions')) {
- // TRANS: Menu item title/tooltip
- $menu_title = _('Sessions configuration');
- // TRANS: Menu item for site administration
- $this->out->menuItem(common_local_url('sessionsadminpanel'), _('Sessions'),
- $menu_title, $action_name == 'sessionsadminpanel', 'nav_sessions_admin_panel');
- }
-
- if (AdminPanelAction::canAdmin('sitenotice')) {
- // TRANS: Menu item title/tooltip
- $menu_title = _('Edit site notice');
- // TRANS: Menu item for site administration
- $this->out->menuItem(common_local_url('sitenoticeadminpanel'), _('Site notice'),
- $menu_title, $action_name == 'sitenoticeadminpanel', 'nav_sitenotice_admin_panel');
- }
-
- if (AdminPanelAction::canAdmin('snapshot')) {
- // TRANS: Menu item title/tooltip
- $menu_title = _('Snapshots configuration');
- // TRANS: Menu item for site administration
- $this->out->menuItem(common_local_url('snapshotadminpanel'), _('Snapshots'),
- $menu_title, $action_name == 'snapshotadminpanel', 'nav_snapshot_admin_panel');
- }
-
- if (AdminPanelAction::canAdmin('license')) {
- // TRANS: Menu item title/tooltip
- $menu_title = _('Set site license');
- // TRANS: Menu item for site administration
- $this->out->menuItem(common_local_url('licenseadminpanel'), _('License'),
- $menu_title, $action_name == 'licenseadminpanel', 'nav_license_admin_panel');
- }
-
- Event::handle('EndAdminPanelNav', array($this));
- }
- $this->action->elementEnd('ul');
}
}
diff --git a/lib/adminpanelnav.php b/lib/adminpanelnav.php
new file mode 100644
index 0000000000..2c9d83ceba
--- /dev/null
+++ b/lib/adminpanelnav.php
@@ -0,0 +1,167 @@
+.
+ *
+ * @category Menu
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Menu for admin panels
+ *
+ * @category Output
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @author Sarven Capadisli
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class AdminPanelNav extends Menu
+{
+ /**
+ * Show the menu
+ *
+ * @return void
+ */
+ function show()
+ {
+ $action_name = $this->action->trimmed('action');
+ $user = common_current_user();
+ $nickname = $user->nickname;
+ $name = $user->getProfile()->getBestName();
+
+ // Stub section w/ home link
+ $this->action->elementStart('ul');
+ $this->action->element('h3', null, _('Home'));
+ $this->action->elementStart('ul', 'nav');
+ $this->out->menuItem(common_local_url('all', array('nickname' =>
+ $nickname)),
+ _('Home'),
+ sprintf(_('%s and friends'), $name),
+ $this->action == 'all', 'nav_timeline_personal');
+
+ $this->action->elementEnd('ul');
+ $this->action->elementEnd('ul');
+
+ $this->action->elementStart('ul');
+ $this->action->element('h3', null, _('Admin'));
+ $this->action->elementStart('ul', array('class' => 'nav'));
+
+ if (Event::handle('StartAdminPanelNav', array($this))) {
+
+ if (AdminPanelAction::canAdmin('site')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('Basic site configuration');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('siteadminpanel'), _m('MENU', 'Site'),
+ $menu_title, $action_name == 'siteadminpanel', 'nav_site_admin_panel');
+ }
+
+ if (AdminPanelAction::canAdmin('design')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('Design configuration');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('designadminpanel'), _m('MENU', 'Design'),
+ $menu_title, $action_name == 'designadminpanel', 'nav_design_admin_panel');
+ }
+
+ if (AdminPanelAction::canAdmin('user')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('User configuration');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('useradminpanel'), _('User'),
+ $menu_title, $action_name == 'useradminpanel', 'nav_user_admin_panel');
+ }
+
+ if (AdminPanelAction::canAdmin('access')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('Access configuration');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('accessadminpanel'), _('Access'),
+ $menu_title, $action_name == 'accessadminpanel', 'nav_access_admin_panel');
+ }
+
+ if (AdminPanelAction::canAdmin('paths')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('Paths configuration');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('pathsadminpanel'), _('Paths'),
+ $menu_title, $action_name == 'pathsadminpanel', 'nav_paths_admin_panel');
+ }
+
+ if (AdminPanelAction::canAdmin('sessions')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('Sessions configuration');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('sessionsadminpanel'), _('Sessions'),
+ $menu_title, $action_name == 'sessionsadminpanel', 'nav_sessions_admin_panel');
+ }
+
+ if (AdminPanelAction::canAdmin('sitenotice')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('Edit site notice');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('sitenoticeadminpanel'), _('Site notice'),
+ $menu_title, $action_name == 'sitenoticeadminpanel', 'nav_sitenotice_admin_panel');
+ }
+
+ if (AdminPanelAction::canAdmin('snapshot')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('Snapshots configuration');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('snapshotadminpanel'), _('Snapshots'),
+ $menu_title, $action_name == 'snapshotadminpanel', 'nav_snapshot_admin_panel');
+ }
+
+ if (AdminPanelAction::canAdmin('license')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('Set site license');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('licenseadminpanel'), _('License'),
+ $menu_title, $action_name == 'licenseadminpanel', 'nav_license_admin_panel');
+ }
+
+ if (AdminPanelAction::canAdmin('plugins')) {
+ // TRANS: Menu item title/tooltip
+ $menu_title = _('Plugins configuration');
+ // TRANS: Menu item for site administration
+ $this->out->menuItem(common_local_url('pluginsadminpanel'), _('Plugins'),
+ $menu_title, $action_name == 'pluginsadminpanel', 'nav_design_admin_panel');
+ }
+
+ Event::handle('EndAdminPanelNav', array($this));
+ }
+ $this->action->elementEnd('ul');
+ $this->action->elementEnd('ul');
+ }
+}
diff --git a/lib/apiaction.php b/lib/apiaction.php
index 5d70425720..ebda36db7f 100644
--- a/lib/apiaction.php
+++ b/lib/apiaction.php
@@ -98,6 +98,8 @@ if (!defined('STATUSNET')) {
exit(1);
}
+class ApiValidationException extends Exception { }
+
/**
* Contains most of the Twitter-compatible API output functions.
*
diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php
index c3d3f4d116..cf7c9acc14 100644
--- a/lib/attachmentlist.php
+++ b/lib/attachmentlist.php
@@ -92,19 +92,12 @@ class AttachmentList extends Widget
function showListStart()
{
- $this->out->elementStart('dl', array('id' =>'attachments',
- 'class' => 'entry-content'));
- // TRANS: DT element label in attachment list.
- $this->out->element('dt', null, _('Attachments'));
- $this->out->elementStart('dd');
- $this->out->elementStart('ol', array('class' => 'attachments'));
+ $this->out->elementStart('ol', array('class' => 'attachments entry-content'));
}
function showListEnd()
{
- $this->out->elementEnd('dd');
$this->out->elementEnd('ol');
- $this->out->elementEnd('dl');
}
/**
@@ -288,32 +281,22 @@ class Attachment extends AttachmentListItem
$this->out->elementStart('div', array('id' => 'oembed_info',
'class' => 'entry-content'));
if (!empty($this->oembed->author_name)) {
- $this->out->elementStart('dl', 'vcard author');
- // TRANS: DT element label in attachment list item.
- $this->out->element('dt', null, _('Author'));
- $this->out->elementStart('dd', 'fn');
+ $this->out->elementStart('div', 'fn vcard author');
if (empty($this->oembed->author_url)) {
$this->out->text($this->oembed->author_name);
} else {
$this->out->element('a', array('href' => $this->oembed->author_url,
'class' => 'url'), $this->oembed->author_name);
}
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
}
if (!empty($this->oembed->provider)) {
- $this->out->elementStart('dl', 'vcard');
- // TRANS: DT element label in attachment list item.
- $this->out->element('dt', null, _('Provider'));
- $this->out->elementStart('dd', 'fn');
+ $this->out->elementStart('div', 'fn vcard');
if (empty($this->oembed->provider_url)) {
$this->out->text($this->oembed->provider);
} else {
$this->out->element('a', array('href' => $this->oembed->provider_url,
'class' => 'url'), $this->oembed->provider);
}
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
}
$this->out->elementEnd('div');
}
diff --git a/lib/cache.php b/lib/cache.php
index bf0603c62d..eb4eb66656 100644
--- a/lib/cache.php
+++ b/lib/cache.php
@@ -80,7 +80,7 @@ class Cache
$base_key = common_config('cache', 'base');
if (empty($base_key)) {
- $base_key = common_keyize(common_config('site', 'name'));
+ $base_key = self::keyize(common_config('site', 'name'));
}
return 'statusnet:' . $base_key . ':' . $extra;
diff --git a/lib/channel.php b/lib/channel.php
index fbc2e8697c..ae9b2d214f 100644
--- a/lib/channel.php
+++ b/lib/channel.php
@@ -69,62 +69,6 @@ class CLIChannel extends Channel
}
}
-class XMPPChannel extends Channel
-{
- var $conn = null;
-
- function source()
- {
- return 'xmpp';
- }
-
- function __construct($conn)
- {
- $this->conn = $conn;
- }
-
- function on($user)
- {
- return $this->set_notify($user, 1);
- }
-
- function off($user)
- {
- return $this->set_notify($user, 0);
- }
-
- function output($user, $text)
- {
- $text = '['.common_config('site', 'name') . '] ' . $text;
- jabber_send_message($user->jabber, $text);
- }
-
- function error($user, $text)
- {
- $text = '['.common_config('site', 'name') . '] ' . $text;
- jabber_send_message($user->jabber, $text);
- }
-
- function set_notify(&$user, $notify)
- {
- $orig = clone($user);
- $user->jabbernotify = $notify;
- $result = $user->update($orig);
- if (!$result) {
- $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
- common_log(LOG_ERR,
- 'Could not set notify flag to ' . $notify .
- ' for user ' . common_log_objstring($user) .
- ': ' . $last_error->message);
- return false;
- } else {
- common_log(LOG_INFO,
- 'User ' . $user->nickname . ' set notify flag to ' . $notify);
- return true;
- }
- }
-}
-
class WebChannel extends Channel
{
var $out = null;
@@ -216,12 +160,12 @@ class MailChannel extends Channel
function on($user)
{
- return $this->set_notify($user, 1);
+ return $this->setNotify($user, 1);
}
function off($user)
{
- return $this->set_notify($user, 0);
+ return $this->setNotify($user, 0);
}
function output($user, $text)
@@ -246,7 +190,7 @@ class MailChannel extends Channel
return mail_send(array($this->addr), $headers, $text);
}
- function set_notify($user, $value)
+ function setNotify($user, $value)
{
$orig = clone($user);
$user->smsnotify = $value;
diff --git a/lib/command.php b/lib/command.php
index 29aa286d1d..03baa8212d 100644
--- a/lib/command.php
+++ b/lib/command.php
@@ -730,7 +730,7 @@ class OffCommand extends Command
}
function handle($channel)
{
- if ($other) {
+ if ($this->other) {
// TRANS: Error text shown when issuing the command "off" with a setting which has not yet been implemented.
$channel->error($this->user, _("Command not yet implemented."));
} else {
@@ -756,7 +756,7 @@ class OnCommand extends Command
function handle($channel)
{
- if ($other) {
+ if ($this->other) {
// TRANS: Error text shown when issuing the command "on" with a setting which has not yet been implemented.
$channel->error($this->user, _("Command not yet implemented."));
} else {
@@ -911,45 +911,88 @@ class HelpCommand extends Command
{
function handle($channel)
{
- $channel->output($this->user,
- // TRANS: Help text for commands. Do not translate the command names themselves; they are fixed strings.
- _("Commands:\n".
- "on - turn on notifications\n".
- "off - turn off notifications\n".
- "help - show this help\n".
- "follow - subscribe to user\n".
- "groups - lists the groups you have joined\n".
- "subscriptions - list the people you follow\n".
- "subscribers - list the people that follow you\n".
- "leave - unsubscribe from user\n".
- "d - direct message to user\n".
- "get - get last notice from user\n".
- "whois - get profile info on user\n".
- "lose - force user to stop following you\n".
- "fav - add user's last notice as a 'fave'\n".
- "fav # - add notice with the given id as a 'fave'\n".
- "repeat # - repeat a notice with a given id\n".
- "repeat - repeat the last notice from user\n".
- "reply # - reply to notice with a given id\n".
- "reply - reply to the last notice from user\n".
- "join - join group\n".
- "login - Get a link to login to the web interface\n".
- "drop - leave group\n".
- "stats - get your stats\n".
- "stop - same as 'off'\n".
- "quit - same as 'off'\n".
- "sub - same as 'follow'\n".
- "unsub - same as 'leave'\n".
- "last - same as 'get'\n".
- "on - not yet implemented.\n".
- "off - not yet implemented.\n".
- "nudge - remind a user to update.\n".
- "invite - not yet implemented.\n".
- "track - not yet implemented.\n".
- "untrack - not yet implemented.\n".
- "track off - not yet implemented.\n".
- "untrack all - not yet implemented.\n".
- "tracks - not yet implemented.\n".
- "tracking - not yet implemented.\n"));
+ // TRANS: Header line of help text for commands.
+ $out = array(_m('COMMANDHELP', "Commands:"));
+ $commands = array(// TRANS: Help message for IM/SMS command "on"
+ "on" => _m('COMMANDHELP', "turn on notifications"),
+ // TRANS: Help message for IM/SMS command "off"
+ "off" => _m('COMMANDHELP', "turn off notifications"),
+ // TRANS: Help message for IM/SMS command "help"
+ "help" => _m('COMMANDHELP', "show this help"),
+ // TRANS: Help message for IM/SMS command "follow "
+ "follow " => _m('COMMANDHELP', "subscribe to user"),
+ // TRANS: Help message for IM/SMS command "groups"
+ "groups" => _m('COMMANDHELP', "lists the groups you have joined"),
+ // TRANS: Help message for IM/SMS command "subscriptions"
+ "subscriptions" => _m('COMMANDHELP', "list the people you follow"),
+ // TRANS: Help message for IM/SMS command "subscribers"
+ "subscribers" => _m('COMMANDHELP', "list the people that follow you"),
+ // TRANS: Help message for IM/SMS command "leave "
+ "leave " => _m('COMMANDHELP', "unsubscribe from user"),
+ // TRANS: Help message for IM/SMS command "d "
+ "d " => _m('COMMANDHELP', "direct message to user"),
+ // TRANS: Help message for IM/SMS command "get "
+ "get " => _m('COMMANDHELP', "get last notice from user"),
+ // TRANS: Help message for IM/SMS command "whois "
+ "whois " => _m('COMMANDHELP', "get profile info on user"),
+ // TRANS: Help message for IM/SMS command "lose "
+ "lose " => _m('COMMANDHELP', "force user to stop following you"),
+ // TRANS: Help message for IM/SMS command "fav "
+ "fav " => _m('COMMANDHELP', "add user's last notice as a 'fave'"),
+ // TRANS: Help message for IM/SMS command "fav #"
+ "fav #" => _m('COMMANDHELP', "add notice with the given id as a 'fave'"),
+ // TRANS: Help message for IM/SMS command "repeat #"
+ "repeat #" => _m('COMMANDHELP', "repeat a notice with a given id"),
+ // TRANS: Help message for IM/SMS command "repeat "
+ "repeat " => _m('COMMANDHELP', "repeat the last notice from user"),
+ // TRANS: Help message for IM/SMS command "reply #"
+ "reply #" => _m('COMMANDHELP', "reply to notice with a given id"),
+ // TRANS: Help message for IM/SMS command "reply "
+ "reply " => _m('COMMANDHELP', "reply to the last notice from user"),
+ // TRANS: Help message for IM/SMS command "join "
+ "join " => _m('COMMANDHELP', "join group"),
+ // TRANS: Help message for IM/SMS command "login"
+ "login" => _m('COMMANDHELP', "Get a link to login to the web interface"),
+ // TRANS: Help message for IM/SMS command "drop "
+ "drop " => _m('COMMANDHELP', "leave group"),
+ // TRANS: Help message for IM/SMS command "stats"
+ "stats" => _m('COMMANDHELP', "get your stats"),
+ // TRANS: Help message for IM/SMS command "stop"
+ "stop" => _m('COMMANDHELP', "same as 'off'"),
+ // TRANS: Help message for IM/SMS command "quit"
+ "quit" => _m('COMMANDHELP', "same as 'off'"),
+ // TRANS: Help message for IM/SMS command "sub "
+ "sub " => _m('COMMANDHELP', "same as 'follow'"),
+ // TRANS: Help message for IM/SMS command "unsub "
+ "unsub " => _m('COMMANDHELP', "same as 'leave'"),
+ // TRANS: Help message for IM/SMS command "last "
+ "last " => _m('COMMANDHELP', "same as 'get'"),
+ // TRANS: Help message for IM/SMS command "on "
+ "on " => _m('COMMANDHELP', "not yet implemented."),
+ // TRANS: Help message for IM/SMS command "off "
+ "off " => _m('COMMANDHELP', "not yet implemented."),
+ // TRANS: Help message for IM/SMS command "nudge "
+ "nudge " => _m('COMMANDHELP', "remind a user to update."),
+ // TRANS: Help message for IM/SMS command "invite "
+ "invite " => _m('COMMANDHELP', "not yet implemented."),
+ // TRANS: Help message for IM/SMS command "track "
+ "track " => _m('COMMANDHELP', "not yet implemented."),
+ // TRANS: Help message for IM/SMS command "untrack "
+ "untrack " => _m('COMMANDHELP', "not yet implemented."),
+ // TRANS: Help message for IM/SMS command "track off"
+ "track off" => _m('COMMANDHELP', "not yet implemented."),
+ // TRANS: Help message for IM/SMS command "untrack all"
+ "untrack all" => _m('COMMANDHELP', "not yet implemented."),
+ // TRANS: Help message for IM/SMS command "tracks"
+ "tracks" => _m('COMMANDHELP', "not yet implemented."),
+ // TRANS: Help message for IM/SMS command "tracking"
+ "tracking" => _m('COMMANDHELP', "not yet implemented."));
+
+ // Give plugins a chance to add or override...
+ Event::handle('HelpCommandMessages', array($this, &$commands));
+ foreach ($commands as $command => $help) {
+ $out[] = "$command - $help";
+ }
+ $channel->output($this->user, implode("\n", $out));
}
}
diff --git a/lib/commandinterpreter.php b/lib/commandinterpreter.php
index fe426f1fcd..6b1b70055e 100644
--- a/lib/commandinterpreter.php
+++ b/lib/commandinterpreter.php
@@ -314,7 +314,7 @@ class CommandInterpreter
$result = false;
}
- Event::handle('EndInterpretCommand', array($cmd, $arg, $user, $result));
+ Event::handle('EndInterpretCommand', array($cmd, $arg, $user, &$result));
}
return $result;
diff --git a/lib/common.php b/lib/common.php
index 900ce9fa5b..ca02a3e7fb 100644
--- a/lib/common.php
+++ b/lib/common.php
@@ -1,7 +1,7 @@
isDisabled(); // don't need to check this now
- } catch (ReflectionException $e) {
- // Ok, it *really* doesn't exist!
- function dl($library) {
- return false;
- }
- }
-}
-
-# global configuration object
-
-require_once('PEAR.php');
-require_once('DB/DataObject.php');
-require_once('DB/DataObject/Cast.php'); # for dates
-
-require_once(INSTALLDIR.'/lib/language.php');
-
-// This gets included before the config file, so that admin code and plugins
-// can use it
-
-require_once(INSTALLDIR.'/lib/event.php');
-require_once(INSTALLDIR.'/lib/plugin.php');
-
-function addPlugin($name, $attrs = null)
-{
- return StatusNet::addPlugin($name, $attrs);
-}
-
-function _have_config()
-{
- return StatusNet::haveConfig();
-}
-
-/**
- * Wrapper for class autoloaders.
- * This used to be the special function name __autoload(), but that causes bugs with PHPUnit 3.5+
- */
-function autoload_sn($cls)
-{
- if (file_exists(INSTALLDIR.'/classes/' . $cls . '.php')) {
- require_once(INSTALLDIR.'/classes/' . $cls . '.php');
- } else if (file_exists(INSTALLDIR.'/lib/' . strtolower($cls) . '.php')) {
- require_once(INSTALLDIR.'/lib/' . strtolower($cls) . '.php');
- } else if (mb_substr($cls, -6) == 'Action' &&
- file_exists(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php')) {
- require_once(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
- } else if ($cls == 'OAuthRequest') {
- require_once('OAuth.php');
- } else {
- Event::handle('Autoload', array(&$cls));
- }
-}
-
-spl_autoload_register('autoload_sn');
-
-// XXX: how many of these could be auto-loaded on use?
-// XXX: note that these files should not use config options
-// at compile time since DB config options are not yet loaded.
-
-require_once 'Validate.php';
-require_once 'markdown.php';
-
-// XXX: other formats here
-
-/**
- * Avoid the NICKNAME_FMT constant; use the Nickname class instead.
- *
- * Nickname::DISPLAY_FMT is more suitable for inserting into regexes;
- * note that it includes the [] and repeating bits, so should be wrapped
- * directly in a capture paren usually.
- *
- * For validation, use Nickname::normalize(), Nickname::isValid() etc.
- *
- * @deprecated
- */
-define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER);
-
-require_once INSTALLDIR.'/lib/util.php';
-require_once INSTALLDIR.'/lib/action.php';
-require_once INSTALLDIR.'/lib/mail.php';
-require_once INSTALLDIR.'/lib/subs.php';
-
-require_once INSTALLDIR.'/lib/clientexception.php';
-require_once INSTALLDIR.'/lib/serverexception.php';
+// All the fun stuff to actually initialize StatusNet's framework code,
+// without loading up a site configuration.
+require_once INSTALLDIR . '/lib/framework.php';
try {
StatusNet::init(@$server, @$path, @$conffile);
diff --git a/lib/connectsettingsaction.php b/lib/connectsettingsaction.php
deleted file mode 100644
index 20f18ef0d9..0000000000
--- a/lib/connectsettingsaction.php
+++ /dev/null
@@ -1,137 +0,0 @@
-.
- *
- * @category Settings
- * @package StatusNet
- * @author Evan Prodromou
- * @copyright 2008-2009 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/settingsaction.php';
-
-/**
- * Base class for connection settings actions
- *
- * @category Settings
- * @package StatusNet
- * @author Evan Prodromou
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- *
- * @see Widget
- */
-class ConnectSettingsAction extends SettingsAction
-{
- /**
- * Show the local navigation menu
- *
- * This is the same for all settings, so we show it here.
- *
- * @return void
- */
- function showLocalNav()
- {
- $menu = new ConnectSettingsNav($this);
- $menu->show();
- }
-}
-
-/**
- * A widget for showing the connect group local nav menu
- *
- * @category Widget
- * @package StatusNet
- * @author Evan Prodromou
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- *
- * @see HTMLOutputter
- */
-class ConnectSettingsNav extends Widget
-{
- var $action = null;
-
- /**
- * Construction
- *
- * @param Action $action current action, used for output
- */
- function __construct($action=null)
- {
- parent::__construct($action);
- $this->action = $action;
- }
-
- /**
- * Show the menu
- *
- * @return void
- */
- function show()
- {
- $action_name = $this->action->trimmed('action');
- $this->action->elementStart('ul', array('class' => 'nav'));
-
- if (Event::handle('StartConnectSettingsNav', array($this->action))) {
-
- # action => array('prompt', 'title')
- $menu = array();
- if (common_config('xmpp', 'enabled')) {
- $menu['imsettings'] =
- // TRANS: Menu item for Instant Messaging settings.
- array(_m('MENU','IM'),
- // TRANS: Tooltip for Instant Messaging menu item.
- _('Updates by instant messenger (IM)'));
- }
- if (common_config('sms', 'enabled')) {
- $menu['smssettings'] =
- // TRANS: Menu item for Short Message Service settings.
- array(_m('MENU','SMS'),
- // TRANS: Tooltip for Short Message Service menu item.
- _('Updates by SMS'));
- }
-
- $menu['oauthconnectionssettings'] = array(
- // TRANS: Menu item for OuAth connection settings.
- _m('MENU','Connections'),
- // TRANS: Tooltip for connected applications (Connections through OAuth) menu item.
- _('Authorized connected applications')
- );
-
- foreach ($menu as $menuaction => $menudesc) {
- $this->action->menuItem(common_local_url($menuaction),
- $menudesc[0],
- $menudesc[1],
- $action_name === $menuaction);
- }
-
- Event::handle('EndConnectSettingsNav', array($this->action));
- }
-
- $this->action->elementEnd('ul');
- }
-}
diff --git a/lib/default.php b/lib/default.php
index 124c90c998..e6caf0301a 100644
--- a/lib/default.php
+++ b/lib/default.php
@@ -33,7 +33,7 @@ $default =
'nickname' => 'statusnet',
'wildcard' => null,
'server' => $_server,
- 'theme' => 'default',
+ 'theme' => 'neo',
'path' => $_path,
'logfile' => null,
'logo' => null,
@@ -63,6 +63,7 @@ $default =
'use_x_sendfile' => false,
'notice' => null, // site wide notice text
'build' => 1, // build number, for code-dependent cache
+ 'minify' => true, // true to use the minified versions of JS files; false to use orig files. Can aid during development
),
'db' =>
array('database' => 'YOU HAVE TO SET THIS IN config.php',
@@ -78,7 +79,8 @@ $default =
'schemacheck' => 'runtime', // 'runtime' or 'script'
'annotate_queries' => false, // true to add caller comments to queries, eg /* POST Notice::saveNew */
'log_queries' => false, // true to log all DB queries
- 'log_slow_queries' => 0), // if set, log queries taking over N seconds
+ 'log_slow_queries' => 0, // if set, log queries taking over N seconds
+ 'mysql_foreign_keys' => false), // if set, enables experimental foreign key support on MySQL
'syslog' =>
array('appname' => 'statusnet', # for syslog
'priority' => 'debug', # XXX: currently ignored
@@ -297,21 +299,13 @@ $default =
'logincommand' =>
array('disabled' => true),
'plugins' =>
- array('default' => array('LilUrl' => array('shortenerName'=>'ur1.ca',
- 'freeService' => true,
- 'serviceUrl'=>'http://ur1.ca/'),
- 'PtitUrl' => array('shortenerName' => 'ptiturl.com',
- 'serviceUrl' => 'http://ptiturl.com/?creer=oui&action=Reduire&url=%1$s'),
- 'SimpleUrl' => array(array('shortenerName' => 'is.gd', 'serviceUrl' => 'http://is.gd/api.php?longurl=%1$s'),
- array('shortenerName' => 'snipr.com', 'serviceUrl' => 'http://snipr.com/site/snip?r=simple&link=%1$s'),
- array('shortenerName' => 'metamark.net', 'serviceUrl' => 'http://metamark.net/api/rest/simple?long_url=%1$s'),
- array('shortenerName' => 'tinyurl.com', 'serviceUrl' => 'http://tinyurl.com/api-create.php?url=%1$s')),
- 'TightUrl' => array('shortenerName' => '2tu.us', 'freeService' => true,'serviceUrl'=>'http://2tu.us/?save=y&url=%1$s'),
- 'Geonames' => null,
+ array('default' => array('Geonames' => null,
'Mapstraction' => null,
'OStatus' => null,
'WikiHashtags' => null,
'RSSCloud' => null,
+ 'ClientSideShorten' => null,
+ 'StrictTransportSecurity' => null,
'OpenID' => null),
'locale_path' => false, // Set to a path to use *instead of* each plugin's own locale subdirectories
'server' => null,
@@ -319,8 +313,9 @@ $default =
'path' => null,
'sslpath' => null,
),
+ 'pluginlist' => array(),
'admin' =>
- array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions', 'sitenotice', 'license')),
+ array('panels' => array('design', 'site', 'user', 'paths', 'access', 'sessions', 'sitenotice', 'license', 'plugins')),
'singleuser' =>
array('enabled' => false,
'nickname' => null),
@@ -335,6 +330,10 @@ $default =
'members' => true,
'peopletag' => true,
'external' => 'sometimes'), // Options: 'sometimes', 'never', default = 'sometimes'
+ 'url' =>
+ array('shortener' => 'ur1.ca',
+ 'maxlength' => 25,
+ 'maxnoticelength' => -1),
'http' => // HTTP client settings when contacting other sites
array('ssl_cafile' => false, // To enable SSL cert validation, point to a CA bundle (eg '/usr/lib/ssl/certs/ca-certificates.crt')
'curl' => false, // Use CURL backend for HTTP fetches if available. (If not, PHP's socket streams will be used.)
diff --git a/lib/defaultlocalnav.php b/lib/defaultlocalnav.php
new file mode 100644
index 0000000000..7af3c9673f
--- /dev/null
+++ b/lib/defaultlocalnav.php
@@ -0,0 +1,66 @@
+.
+ *
+ * @category Menu
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Default menu
+ *
+ * @category Menu
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class DefaultLocalNav extends Menu
+{
+ function show()
+ {
+ $this->action->elementStart('ul', array('id' => 'nav_local_default'));
+
+ $user = common_current_user();
+
+ if (!empty($user)) {
+ $pn = new PersonalGroupNav($this->action);
+ $this->submenu(_m('Home'), $pn);
+ }
+
+ $bn = new PublicGroupNav($this->action);
+ $this->submenu(_('Public'), $bn);
+
+ $this->action->elementEnd('ul');
+ }
+}
diff --git a/lib/defaultprofileblock.php b/lib/defaultprofileblock.php
new file mode 100644
index 0000000000..b8af14ac21
--- /dev/null
+++ b/lib/defaultprofileblock.php
@@ -0,0 +1,89 @@
+.
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Default profile block
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class DefaultProfileBlock extends AccountProfileBlock
+{
+ function __construct($out)
+ {
+ $user = common_current_user();
+ if (empty($user)) {
+ throw new Exception("DefaultProfileBlock with no user.");
+ }
+ parent::__construct($out, $user->getProfile());
+ }
+
+ function avatarSize()
+ {
+ return AVATAR_STREAM_SIZE;
+ }
+
+ function avatar()
+ {
+ $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE);
+ if (empty($avatar)) {
+ $avatar = $this->profile->getAvatar(73);
+ }
+ return (!empty($avatar)) ?
+ $avatar->displayUrl() :
+ Avatar::defaultImage(AVATAR_STREAM_SIZE);
+ }
+
+ function location()
+ {
+ return null;
+ }
+
+ function homepage()
+ {
+ return null;
+ }
+
+ function description()
+ {
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/lib/designform.php b/lib/designform.php
new file mode 100644
index 0000000000..7702b873fe
--- /dev/null
+++ b/lib/designform.php
@@ -0,0 +1,324 @@
+.
+ *
+ * @category Form
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @author Sarven Capadisli
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Form for choosing a design
+ *
+ * Used for choosing a site design, user design, or group design.
+ *
+ * @category Form
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @author Sarven Capadisli
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ */
+
+class DesignForm extends Form
+{
+ /**
+ * Return-to args
+ */
+
+ var $design = null;
+ var $actionurl = null;
+
+ /**
+ * Constructor
+ *
+ * @param HTMLOutputter $out output channel
+ * @param Design $design initial design
+ * @param Design $actionurl url of action (for form posting)
+ */
+
+ function __construct($out, $design, $actionurl)
+ {
+ parent::__construct($out);
+
+ $this->design = $design;
+ $this->actionurl = $actionurl;
+ }
+
+ /**
+ * ID of the form
+ *
+ * @return int ID of the form
+ */
+
+ function id()
+ {
+ return 'design';
+ }
+
+ /**
+ * class of the form
+ *
+ * @return string class of the form
+ */
+
+ function formClass()
+ {
+ return 'form_design';
+ }
+
+ /**
+ * Action of the form
+ *
+ * @return string URL of the action
+ */
+
+ function action()
+ {
+ return $this->actionurl;
+ }
+
+ /**
+ * Legend of the Form
+ *
+ * @return void
+ */
+ function formLegend()
+ {
+ $this->out->element('legend', null, _('Change design'));
+ }
+
+ /**
+ * Data elements of the form
+ *
+ * @return void
+ */
+
+ function formData()
+ {
+ $this->backgroundData();
+
+ $this->out->elementEnd('fieldset');
+
+ $this->out->elementStart('fieldset', array('id' => 'settings_design_color'));
+ // TRANS: Fieldset legend on profile design page to change profile page colours.
+ $this->out->element('legend', null, _('Change colours'));
+ $this->colourData();
+ $this->out->elementEnd('fieldset');
+
+ $this->out->elementStart('fieldset');
+
+ // TRANS: Button text on profile design page to immediately reset all colour settings to default.
+ $this->out->submit('defaults', _('Use defaults'), 'submit form_action-default',
+ // TRANS: Title for button on profile design page to reset all colour settings to default.
+ 'defaults', _('Restore default designs'));
+
+ $this->out->element('input', array('id' => 'settings_design_reset',
+ 'type' => 'reset',
+ // TRANS: Button text on profile design page to reset all colour settings to default without saving.
+ 'value' => _m('BUTTON', 'Reset'),
+ 'class' => 'submit form_action-primary',
+ // TRANS: Title for button on profile design page to reset all colour settings to default without saving.
+ 'title' => _('Reset back to default')));
+ }
+
+ function backgroundData()
+ {
+ $this->out->elementStart('ul', 'form_data');
+ $this->out->elementStart('li');
+ $this->out->element('label', array('for' => 'design_background-image_file'),
+ // TRANS: Label in form on profile design page.
+ // TRANS: Field contains file name on user's computer that could be that user's custom profile background image.
+ _('Upload file'));
+ $this->out->element('input', array('name' => 'design_background-image_file',
+ 'type' => 'file',
+ 'id' => 'design_background-image_file'));
+ // TRANS: Instructions for form on profile design page.
+ $this->out->element('p', 'form_guide', _('You can upload your personal ' .
+ 'background image. The maximum file size is 2Mb.'));
+ $this->out->element('input', array('name' => 'MAX_FILE_SIZE',
+ 'type' => 'hidden',
+ 'id' => 'MAX_FILE_SIZE',
+ 'value' => ImageFile::maxFileSizeInt()));
+ $this->out->elementEnd('li');
+
+ if (!empty($this->design->backgroundimage)) {
+
+ $this->out->elementStart('li', array('id' =>
+ 'design_background-image_onoff'));
+
+ $this->out->element('img', array('src' =>
+ Design::url($this->design->backgroundimage)));
+
+ $attrs = array('name' => 'design_background-image_onoff',
+ 'type' => 'radio',
+ 'id' => 'design_background-image_on',
+ 'class' => 'radio',
+ 'value' => 'on');
+
+ if ($this->design->disposition & BACKGROUND_ON) {
+ $attrs['checked'] = 'checked';
+ }
+
+ $this->out->element('input', $attrs);
+
+ $this->out->element('label', array('for' => 'design_background-image_on',
+ 'class' => 'radio'),
+ // TRANS: Radio button on profile design page that will enable use of the uploaded profile image.
+ _m('RADIO', 'On'));
+
+ $attrs = array('name' => 'design_background-image_onoff',
+ 'type' => 'radio',
+ 'id' => 'design_background-image_off',
+ 'class' => 'radio',
+ 'value' => 'off');
+
+ if ($this->design->disposition & BACKGROUND_OFF) {
+ $attrs['checked'] = 'checked';
+ }
+
+ $this->out->element('input', $attrs);
+
+ $this->out->element('label', array('for' => 'design_background-image_off',
+ 'class' => 'radio'),
+ // TRANS: Radio button on profile design page that will disable use of the uploaded profile image.
+ _m('RADIO', 'Off'));
+ // TRANS: Form guide for a set of radio buttons on the profile design page that will enable or disable
+ // TRANS: use of the uploaded profile image.
+ $this->out->element('p', 'form_guide', _('Turn background image on or off.'));
+ $this->out->elementEnd('li');
+
+ $this->out->elementStart('li');
+ $this->out->checkbox('design_background-image_repeat',
+ // TRANS: Checkbox label on profile design page that will cause the profile image to be tiled.
+ _('Tile background image'),
+ ($this->design->disposition & BACKGROUND_TILE) ? true : false);
+ $this->out->elementEnd('li');
+ }
+
+ $this->out->elementEnd('ul');
+ }
+
+ function colourData()
+ {
+ $this->out->elementStart('ul', 'form_data');
+
+ try {
+
+ $bgcolor = new WebColor($this->design->backgroundcolor);
+
+ $this->out->elementStart('li');
+ // TRANS: Label on profile design page for setting a profile page background colour.
+ $this->out->element('label', array('for' => 'swatch-1'), _('Background'));
+ $this->out->element('input', array('name' => 'design_background',
+ 'type' => 'text',
+ 'id' => 'swatch-1',
+ 'class' => 'swatch',
+ 'maxlength' => '7',
+ 'size' => '7',
+ 'value' => ''));
+ $this->out->elementEnd('li');
+
+ $ccolor = new WebColor($this->design->contentcolor);
+
+ $this->out->elementStart('li');
+ // TRANS: Label on profile design page for setting a profile page content colour.
+ $this->out->element('label', array('for' => 'swatch-2'), _('Content'));
+ $this->out->element('input', array('name' => 'design_content',
+ 'type' => 'text',
+ 'id' => 'swatch-2',
+ 'class' => 'swatch',
+ 'maxlength' => '7',
+ 'size' => '7',
+ 'value' => ''));
+ $this->out->elementEnd('li');
+
+ $sbcolor = new WebColor($this->design->sidebarcolor);
+
+ $this->out->elementStart('li');
+ // TRANS: Label on profile design page for setting a profile page sidebar colour.
+ $this->out->element('label', array('for' => 'swatch-3'), _('Sidebar'));
+ $this->out->element('input', array('name' => 'design_sidebar',
+ 'type' => 'text',
+ 'id' => 'swatch-3',
+ 'class' => 'swatch',
+ 'maxlength' => '7',
+ 'size' => '7',
+ 'value' => ''));
+ $this->out->elementEnd('li');
+
+ $tcolor = new WebColor($this->design->textcolor);
+
+ $this->out->elementStart('li');
+ // TRANS: Label on profile design page for setting a profile page text colour.
+ $this->out->element('label', array('for' => 'swatch-4'), _('Text'));
+ $this->out->element('input', array('name' => 'design_text',
+ 'type' => 'text',
+ 'id' => 'swatch-4',
+ 'class' => 'swatch',
+ 'maxlength' => '7',
+ 'size' => '7',
+ 'value' => ''));
+ $this->out->elementEnd('li');
+
+ $lcolor = new WebColor($this->design->linkcolor);
+
+ $this->out->elementStart('li');
+ // TRANS: Label on profile design page for setting a profile page links colour.
+ $this->out->element('label', array('for' => 'swatch-5'), _('Links'));
+ $this->out->element('input', array('name' => 'design_links',
+ 'type' => 'text',
+ 'id' => 'swatch-5',
+ 'class' => 'swatch',
+ 'maxlength' => '7',
+ 'size' => '7',
+ 'value' => ''));
+ $this->out->elementEnd('li');
+
+ } catch (WebColorException $e) {
+ common_log(LOG_ERR, 'Bad color values in design ID: ' .$this->design->id);
+ }
+
+ $this->out->elementEnd('ul');
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ // TRANS: Button text on profile design page to save settings.
+ $this->out->submit('save', _m('BUTTON','Save'), 'submit form_action-secondary',
+ // TRANS: Title for button on profile design page to save settings.
+ 'save', _('Save design'));
+ }
+}
diff --git a/lib/designsettings.php b/lib/designsettings.php
index d7da0b77d8..eb3a5908e6 100644
--- a/lib/designsettings.php
+++ b/lib/designsettings.php
@@ -32,9 +32,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
-require_once INSTALLDIR . '/lib/accountsettingsaction.php';
-require_once INSTALLDIR . '/lib/webcolor.php';
-
/**
* Base class for setting a user or group design
*
@@ -48,7 +45,8 @@ require_once INSTALLDIR . '/lib/webcolor.php';
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-class DesignSettingsAction extends AccountSettingsAction
+
+class DesignSettingsAction extends SettingsAction
{
var $submitaction = null;
@@ -84,195 +82,9 @@ class DesignSettingsAction extends AccountSettingsAction
*/
function showDesignForm($design)
{
- $this->elementStart('form', array('method' => 'post',
- 'enctype' => 'multipart/form-data',
- 'id' => 'form_settings_design',
- 'class' => 'form_settings',
- 'action' => $this->submitaction));
- $this->elementStart('fieldset');
- $this->hidden('token', common_session_token());
+ $form = new DesignForm($this, $design, $this->selfUrl());
+ $form->show();
- $this->elementStart('fieldset', array('id' =>
- 'settings_design_background-image'));
- // TRANS: Fieldset legend on profile design page.
- $this->element('legend', null, _('Change background image'));
- $this->elementStart('ul', 'form_data');
- $this->elementStart('li');
- $this->element('input', array('name' => 'MAX_FILE_SIZE',
- 'type' => 'hidden',
- 'id' => 'MAX_FILE_SIZE',
- 'value' => ImageFile::maxFileSizeInt()));
- $this->element('label', array('for' => 'design_background-image_file'),
- // TRANS: Label in form on profile design page.
- // TRANS: Field contains file name on user's computer that could be that user's custom profile background image.
- _('Upload file'));
- $this->element('input', array('name' => 'design_background-image_file',
- 'type' => 'file',
- 'id' => 'design_background-image_file'));
- // TRANS: Instructions for form on profile design page.
- $this->element('p', 'form_guide', _('You can upload your personal ' .
- 'background image. The maximum file size is 2MB.'));
- $this->elementEnd('li');
-
- if (!empty($design->backgroundimage)) {
- $this->elementStart('li', array('id' =>
- 'design_background-image_onoff'));
-
- $this->element('img', array('src' =>
- Design::url($design->backgroundimage)));
-
- $attrs = array('name' => 'design_background-image_onoff',
- 'type' => 'radio',
- 'id' => 'design_background-image_on',
- 'class' => 'radio',
- 'value' => 'on');
-
- if ($design->disposition & BACKGROUND_ON) {
- $attrs['checked'] = 'checked';
- }
-
- $this->element('input', $attrs);
-
- $this->element('label', array('for' => 'design_background-image_on',
- 'class' => 'radio'),
- // TRANS: Radio button on profile design page that will enable use of the uploaded profile image.
- _m('RADIO','On'));
-
- $attrs = array('name' => 'design_background-image_onoff',
- 'type' => 'radio',
- 'id' => 'design_background-image_off',
- 'class' => 'radio',
- 'value' => 'off');
-
- if ($design->disposition & BACKGROUND_OFF) {
- $attrs['checked'] = 'checked';
- }
-
- $this->element('input', $attrs);
-
- $this->element('label', array('for' => 'design_background-image_off',
- 'class' => 'radio'),
- // TRANS: Radio button on profile design page that will disable use of the uploaded profile image.
- _m('RADIO','Off'));
- // TRANS: Form guide for a set of radio buttons on the profile design page that will enable or disable
- // TRANS: use of the uploaded profile image.
- $this->element('p', 'form_guide', _('Turn background image on or off.'));
- $this->elementEnd('li');
-
- $this->elementStart('li');
- $this->checkbox('design_background-image_repeat',
- // TRANS: Checkbox label on profile design page that will cause the profile image to be tiled.
- _('Tile background image'),
- ($design->disposition & BACKGROUND_TILE) ? true : false);
- $this->elementEnd('li');
- }
-
- $this->elementEnd('ul');
- $this->elementEnd('fieldset');
-
- $this->elementStart('fieldset', array('id' => 'settings_design_color'));
- // TRANS: Fieldset legend on profile design page to change profile page colours.
- $this->element('legend', null, _('Change colours'));
- $this->elementStart('ul', 'form_data');
-
- try {
- $bgcolor = new WebColor($design->backgroundcolor);
-
- $this->elementStart('li');
- // TRANS: Label on profile design page for setting a profile page background colour.
- $this->element('label', array('for' => 'swatch-1'), _('Background'));
- $this->element('input', array('name' => 'design_background',
- 'type' => 'text',
- 'id' => 'swatch-1',
- 'class' => 'swatch',
- 'maxlength' => '7',
- 'size' => '7',
- 'value' => ''));
- $this->elementEnd('li');
-
- $ccolor = new WebColor($design->contentcolor);
-
- $this->elementStart('li');
- // TRANS: Label on profile design page for setting a profile page content colour.
- $this->element('label', array('for' => 'swatch-2'), _('Content'));
- $this->element('input', array('name' => 'design_content',
- 'type' => 'text',
- 'id' => 'swatch-2',
- 'class' => 'swatch',
- 'maxlength' => '7',
- 'size' => '7',
- 'value' => ''));
- $this->elementEnd('li');
-
- $sbcolor = new WebColor($design->sidebarcolor);
-
- $this->elementStart('li');
- // TRANS: Label on profile design page for setting a profile page sidebar colour.
- $this->element('label', array('for' => 'swatch-3'), _('Sidebar'));
- $this->element('input', array('name' => 'design_sidebar',
- 'type' => 'text',
- 'id' => 'swatch-3',
- 'class' => 'swatch',
- 'maxlength' => '7',
- 'size' => '7',
- 'value' => ''));
- $this->elementEnd('li');
-
- $tcolor = new WebColor($design->textcolor);
-
- $this->elementStart('li');
- // TRANS: Label on profile design page for setting a profile page text colour.
- $this->element('label', array('for' => 'swatch-4'), _('Text'));
- $this->element('input', array('name' => 'design_text',
- 'type' => 'text',
- 'id' => 'swatch-4',
- 'class' => 'swatch',
- 'maxlength' => '7',
- 'size' => '7',
- 'value' => ''));
- $this->elementEnd('li');
-
- $lcolor = new WebColor($design->linkcolor);
-
- $this->elementStart('li');
- // TRANS: Label on profile design page for setting a profile page links colour.
- $this->element('label', array('for' => 'swatch-5'), _('Links'));
- $this->element('input', array('name' => 'design_links',
- 'type' => 'text',
- 'id' => 'swatch-5',
- 'class' => 'swatch',
- 'maxlength' => '7',
- 'size' => '7',
- 'value' => ''));
- $this->elementEnd('li');
-
- } catch (WebColorException $e) {
- common_log(LOG_ERR, 'Bad color values in design ID: ' .$design->id);
- }
-
- $this->elementEnd('ul');
- $this->elementEnd('fieldset');
-
- // TRANS: Button text on profile design page to immediately reset all colour settings to default.
- $this->submit('defaults', _('Use defaults'), 'submit form_action-default',
- // TRANS: Title for button on profile design page to reset all colour settings to default.
- 'defaults', _('Restore default designs'));
-
- $this->element('input', array('id' => 'settings_design_reset',
- 'type' => 'reset',
- // TRANS: Button text on profile design page to reset all colour settings to default without saving.
- 'value' => _m('BUTTON','Reset'),
- 'class' => 'submit form_action-primary',
- // TRANS: Title for button on profile design page to reset all colour settings to default without saving.
- 'title' => _('Reset back to default')));
-
- // TRANS: Button text on profile design page to save settings.
- $this->submit('save', _m('BUTTON','Save'), 'submit form_action-secondary',
- // TRANS: Title for button on profile design page to save settings.
- 'save', _('Save design'));
-
- $this->elementEnd('fieldset');
- $this->elementEnd('form');
}
/**
@@ -362,22 +174,21 @@ class DesignSettingsAction extends AccountSettingsAction
// associated with the Design rather than the User was worth
// it. -- Zach
- if ($_FILES['design_background-image_file']['error'] ==
- UPLOAD_ERR_OK) {
+ if (array_key_exists('design_background-image_file', $_FILES) &&
+ $_FILES['design_background-image_file']['error'] == UPLOAD_ERR_OK) {
$filepath = null;
try {
- $imagefile =
- ImageFile::fromUpload('design_background-image_file');
+ $imagefile = ImageFile::fromUpload('design_background-image_file');
} catch (Exception $e) {
$this->showForm($e->getMessage());
return;
}
$filename = Design::filename($design->id,
- image_type_to_extension($imagefile->type),
- common_timestamp());
+ image_type_to_extension($imagefile->type),
+ common_timestamp());
$filepath = Design::path($filename);
diff --git a/lib/disfavorform.php b/lib/disfavorform.php
index 3a1c7d17fb..9d0e39784e 100644
--- a/lib/disfavorform.php
+++ b/lib/disfavorform.php
@@ -147,6 +147,6 @@ class DisfavorForm extends Form
*/
function formClass()
{
- return 'form_disfavor';
+ return 'form_disfavor ajax';
}
}
diff --git a/lib/error.php b/lib/error.php
index 762425dc44..4024a9affc 100644
--- a/lib/error.php
+++ b/lib/error.php
@@ -68,7 +68,11 @@ class ErrorAction extends InfoAction
function showPage()
{
- if ($this->minimal) {
+ if (StatusNet::isAjax()) {
+ $this->extraHeaders();
+ $this->ajaxErrorMsg();
+ exit();
+ } if ($this->minimal) {
// Even more minimal -- we're in a machine API
// and don't want to flood the output.
$this->extraHeaders();
@@ -91,6 +95,30 @@ class ErrorAction extends InfoAction
$this->element('div', array('class' => 'error'), $this->message);
}
+ function showNoticeForm()
+ {
+ }
+ /**
+ * Show an Ajax-y error message
+ *
+ * Goes back to the browser, where it's shown in a popup.
+ *
+ * @param string $msg Message to show
+ *
+ * @return void
+ */
+ function ajaxErrorMsg()
+ {
+ $this->startHTML('text/xml;charset=utf-8', true);
+ $this->elementStart('head');
+ // TRANS: Page title after an AJAX error occurs on the send notice page.
+ $this->element('title', null, _('Ajax Error'));
+ $this->elementEnd('head');
+ $this->elementStart('body');
+ $this->element('p', array('id' => 'error'), $this->message);
+ $this->elementEnd('body');
+ $this->elementEnd('html');
+ }
}
diff --git a/lib/favorform.php b/lib/favorform.php
index 956cc896a2..c07ba6df59 100644
--- a/lib/favorform.php
+++ b/lib/favorform.php
@@ -146,6 +146,6 @@ class FavorForm extends Form
*/
function formClass()
{
- return 'form_favor';
+ return 'form_favor ajax';
}
}
diff --git a/lib/form.php b/lib/form.php
index f6501dc6da..74737f6df5 100644
--- a/lib/form.php
+++ b/lib/form.php
@@ -172,7 +172,13 @@ class Form extends Widget
}
/**
- * Class of the form.
+ * Class of the form. May include space-separated list of multiple classes.
+ *
+ * If 'ajax' is included, the form will automatically be submitted with
+ * an 'ajax=1' parameter added, and the resulting form or error message
+ * will replace the form after submission.
+ *
+ * It's up to you to make sure that the target action supports this!
*
* @return string the form's class
*/
diff --git a/lib/framework.php b/lib/framework.php
new file mode 100644
index 0000000000..da96c8e1d4
--- /dev/null
+++ b/lib/framework.php
@@ -0,0 +1,158 @@
+.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+define('STATUSNET_BASE_VERSION', '1.0.0');
+define('STATUSNET_LIFECYCLE', 'dev'); // 'dev', 'alpha[0-9]+', 'beta[0-9]+', 'rc[0-9]+', 'release'
+define('STATUSNET_VERSION', STATUSNET_BASE_VERSION . STATUSNET_LIFECYCLE);
+
+define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility
+
+define('STATUSNET_CODENAME', 'The Sounds of Science');
+
+define('AVATAR_PROFILE_SIZE', 96);
+define('AVATAR_STREAM_SIZE', 48);
+define('AVATAR_MINI_SIZE', 24);
+
+define('NOTICES_PER_PAGE', 20);
+define('PROFILES_PER_PAGE', 20);
+define('MESSAGES_PER_PAGE', 20);
+
+define('FOREIGN_NOTICE_SEND', 1);
+define('FOREIGN_NOTICE_RECV', 2);
+define('FOREIGN_NOTICE_SEND_REPLY', 4);
+
+define('FOREIGN_FRIEND_SEND', 1);
+define('FOREIGN_FRIEND_RECV', 2);
+
+define('NOTICE_INBOX_SOURCE_SUB', 1);
+define('NOTICE_INBOX_SOURCE_GROUP', 2);
+define('NOTICE_INBOX_SOURCE_REPLY', 3);
+define('NOTICE_INBOX_SOURCE_FORWARD', 4);
+define('NOTICE_INBOX_SOURCE_GATEWAY', -1);
+
+# append our extlib dir as the last-resort place to find libs
+
+set_include_path(get_include_path() . PATH_SEPARATOR . INSTALLDIR . '/extlib/');
+
+// To protect against upstream libraries which haven't updated
+// for PHP 5.3 where dl() function may not be present...
+if (!function_exists('dl')) {
+ // function_exists() returns false for things in disable_functions,
+ // but they still exist and we'll die if we try to redefine them.
+ //
+ // Fortunately trying to call the disabled one will only trigger
+ // a warning, not a fatal, so it's safe to leave it for our case.
+ // Callers will be suppressing warnings anyway.
+ $disabled = array_filter(array_map('trim', explode(',', ini_get('disable_functions'))));
+ if (!in_array('dl', $disabled)) {
+ function dl($library) {
+ return false;
+ }
+ }
+}
+
+# global configuration object
+
+require_once('PEAR.php');
+require_once('PEAR/Exception.php');
+require_once('DB/DataObject.php');
+require_once('DB/DataObject/Cast.php'); # for dates
+
+require_once(INSTALLDIR.'/lib/language.php');
+
+// This gets included before the config file, so that admin code and plugins
+// can use it
+
+require_once(INSTALLDIR.'/lib/event.php');
+require_once(INSTALLDIR.'/lib/plugin.php');
+
+function addPlugin($name, $attrs = null)
+{
+ return StatusNet::addPlugin($name, $attrs);
+}
+
+function _have_config()
+{
+ return StatusNet::haveConfig();
+}
+
+function __autoload($cls)
+{
+ if (file_exists(INSTALLDIR.'/classes/' . $cls . '.php')) {
+ require_once(INSTALLDIR.'/classes/' . $cls . '.php');
+ } else if (file_exists(INSTALLDIR.'/lib/' . strtolower($cls) . '.php')) {
+ require_once(INSTALLDIR.'/lib/' . strtolower($cls) . '.php');
+ } else if (mb_substr($cls, -6) == 'Action' &&
+ file_exists(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php')) {
+ require_once(INSTALLDIR.'/actions/' . strtolower(mb_substr($cls, 0, -6)) . '.php');
+ } else if ($cls == 'OAuthRequest') {
+ require_once('OAuth.php');
+ } else {
+ Event::handle('Autoload', array(&$cls));
+ }
+}
+
+// XXX: how many of these could be auto-loaded on use?
+// XXX: note that these files should not use config options
+// at compile time since DB config options are not yet loaded.
+
+require_once 'Validate.php';
+require_once 'markdown.php';
+
+// XXX: other formats here
+
+/**
+ * Avoid the NICKNAME_FMT constant; use the Nickname class instead.
+ *
+ * Nickname::DISPLAY_FMT is more suitable for inserting into regexes;
+ * note that it includes the [] and repeating bits, so should be wrapped
+ * directly in a capture paren usually.
+ *
+ * For validation, use Nickname::normalize(), Nickname::isValid() etc.
+ *
+ * @deprecated
+ */
+define('NICKNAME_FMT', VALIDATE_NUM.VALIDATE_ALPHA_LOWER);
+
+require_once INSTALLDIR.'/lib/util.php';
+require_once INSTALLDIR.'/lib/action.php';
+require_once INSTALLDIR.'/lib/mail.php';
+require_once INSTALLDIR.'/lib/subs.php';
+
+require_once INSTALLDIR.'/lib/clientexception.php';
+require_once INSTALLDIR.'/lib/serverexception.php';
+
+
+//set PEAR error handling to use regular PHP exceptions
+function PEAR_ErrorToPEAR_Exception($err)
+{
+ //DB_DataObject throws error when an empty set would be returned
+ //That behavior is weird, and not how the rest of StatusNet works.
+ //So just ignore those errors.
+ if ($err->getCode() == DB_DATAOBJECT_ERROR_NODATA) {
+ return;
+ }
+ if ($err->getCode()) {
+ throw new PEAR_Exception($err->getMessage(), $err->getCode());
+ }
+ throw new PEAR_Exception($err->getMessage());
+}
+PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, 'PEAR_ErrorToPEAR_Exception');
diff --git a/lib/galleryaction.php b/lib/galleryaction.php
index 31e36803a7..3db46dd09c 100644
--- a/lib/galleryaction.php
+++ b/lib/galleryaction.php
@@ -94,7 +94,7 @@ class GalleryAction extends OwnerDesignAction
$this->showPage();
}
- function showLocalNav()
+ function showObjectNav()
{
$nav = new SubGroupNav($this, $this->user);
$nav->show();
@@ -117,9 +117,6 @@ class GalleryAction extends OwnerDesignAction
$content[$t] = $t;
}
if ($tags) {
- $this->elementStart('dl', array('id'=>'filter_tags'));
- $this->element('dt', null, _('Filter tags'));
- $this->elementStart('dd');
$this->elementStart('ul');
$this->elementStart('li', array('id' => 'filter_tags_all',
'class' => 'child_1'));
@@ -133,7 +130,7 @@ class GalleryAction extends OwnerDesignAction
$this->elementStart('li', array('id'=>'filter_tags_item'));
$this->elementStart('form', array('name' => 'bytag',
'id' => 'form_filter_bytag',
- 'action' => common_path('?action=' . $this->trimmed('action')),
+ 'action' => common_path('?action=' . $this->trimmed('action')),
'method' => 'post'));
$this->elementStart('fieldset');
$this->element('legend', null, _('Select tag to filter'));
@@ -145,8 +142,6 @@ class GalleryAction extends OwnerDesignAction
$this->elementEnd('form');
$this->elementEnd('li');
$this->elementEnd('ul');
- $this->elementEnd('dd');
- $this->elementEnd('dl');
}
}
@@ -173,4 +168,10 @@ class GalleryAction extends OwnerDesignAction
{
return array();
}
+
+ function showProfileBlock()
+ {
+ $block = new AccountProfileBlock($this, $this->profile);
+ $block->show();
+ }
}
diff --git a/lib/groupdesignaction.php b/lib/groupdesignaction.php
index 3eb3964e87..44f35f6299 100644
--- a/lib/groupdesignaction.php
+++ b/lib/groupdesignaction.php
@@ -68,4 +68,10 @@ class GroupDesignAction extends Action {
}
return parent::getDesign();
}
+
+ function showProfileBlock()
+ {
+ $block = new GroupProfileBlock($this, $this->group);
+ $block->show();
+ }
}
diff --git a/lib/groupnav.php b/lib/groupnav.php
index ee988d0a98..a2dd6eac00 100644
--- a/lib/groupnav.php
+++ b/lib/groupnav.php
@@ -49,9 +49,8 @@ require_once INSTALLDIR.'/lib/widget.php';
* @see HTMLOutputter
*/
-class GroupNav extends Widget
+class GroupNav extends Menu
{
- var $action = null;
var $group = null;
/**
@@ -63,7 +62,6 @@ class GroupNav extends Widget
function __construct($action=null, $group=null)
{
parent::__construct($action);
- $this->action = $action;
$this->group = $group;
}
diff --git a/lib/groupprofileblock.php b/lib/groupprofileblock.php
new file mode 100644
index 0000000000..9df541e343
--- /dev/null
+++ b/lib/groupprofileblock.php
@@ -0,0 +1,122 @@
+.
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Profile block to show for a group
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class GroupProfileBlock extends ProfileBlock
+{
+ protected $group = null;
+
+ function __construct($out, $group)
+ {
+ parent::__construct($out);
+ $this->group = $group;
+ }
+
+ function avatar()
+ {
+ return ($this->group->homepage_logo) ?
+ $this->group->homepage_logo : User_group::defaultLogo(AVATAR_PROFILE_SIZE);
+ }
+
+ function name()
+ {
+ return $this->group->getBestName();
+ }
+
+ function url()
+ {
+ return $this->group->mainpage;
+ }
+
+ function location()
+ {
+ return $this->group->location;
+ }
+
+ function homepage()
+ {
+ return $this->group->homepage;
+ }
+
+ function description()
+ {
+ return $this->group->description;
+ }
+
+ function showActions()
+ {
+ $cur = common_current_user();
+ $this->out->elementStart('div', 'entity_actions');
+ // TRANS: Group actions header (h2). Text hidden by default.
+ $this->out->element('h2', null, _('Group actions'));
+ $this->out->elementStart('ul');
+ if (Event::handle('StartGroupActionsList', array($this, $this->group))) {
+ $this->out->elementStart('li', 'entity_subscribe');
+ if (Event::handle('StartGroupSubscribe', array($this, $this->group))) {
+ if ($cur) {
+ if ($cur->isMember($this->group)) {
+ $lf = new LeaveForm($this->out, $this->group);
+ $lf->show();
+ } else if (!Group_block::isBlocked($this->group, $cur->getProfile())) {
+ $jf = new JoinForm($this->out, $this->group);
+ $jf->show();
+ }
+ }
+ Event::handle('EndGroupSubscribe', array($this, $this->group));
+ }
+ $this->out->elementEnd('li');
+ if ($cur && $cur->hasRight(Right::DELETEGROUP)) {
+ $this->out->elementStart('li', 'entity_delete');
+ $df = new DeleteGroupForm($this->out, $this->group);
+ $df->show();
+ $this->out->elementEnd('li');
+ }
+ Event::handle('EndGroupActionsList', array($this, $this->group));
+ }
+ $this->out->elementEnd('ul');
+ $this->out->elementEnd('div');
+ }
+}
diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php
index b341d14958..fdb693f92c 100644
--- a/lib/htmloutputter.php
+++ b/lib/htmloutputter.php
@@ -184,7 +184,7 @@ class HTMLOutputter extends XMLOutputter
$attrs = array('name' => $id,
'type' => 'text',
'id' => $id);
- if ($value) {
+ if (!is_null($value)) { // value can be 0 or ''
$attrs['value'] = $value;
}
$this->element('input', $attrs);
diff --git a/lib/imchannel.php b/lib/imchannel.php
new file mode 100644
index 0000000000..61355a429c
--- /dev/null
+++ b/lib/imchannel.php
@@ -0,0 +1,104 @@
+.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+class IMChannel extends Channel
+{
+
+ var $imPlugin;
+
+ function source()
+ {
+ return $imPlugin->transport;
+ }
+
+ function __construct($imPlugin)
+ {
+ $this->imPlugin = $imPlugin;
+ }
+
+ function on($user)
+ {
+ return $this->setNotify($user, 1);
+ }
+
+ function off($user)
+ {
+ return $this->setNotify($user, 0);
+ }
+
+ function output($user, $text)
+ {
+ $text = '['.common_config('site', 'name') . '] ' . $text;
+ $this->imPlugin->sendMessage($this->imPlugin->getScreenname($user), $text);
+ }
+
+ function error($user, $text)
+ {
+ $text = '['.common_config('site', 'name') . '] ' . $text;
+
+ $screenname = $this->imPlugin->getScreenname($user);
+ if($screenname){
+ $this->imPlugin->sendMessage($screenname, $text);
+ return true;
+ }else{
+ common_log(LOG_ERR,
+ 'Could not send error message to user ' . common_log_objstring($user) .
+ ' on transport ' . $this->imPlugin->transport .' : user preference does not exist');
+ return false;
+ }
+ }
+
+ function setNotify($user, $notify)
+ {
+ $user_im_prefs = new User_im_prefs();
+ $user_im_prefs->transport = $this->imPlugin->transport;
+ $user_im_prefs->user_id = $user->id;
+ if($user_im_prefs->find() && $user_im_prefs->fetch()){
+ if($user_im_prefs->notify == $notify){
+ //notify is already set the way they want
+ return true;
+ }else{
+ $original = clone($user_im_prefs);
+ $user_im_prefs->notify = $notify;
+ $result = $user_im_prefs->update($original);
+
+ if (!$result) {
+ $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError');
+ common_log(LOG_ERR,
+ 'Could not set notify flag to ' . $notify .
+ ' for user ' . common_log_objstring($user) .
+ ' on transport ' . $this->imPlugin->transport .' : ' . $last_error->message);
+ return false;
+ } else {
+ common_log(LOG_INFO,
+ 'User ' . $user->nickname . ' set notify flag to ' . $notify);
+ return true;
+ }
+ }
+ }else{
+ common_log(LOG_ERR,
+ 'Could not set notify flag to ' . $notify .
+ ' for user ' . common_log_objstring($user) .
+ ' on transport ' . $this->imPlugin->transport .' : user preference does not exist');
+ return false;
+ }
+ }
+}
diff --git a/lib/immanager.php b/lib/immanager.php
new file mode 100644
index 0000000000..9563a53262
--- /dev/null
+++ b/lib/immanager.php
@@ -0,0 +1,56 @@
+.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+/**
+ * IKM background connection manager for IM-using queue handlers,
+ * allowing them to send outgoing messages on the right connection.
+ *
+ * In a multi-site queuedaemon.php run, one connection will be instantiated
+ * for each site being handled by the current process that has IM enabled.
+ *
+ * Implementations that extend this class will likely want to:
+ * 1) override start() with their connection process.
+ * 2) override handleInput() with what to do when data is waiting on
+ * one of the sockets
+ * 3) override idle($timeout) to do keepalives (if necessary)
+ * 4) implement send_raw_message() to send raw data that ImPlugin::enqueueOutgoingRaw
+ * enqueued
+ */
+
+abstract class ImManager extends IoManager
+{
+ abstract function send_raw_message($data);
+
+ function __construct($imPlugin)
+ {
+ $this->plugin = $imPlugin;
+ $this->plugin->imManager = $this;
+ }
+
+ /**
+ * Fetch the singleton manager for the current site.
+ * @return mixed ImManager, or false if unneeded
+ */
+ public static function get()
+ {
+ throw new Exception('ImManager should be created using it\'s constructor, not the static get method');
+ }
+}
diff --git a/lib/implugin.php b/lib/implugin.php
new file mode 100644
index 0000000000..2811e7d644
--- /dev/null
+++ b/lib/implugin.php
@@ -0,0 +1,626 @@
+.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Superclass for plugins that do authentication
+ *
+ * Implementations will likely want to override onStartIoManagerClasses() so that their
+ * IO manager is used
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+abstract class ImPlugin extends Plugin
+{
+ //name of this IM transport
+ public $transport = null;
+ //list of screennames that should get all public notices
+ public $public = array();
+
+ /**
+ * normalize a screenname for comparison
+ *
+ * @param string $screenname screenname to normalize
+ *
+ * @return string an equivalent screenname in normalized form
+ */
+ abstract function normalize($screenname);
+
+ /**
+ * validate (ensure the validity of) a screenname
+ *
+ * @param string $screenname screenname to validate
+ *
+ * @return boolean
+ */
+ abstract function validate($screenname);
+
+ /**
+ * get the internationalized/translated display name of this IM service
+ *
+ * @return string
+ */
+ abstract function getDisplayName();
+
+ /**
+ * send a single notice to a given screenname
+ * The implementation should put raw data, ready to send, into the outgoing
+ * queue using enqueueOutgoingRaw()
+ *
+ * @param string $screenname screenname to send to
+ * @param Notice $notice notice to send
+ *
+ * @return boolean success value
+ */
+ function sendNotice($screenname, $notice)
+ {
+ return $this->sendMessage($screenname, $this->formatNotice($notice));
+ }
+
+ /**
+ * send a message (text) to a given screenname
+ * The implementation should put raw data, ready to send, into the outgoing
+ * queue using enqueueOutgoingRaw()
+ *
+ * @param string $screenname screenname to send to
+ * @param Notice $body text to send
+ *
+ * @return boolean success value
+ */
+ abstract function sendMessage($screenname, $body);
+
+ /**
+ * receive a raw message
+ * Raw IM data is taken from the incoming queue, and passed to this function.
+ * It should parse the raw message and call handleIncoming()
+ *
+ * Returning false may CAUSE REPROCESSING OF THE QUEUE ITEM, and should
+ * be used for temporary failures only. For permanent failures such as
+ * unrecognized addresses, return true to indicate your processing has
+ * completed.
+ *
+ * @param object $data raw IM data
+ *
+ * @return boolean true if processing completed, false for temporary failures
+ */
+ abstract function receiveRawMessage($data);
+
+ /**
+ * get the screenname of the daemon that sends and receives message for this service
+ *
+ * @return string screenname of this plugin
+ */
+ abstract function daemonScreenname();
+
+ /**
+ * get the microid uri of a given screenname
+ *
+ * @param string $screenname screenname
+ *
+ * @return string microid uri
+ */
+ function microiduri($screenname)
+ {
+ return $this->transport . ':' . $screenname;
+ }
+ //========================UTILITY FUNCTIONS USEFUL TO IMPLEMENTATIONS - MISC ========================\
+
+ /**
+ * Put raw message data (ready to send) into the outgoing queue
+ *
+ * @param object $data
+ */
+ function enqueueOutgoingRaw($data)
+ {
+ $qm = QueueManager::get();
+ $qm->enqueue($data, $this->transport . '-out');
+ }
+
+ /**
+ * Put raw message data (received, ready to be processed) into the incoming queue
+ *
+ * @param object $data
+ */
+ function enqueueIncomingRaw($data)
+ {
+ $qm = QueueManager::get();
+ $qm->enqueue($data, $this->transport . '-in');
+ }
+
+ /**
+ * given a screenname, get the corresponding user
+ *
+ * @param string $screenname
+ *
+ * @return User user
+ */
+ function getUser($screenname)
+ {
+ $user_im_prefs = $this->getUserImPrefsFromScreenname($screenname);
+ if($user_im_prefs){
+ $user = User::staticGet('id', $user_im_prefs->user_id);
+ $user_im_prefs->free();
+ return $user;
+ }else{
+ return false;
+ }
+ }
+
+ /**
+ * given a screenname, get the User_im_prefs object for this transport
+ *
+ * @param string $screenname
+ *
+ * @return User_im_prefs user_im_prefs
+ */
+ function getUserImPrefsFromScreenname($screenname)
+ {
+ $user_im_prefs = User_im_prefs::pkeyGet(
+ array('transport' => $this->transport,
+ 'screenname' => $this->normalize($screenname)));
+ if ($user_im_prefs) {
+ return $user_im_prefs;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * given a User, get their screenname
+ *
+ * @param User $user
+ *
+ * @return string screenname of that user
+ */
+ function getScreenname($user)
+ {
+ $user_im_prefs = $this->getUserImPrefsFromUser($user);
+ if ($user_im_prefs) {
+ return $user_im_prefs->screenname;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * given a User, get their User_im_prefs
+ *
+ * @param User $user
+ *
+ * @return User_im_prefs user_im_prefs of that user
+ */
+ function getUserImPrefsFromUser($user)
+ {
+ $user_im_prefs = User_im_prefs::pkeyGet(
+ array('transport' => $this->transport,
+ 'user_id' => $user->id));
+ if ($user_im_prefs){
+ return $user_im_prefs;
+ } else {
+ return false;
+ }
+ }
+ //========================UTILITY FUNCTIONS USEFUL TO IMPLEMENTATIONS - SENDING ========================\
+ /**
+ * Send a message to a given screenname from the site
+ *
+ * @param string $screenname screenname to send the message to
+ * @param string $msg message contents to send
+ *
+ * @param boolean success
+ */
+ protected function sendFromSite($screenname, $msg)
+ {
+ $text = '['.common_config('site', 'name') . '] ' . $msg;
+ $this->sendMessage($screenname, $text);
+ }
+
+ /**
+ * send a confirmation code to a user
+ *
+ * @param string $screenname screenname sending to
+ * @param string $code the confirmation code
+ * @param User $user user sending to
+ *
+ * @return boolean success value
+ */
+ function sendConfirmationCode($screenname, $code, $user)
+ {
+ $body = sprintf(_('User "%s" on %s has said that your %s screenname belongs to them. ' .
+ 'If that\'s true, you can confirm by clicking on this URL: ' .
+ '%s' .
+ ' . (If you cannot click it, copy-and-paste it into the ' .
+ 'address bar of your browser). If that user isn\'t you, ' .
+ 'or if you didn\'t request this confirmation, just ignore this message.'),
+ $user->nickname, common_config('site', 'name'), $this->getDisplayName(), common_local_url('confirmaddress', array('code' => $code)));
+
+ return $this->sendMessage($screenname, $body);
+ }
+
+ /**
+ * send a notice to all public listeners
+ *
+ * For notices that are generated on the local system (by users), we can optionally
+ * forward them to remote listeners by XMPP.
+ *
+ * @param Notice $notice notice to broadcast
+ *
+ * @return boolean success flag
+ */
+
+ function publicNotice($notice)
+ {
+ // Now, users who want everything
+
+ // FIXME PRIV don't send out private messages here
+ // XXX: should we send out non-local messages if public,localonly
+ // = false? I think not
+
+ foreach ($this->public as $screenname) {
+ common_log(LOG_INFO,
+ 'Sending notice ' . $notice->id .
+ ' to public listener ' . $screenname,
+ __FILE__);
+ $this->sendNotice($screenname, $notice);
+ }
+
+ return true;
+ }
+
+ /**
+ * broadcast a notice to all subscribers and reply recipients
+ *
+ * This function will send a notice to all subscribers on the local server
+ * who have IM addresses, and have IM notification enabled, and
+ * have this subscription enabled for IM. It also sends the notice to
+ * all recipients of @-replies who have IM addresses and IM notification
+ * enabled. This is really the heart of IM distribution in StatusNet.
+ *
+ * @param Notice $notice The notice to broadcast
+ *
+ * @return boolean success flag
+ */
+
+ function broadcastNotice($notice)
+ {
+
+ $ni = $notice->whoGets();
+
+ foreach ($ni as $user_id => $reason) {
+ $user = User::staticGet($user_id);
+ if (empty($user)) {
+ // either not a local user, or just not found
+ continue;
+ }
+ $user_im_prefs = $this->getUserImPrefsFromUser($user);
+ if(!$user_im_prefs || !$user_im_prefs->notify){
+ continue;
+ }
+
+ switch ($reason) {
+ case NOTICE_INBOX_SOURCE_REPLY:
+ if (!$user_im_prefs->replies) {
+ continue 2;
+ }
+ break;
+ case NOTICE_INBOX_SOURCE_SUB:
+ $sub = Subscription::pkeyGet(array('subscriber' => $user->id,
+ 'subscribed' => $notice->profile_id));
+ if (empty($sub) || !$sub->jabber) {
+ continue 2;
+ }
+ break;
+ case NOTICE_INBOX_SOURCE_GROUP:
+ break;
+ default:
+ throw new Exception(sprintf(_("Unknown inbox source %d."), $reason));
+ }
+
+ common_log(LOG_INFO,
+ 'Sending notice ' . $notice->id . ' to ' . $user_im_prefs->screenname,
+ __FILE__);
+ $this->sendNotice($user_im_prefs->screenname, $notice);
+ $user_im_prefs->free();
+ }
+
+ return true;
+ }
+
+ /**
+ * makes a plain-text formatted version of a notice, suitable for IM distribution
+ *
+ * @param Notice $notice notice being sent
+ *
+ * @return string plain-text version of the notice, with user nickname prefixed
+ */
+
+ function formatNotice($notice)
+ {
+ $profile = $notice->getProfile();
+ return $profile->nickname . ': ' . $notice->content . ' [' . $notice->id . ']';
+ }
+ //========================UTILITY FUNCTIONS USEFUL TO IMPLEMENTATIONS - RECEIVING ========================\
+
+ /**
+ * Attempt to handle a message as a command
+ * @param User $user user the message is from
+ * @param string $body message text
+ * @return boolean true if the message was a command and was executed, false if it was not a command
+ */
+ protected function handleCommand($user, $body)
+ {
+ $inter = new CommandInterpreter();
+ $cmd = $inter->handle_command($user, $body);
+ if ($cmd) {
+ $chan = new IMChannel($this);
+ $cmd->execute($chan);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Is some text an autoreply message?
+ * @param string $txt message text
+ * @return boolean true if autoreply
+ */
+ protected function isAutoreply($txt)
+ {
+ if (preg_match('/[\[\(]?[Aa]uto[-\s]?[Rr]e(ply|sponse)[\]\)]/', $txt)) {
+ return true;
+ } else if (preg_match('/^System: Message wasn\'t delivered. Offline storage size was exceeded.$/', $txt)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Is some text an OTR message?
+ * @param string $txt message text
+ * @return boolean true if OTR
+ */
+ protected function isOtr($txt)
+ {
+ if (preg_match('/^\?OTR/', $txt)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Helper for handling incoming messages
+ * Your incoming message handler will probably want to call this function
+ *
+ * @param string $from screenname the message was sent from
+ * @param string $message message contents
+ *
+ * @param boolean success
+ */
+ protected function handleIncoming($from, $notice_text)
+ {
+ $user = $this->getUser($from);
+ // For common_current_user to work
+ global $_cur;
+ $_cur = $user;
+
+ if (!$user) {
+ $this->sendFromSite($from, 'Unknown user; go to ' .
+ common_local_url('imsettings') .
+ ' to add your address to your account');
+ common_log(LOG_WARNING, 'Message from unknown user ' . $from);
+ return;
+ }
+ if ($this->handleCommand($user, $notice_text)) {
+ common_log(LOG_INFO, "Command message by $from handled.");
+ return;
+ } else if ($this->isAutoreply($notice_text)) {
+ common_log(LOG_INFO, 'Ignoring auto reply from ' . $from);
+ return;
+ } else if ($this->isOtr($notice_text)) {
+ common_log(LOG_INFO, 'Ignoring OTR from ' . $from);
+ return;
+ } else {
+
+ common_log(LOG_INFO, 'Posting a notice from ' . $user->nickname);
+
+ $this->addNotice($from, $user, $notice_text);
+ }
+
+ $user->free();
+ unset($user);
+ unset($_cur);
+ unset($message);
+ }
+
+ /**
+ * Helper for handling incoming messages
+ * Your incoming message handler will probably want to call this function
+ *
+ * @param string $from screenname the message was sent from
+ * @param string $message message contents
+ *
+ * @param boolean success
+ */
+ protected function addNotice($screenname, $user, $body)
+ {
+ $body = trim(strip_tags($body));
+ $content_shortened = common_shorten_links($body);
+ if (Notice::contentTooLong($content_shortened)) {
+ $this->sendFromSite($screenname, sprintf(_('Message too long - maximum is %1$d characters, you sent %2$d.'),
+ Notice::maxContent(),
+ mb_strlen($content_shortened)));
+ return;
+ }
+
+ try {
+ $notice = Notice::saveNew($user->id, $content_shortened, $this->transport);
+ } catch (Exception $e) {
+ common_log(LOG_ERR, $e->getMessage());
+ $this->sendFromSite($from, $e->getMessage());
+ return;
+ }
+
+ common_log(LOG_INFO,
+ 'Added notice ' . $notice->id . ' from user ' . $user->nickname);
+ $notice->free();
+ unset($notice);
+ }
+
+ //========================EVENT HANDLERS========================\
+
+ /**
+ * Register notice queue handler
+ *
+ * @param QueueManager $manager
+ *
+ * @return boolean hook return
+ */
+ function onEndInitializeQueueManager($manager)
+ {
+ $manager->connect($this->transport . '-in', new ImReceiverQueueHandler($this), 'im');
+ $manager->connect($this->transport, new ImQueueHandler($this));
+ $manager->connect($this->transport . '-out', new ImSenderQueueHandler($this), 'im');
+ return true;
+ }
+
+ function onStartImDaemonIoManagers(&$classes)
+ {
+ //$classes[] = new ImManager($this); // handles sending/receiving/pings/reconnects
+ return true;
+ }
+
+ function onStartEnqueueNotice($notice, &$transports)
+ {
+ $profile = Profile::staticGet($notice->profile_id);
+
+ if (!$profile) {
+ common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
+ 'unknown profile ' . common_log_objstring($notice),
+ __FILE__);
+ }else{
+ $transports[] = $this->transport;
+ }
+
+ return true;
+ }
+
+ function onEndShowHeadElements($action)
+ {
+ $aname = $action->trimmed('action');
+
+ if ($aname == 'shownotice') {
+
+ $user_im_prefs = new User_im_prefs();
+ $user_im_prefs->user_id = $action->profile->id;
+ $user_im_prefs->transport = $this->transport;
+
+ if ($user_im_prefs->find() && $user_im_prefs->fetch() && $user_im_prefs->microid && $action->notice->uri) {
+ $id = new Microid($this->microiduri($user_im_prefs->screenname),
+ $action->notice->uri);
+ $action->element('meta', array('name' => 'microid',
+ 'content' => $id->toString()));
+ }
+
+ } else if ($aname == 'showstream') {
+
+ $user_im_prefs = new User_im_prefs();
+ $user_im_prefs->user_id = $action->user->id;
+ $user_im_prefs->transport = $this->transport;
+
+ if ($user_im_prefs->find() && $user_im_prefs->fetch() && $user_im_prefs->microid && $action->profile->profileurl) {
+ $id = new Microid($this->microiduri($user_im_prefs->screenname),
+ $action->selfUrl());
+ $action->element('meta', array('name' => 'microid',
+ 'content' => $id->toString()));
+ }
+ }
+ }
+
+ function onNormalizeImScreenname($transport, &$screenname)
+ {
+ if($transport == $this->transport)
+ {
+ $screenname = $this->normalize($screenname);
+ return false;
+ }
+ }
+
+ function onValidateImScreenname($transport, $screenname, &$valid)
+ {
+ if($transport == $this->transport)
+ {
+ $valid = $this->validate($screenname);
+ return false;
+ }
+ }
+
+ function onGetImTransports(&$transports)
+ {
+ $transports[$this->transport] = array(
+ 'display' => $this->getDisplayName(),
+ 'daemonScreenname' => $this->daemonScreenname());
+ }
+
+ function onSendImConfirmationCode($transport, $screenname, $code, $user)
+ {
+ if($transport == $this->transport)
+ {
+ $this->sendConfirmationCode($screenname, $code, $user);
+ return false;
+ }
+ }
+
+ function onUserDeleteRelated($user, &$tables)
+ {
+ $tables[] = 'User_im_prefs';
+ return true;
+ }
+
+ function initialize()
+ {
+ if( ! common_config('queue', 'enabled'))
+ {
+ throw new ServerException("Queueing must be enabled to use IM plugins");
+ }
+
+ if(is_null($this->transport)){
+ throw new ServerException('transport cannot be null');
+ }
+ }
+}
diff --git a/lib/jabberqueuehandler.php b/lib/imqueuehandler.php
similarity index 60%
rename from lib/jabberqueuehandler.php
rename to lib/imqueuehandler.php
index d6b4b7416a..9c35890c62 100644
--- a/lib/jabberqueuehandler.php
+++ b/lib/imqueuehandler.php
@@ -17,31 +17,32 @@
* along with this program. If not, see .
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
/**
- * Queue handler for pushing new notices to Jabber users.
- * @fixme this exception handling doesn't look very good.
+ * Common superclass for all IM sending queue handlers.
*/
-class JabberQueueHandler extends QueueHandler
-{
- var $conn = null;
- function transport()
+class ImQueueHandler extends QueueHandler
+{
+ function __construct($plugin)
{
- return 'jabber';
+ $this->plugin = $plugin;
}
+ /**
+ * Handle a notice
+ * @param Notice $notice
+ * @return boolean success
+ */
function handle($notice)
{
- require_once(INSTALLDIR.'/lib/jabber.php');
- try {
- return jabber_broadcast_notice($notice);
- } catch (XMPPHP_Exception $e) {
- common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
- return false;
+ $this->plugin->broadcastNotice($notice);
+ if ($notice->is_local == Notice::LOCAL_PUBLIC ||
+ $notice->is_local == Notice::LOCAL_NONPUBLIC) {
+ $this->plugin->publicNotice($notice);
}
+ return true;
}
+
}
diff --git a/lib/publicqueuehandler.php b/lib/imreceiverqueuehandler.php
similarity index 60%
rename from lib/publicqueuehandler.php
rename to lib/imreceiverqueuehandler.php
index a497d13850..aa4a663b7a 100644
--- a/lib/publicqueuehandler.php
+++ b/lib/imreceiverqueuehandler.php
@@ -17,29 +17,26 @@
* along with this program. If not, see .
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
/**
- * Queue handler for pushing new notices to public XMPP subscribers.
+ * Common superclass for all IM receiving queue handlers.
*/
-class PublicQueueHandler extends QueueHandler
-{
- function transport()
+class ImReceiverQueueHandler extends QueueHandler
+{
+ function __construct($plugin)
{
- return 'public';
+ $this->plugin = $plugin;
}
- function handle($notice)
+ /**
+ * Handle incoming IM data sent by a user to the IM bot
+ * @param object $data
+ * @return boolean success
+ */
+ function handle($data)
{
- require_once(INSTALLDIR.'/lib/jabber.php');
- try {
- return jabber_public_notice($notice);
- } catch (XMPPHP_Exception $e) {
- common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
- return false;
- }
+ return $this->plugin->receiveRawMessage($data);
}
}
diff --git a/lib/imsenderqueuehandler.php b/lib/imsenderqueuehandler.php
new file mode 100644
index 0000000000..790dd7b107
--- /dev/null
+++ b/lib/imsenderqueuehandler.php
@@ -0,0 +1,43 @@
+.
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
+
+/**
+ * Common superclass for all IM sending queue handlers.
+ */
+
+class ImSenderQueueHandler extends QueueHandler
+{
+ function __construct($plugin)
+ {
+ $this->plugin = $plugin;
+ }
+
+ /**
+ * Handle outgoing IM data to be sent from the bot to a user
+ * @param object $data
+ * @return boolean success
+ */
+ function handle($data)
+ {
+ return $this->plugin->imManager->send_raw_message($data);
+ }
+}
+
diff --git a/lib/info.php b/lib/info.php
index 395c6522ec..f72bed59d6 100644
--- a/lib/info.php
+++ b/lib/info.php
@@ -93,8 +93,14 @@ class InfoAction extends Action
function showCore()
{
$this->elementStart('div', array('id' => 'core'));
+ $this->elementStart('div', array('id' => 'aside_primary_wrapper'));
+ $this->elementStart('div', array('id' => 'content_wrapper'));
+ $this->elementStart('div', array('id' => 'site_nav_local_views_wrapper'));
$this->showContentBlock();
$this->elementEnd('div');
+ $this->elementEnd('div');
+ $this->elementEnd('div');
+ $this->elementEnd('div');
}
function showHeader()
diff --git a/lib/installer.php b/lib/installer.php
index a9d8090110..1add65ba81 100644
--- a/lib/installer.php
+++ b/lib/installer.php
@@ -2,7 +2,7 @@
/**
* StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
+ * Copyright (C) 2009-2010, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -32,9 +32,10 @@
* @author Sarven Capadisli
* @author Tom Adams
* @author Zach Copley
+ * @copyright 2009-2010 StatusNet, Inc http://status.net
* @copyright 2009 Free Software Foundation, Inc http://www.fsf.org
* @license GNU Affero General Public License http://www.gnu.org/licenses/
- * @version 0.9.x
+ * @version 1.0.x
* @link http://status.net
*/
@@ -53,12 +54,12 @@ abstract class Installer
'mysql' => array(
'name' => 'MySQL',
'check_module' => 'mysqli',
- 'installer' => 'mysql_db_installer',
+ 'scheme' => 'mysqli', // DSN prefix for PEAR::DB
),
'pgsql' => array(
'name' => 'PostgreSQL',
'check_module' => 'pgsql',
- 'installer' => 'pgsql_db_installer',
+ 'scheme' => 'pgsql', // DSN prefix for PEAR::DB
),
);
@@ -235,7 +236,7 @@ abstract class Installer
}
// @fixme hardcoded list; should use User::allowed_nickname()
// if/when it's safe to have loaded the infrastructure here
- $blacklist = array('main', 'admin', 'twitter', 'settings', 'rsd.xml', 'favorited', 'featured', 'favoritedrss', 'featuredrss', 'rss', 'getfile', 'api', 'groups', 'group', 'peopletag', 'tag', 'user', 'message', 'conversation', 'bookmarklet', 'notice', 'attachment', 'search', 'index.php', 'doc', 'opensearch', 'robots.txt', 'xd_receiver.html', 'facebook');
+ $blacklist = array('main', 'panel', 'twitter', 'settings', 'rsd.xml', 'favorited', 'featured', 'favoritedrss', 'featuredrss', 'rss', 'getfile', 'api', 'groups', 'group', 'peopletag', 'tag', 'user', 'message', 'conversation', 'bookmarklet', 'notice', 'attachment', 'search', 'index.php', 'doc', 'opensearch', 'robots.txt', 'xd_receiver.html', 'facebook');
if (in_array($this->adminNick, $blacklist)) {
$this->updateStatus('The user nickname "' . htmlspecialchars($this->adminNick) .
'" is reserved.', true);
@@ -254,6 +255,7 @@ abstract class Installer
* Set up the database with the appropriate function for the selected type...
* Saves database info into $this->db.
*
+ * @fixme escape things in the connection string in case we have a funny pass etc
* @return mixed array of database connection params on success, false on failure
*/
function setupDatabase()
@@ -261,119 +263,39 @@ abstract class Installer
if ($this->db) {
throw new Exception("Bad order of operations: DB already set up.");
}
- $method = self::$dbModules[$this->dbtype]['installer'];
- $db = call_user_func(array($this, $method),
- $this->host,
- $this->database,
- $this->username,
- $this->password);
- $this->db = $db;
- return $this->db;
- }
-
- /**
- * Set up a database on PostgreSQL.
- * Will output status updates during the operation.
- *
- * @param string $host
- * @param string $database
- * @param string $username
- * @param string $password
- * @return mixed array of database connection params on success, false on failure
- *
- * @fixme escape things in the connection string in case we have a funny pass etc
- */
- function Pgsql_Db_installer($host, $database, $username, $password)
- {
- $connstring = "dbname=$database host=$host user=$username";
-
- //No password would mean trust authentication used.
- if (!empty($password)) {
- $connstring .= " password=$password";
- }
$this->updateStatus("Starting installation...");
+
+ if (empty($this->password)) {
+ $auth = '';
+ } else {
+ $auth = ":$this->password";
+ }
+ $scheme = self::$dbModules[$this->dbtype]['scheme'];
+ $dsn = "{$scheme}://{$this->username}{$auth}@{$this->host}/{$this->database}";
+
$this->updateStatus("Checking database...");
- $conn = pg_connect($connstring);
+ $conn = $this->connectDatabase($dsn);
- if ($conn ===false) {
- $this->updateStatus("Failed to connect to database: $connstring");
- return false;
- }
-
- //ensure database encoding is UTF8
- $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
- if ($record->server_encoding != 'UTF8') {
- $this->updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
- return false;
- }
-
- $this->updateStatus("Running database script...");
- //wrap in transaction;
- pg_query($conn, 'BEGIN');
- $res = $this->runDbScript('statusnet_pg.sql', $conn, 'pgsql');
-
- if ($res === false) {
- $this->updateStatus("Can't run database script.", true);
- return false;
- }
- foreach (array('sms_carrier' => 'SMS carrier',
- 'notice_source' => 'notice source',
- 'foreign_services' => 'foreign service')
- as $scr => $name) {
- $this->updateStatus(sprintf("Adding %s data to database...", $name));
- $res = $this->runDbScript($scr.'.sql', $conn, 'pgsql');
- if ($res === false) {
- $this->updateStatus(sprintf("Can't run %s script.", $name), true);
+ // ensure database encoding is UTF8
+ if ($this->dbtype == 'mysql') {
+ // @fixme utf8m4 support for mysql 5.5?
+ // Force the comms charset to utf8 for sanity
+ // This doesn't currently work. :P
+ //$conn->executes('set names utf8');
+ } else if ($this->dbtype == 'pgsql') {
+ $record = $conn->getRow('SHOW server_encoding');
+ if ($record->server_encoding != 'UTF8') {
+ $this->updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
return false;
}
}
- pg_query($conn, 'COMMIT');
- if (empty($password)) {
- $sqlUrl = "pgsql://$username@$host/$database";
- } else {
- $sqlUrl = "pgsql://$username:$password@$host/$database";
- }
-
- $db = array('type' => 'pgsql', 'database' => $sqlUrl);
-
- return $db;
- }
-
- /**
- * Set up a database on MySQL.
- * Will output status updates during the operation.
- *
- * @param string $host
- * @param string $database
- * @param string $username
- * @param string $password
- * @return mixed array of database connection params on success, false on failure
- *
- * @fixme escape things in the connection string in case we have a funny pass etc
- */
- function Mysql_Db_installer($host, $database, $username, $password)
- {
- $this->updateStatus("Starting installation...");
- $this->updateStatus("Checking database...");
-
- $conn = mysqli_init();
- if (!$conn->real_connect($host, $username, $password)) {
- $this->updateStatus("Can't connect to server '$host' as '$username'.", true);
- return false;
- }
- $this->updateStatus("Changing to database...");
- if (!$conn->select_db($database)) {
- $this->updateStatus("Can't change to database.", true);
+ $res = $this->updateStatus("Creating database tables...");
+ if (!$this->createCoreTables($conn)) {
+ $this->updateStatus("Error creating tables.", true);
return false;
}
- $this->updateStatus("Running database script...");
- $res = $this->runDbScript('statusnet.sql', $conn);
- if ($res === false) {
- $this->updateStatus("Can't run database script.", true);
- return false;
- }
foreach (array('sms_carrier' => 'SMS carrier',
'notice_source' => 'notice source',
'foreign_services' => 'foreign service')
@@ -386,11 +308,54 @@ abstract class Installer
}
}
- $sqlUrl = "mysqli://$username:$password@$host/$database";
- $db = array('type' => 'mysql', 'database' => $sqlUrl);
+ $db = array('type' => $this->dbtype, 'database' => $dsn);
return $db;
}
+ /**
+ * Open a connection to the database.
+ *
+ * @param $dsn
+ * @return
+ */
+ function connectDatabase($dsn)
+ {
+ // @fixme move this someplace more sensible
+ //set_include_path(INSTALLDIR . '/extlib' . PATH_SEPARATOR . get_include_path());
+ require_once 'DB.php';
+ return DB::connect($dsn);
+ }
+
+ /**
+ * Create core tables on the given database connection.
+ *
+ * @param DB_common $conn
+ */
+ function createCoreTables(DB_common $conn)
+ {
+ $schema = Schema::get($conn);
+ $tableDefs = $this->getCoreSchema();
+ foreach ($tableDefs as $name => $def) {
+ if (defined('DEBUG_INSTALLER')) {
+ echo " $name ";
+ }
+ $schema->ensureTable($name, $def);
+ }
+ return true;
+ }
+
+ /**
+ * Fetch the core table schema definitions.
+ *
+ * @return array of table names => table def arrays
+ */
+ function getCoreSchema()
+ {
+ $schema = array();
+ include INSTALLDIR . '/db/core.php';
+ return $schema;
+ }
+
/**
* Return a parseable PHP literal for the given value.
* This will include quotes for strings, etc.
@@ -463,13 +428,12 @@ abstract class Installer
/**
* Install schema into the database
*
- * @param string $filename location of database schema file
- * @param dbconn $conn connection to database
- * @param string $type type of database, currently mysql or pgsql
+ * @param string $filename location of database schema file
+ * @param DB_common $conn connection to database
*
* @return boolean - indicating success or failure
*/
- function runDbScript($filename, $conn, $type = 'mysqli')
+ function runDbScript($filename, DB_common $conn)
{
$sql = trim(file_get_contents(INSTALLDIR . '/db/' . $filename));
$stmts = explode(';', $sql);
@@ -478,26 +442,12 @@ abstract class Installer
if (!mb_strlen($stmt)) {
continue;
}
- // FIXME: use PEAR::DB or PDO instead of our own switch
- switch ($type) {
- case 'mysqli':
- $res = $conn->query($stmt);
- if ($res === false) {
- $error = $conn->error;
- }
- break;
- case 'pgsql':
- $res = pg_query($conn, $stmt);
- if ($res === false) {
- $error = pg_last_error();
- }
- break;
- default:
- $this->updateStatus("runDbScript() error: unknown database type ". $type ." provided.");
- }
- if ($res === false) {
+ try {
+ $res = $conn->simpleQuery($stmt);
+ } catch (Exception $e) {
+ $error = $e->getMessage();
$this->updateStatus("ERROR ($error) for SQL '$stmt'");
- return $res;
+ return false;
}
}
return true;
@@ -510,9 +460,6 @@ abstract class Installer
*/
function registerInitialUser()
{
- define('STATUSNET', true);
- define('LACONICA', true); // compatibility
-
require_once INSTALLDIR . '/lib/common.php';
$data = array('nickname' => $this->adminNick,
@@ -559,10 +506,22 @@ abstract class Installer
*/
function doInstall()
{
- $this->db = $this->setupDatabase();
+ $this->updateStatus("Initializing...");
+ ini_set('display_errors', 1);
+ error_reporting(E_ALL);
+ define('STATUSNET', 1);
+ require_once INSTALLDIR . '/lib/framework.php';
+ StatusNet::initDefaults($this->server, $this->path);
- if (!$this->db) {
- // database connection failed, do not move on to create config file.
+ try {
+ $this->db = $this->setupDatabase();
+ if (!$this->db) {
+ // database connection failed, do not move on to create config file.
+ return false;
+ }
+ } catch (Exception $e) {
+ // Lower-level DB error!
+ $this->updateStatus("Database error: " . $e->getMessage(), true);
return false;
}
diff --git a/lib/jabber.php b/lib/jabber.php
deleted file mode 100644
index cdcfc44232..0000000000
--- a/lib/jabber.php
+++ /dev/null
@@ -1,640 +0,0 @@
-.
- *
- * @category Network
- * @package StatusNet
- * @author Evan Prodromou
- * @copyright 2008 StatusNet, Inc.
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once 'XMPPHP/XMPP.php';
-
-/**
- * Splits a Jabber ID (JID) into node, domain, and resource portions.
- *
- * Based on validation routine submitted by:
- * @copyright 2009 Patrick Georgi
- * @license Licensed under ISC-L, which is compatible with everything else that keeps the copyright notice intact.
- *
- * @param string $jid string to check
- *
- * @return array with "node", "domain", and "resource" indices
- * @throws Exception if input is not valid
- */
-
-function jabber_split_jid($jid)
-{
- $chars = '';
- /* the following definitions come from stringprep, Appendix C,
- which is used in its entirety by nodeprop, Chapter 5, "Prohibited Output" */
- /* C1.1 ASCII space characters */
- $chars .= "\x{20}";
- /* C1.2 Non-ASCII space characters */
- $chars .= "\x{a0}\x{1680}\x{2000}-\x{200b}\x{202f}\x{205f}\x{3000a}";
- /* C2.1 ASCII control characters */
- $chars .= "\x{00}-\x{1f}\x{7f}";
- /* C2.2 Non-ASCII control characters */
- $chars .= "\x{80}-\x{9f}\x{6dd}\x{70f}\x{180e}\x{200c}\x{200d}\x{2028}\x{2029}\x{2060}-\x{2063}\x{206a}-\x{206f}\x{feff}\x{fff9}-\x{fffc}\x{1d173}-\x{1d17a}";
- /* C3 - Private Use */
- $chars .= "\x{e000}-\x{f8ff}\x{f0000}-\x{ffffd}\x{100000}-\x{10fffd}";
- /* C4 - Non-character code points */
- $chars .= "\x{fdd0}-\x{fdef}\x{fffe}\x{ffff}\x{1fffe}\x{1ffff}\x{2fffe}\x{2ffff}\x{3fffe}\x{3ffff}\x{4fffe}\x{4ffff}\x{5fffe}\x{5ffff}\x{6fffe}\x{6ffff}\x{7fffe}\x{7ffff}\x{8fffe}\x{8ffff}\x{9fffe}\x{9ffff}\x{afffe}\x{affff}\x{bfffe}\x{bffff}\x{cfffe}\x{cffff}\x{dfffe}\x{dffff}\x{efffe}\x{effff}\x{ffffe}\x{fffff}\x{10fffe}\x{10ffff}";
- /* C5 - Surrogate codes */
- $chars .= "\x{d800}-\x{dfff}";
- /* C6 - Inappropriate for plain text */
- $chars .= "\x{fff9}-\x{fffd}";
- /* C7 - Inappropriate for canonical representation */
- $chars .= "\x{2ff0}-\x{2ffb}";
- /* C8 - Change display properties or are deprecated */
- $chars .= "\x{340}\x{341}\x{200e}\x{200f}\x{202a}-\x{202e}\x{206a}-\x{206f}";
- /* C9 - Tagging characters */
- $chars .= "\x{e0001}\x{e0020}-\x{e007f}";
-
- /* Nodeprep forbids some more characters */
- $nodeprepchars = $chars;
- $nodeprepchars .= "\x{22}\x{26}\x{27}\x{2f}\x{3a}\x{3c}\x{3e}\x{40}";
-
- $parts = explode("/", $jid, 2);
- if (count($parts) > 1) {
- $resource = $parts[1];
- if ($resource == '') {
- // Warning: empty resource isn't legit.
- // But if we're normalizing, we may as well take it...
- }
- } else {
- $resource = null;
- }
-
- $node = explode("@", $parts[0]);
- if ((count($node) > 2) || (count($node) == 0)) {
- throw new Exception("Invalid JID: too many @s");
- } else if (count($node) == 1) {
- $domain = $node[0];
- $node = null;
- } else {
- $domain = $node[1];
- $node = $node[0];
- if ($node == '') {
- throw new Exception("Invalid JID: @ but no node");
- }
- }
-
- // Length limits per http://xmpp.org/rfcs/rfc3920.html#addressing
- if ($node !== null) {
- if (strlen($node) > 1023) {
- throw new Exception("Invalid JID: node too long.");
- }
- if (preg_match("/[".$nodeprepchars."]/u", $node)) {
- throw new Exception("Invalid JID node '$node'");
- }
- }
-
- if (strlen($domain) > 1023) {
- throw new Exception("Invalid JID: domain too long.");
- }
- if (!common_valid_domain($domain)) {
- throw new Exception("Invalid JID domain name '$domain'");
- }
-
- if ($resource !== null) {
- if (strlen($resource) > 1023) {
- throw new Exception("Invalid JID: resource too long.");
- }
- if (preg_match("/[".$chars."]/u", $resource)) {
- throw new Exception("Invalid JID resource '$resource'");
- }
- }
-
- return array('node' => is_null($node) ? null : mb_strtolower($node),
- 'domain' => is_null($domain) ? null : mb_strtolower($domain),
- 'resource' => $resource);
-}
-
-/**
- * Checks whether a string is a syntactically valid Jabber ID (JID),
- * either with or without a resource.
- *
- * Note that a bare domain can be a valid JID.
- *
- * @param string $jid string to check
- * @param bool $check_domain whether we should validate that domain...
- *
- * @return boolean whether the string is a valid JID
- */
-function jabber_valid_full_jid($jid, $check_domain=false)
-{
- try {
- $parts = jabber_split_jid($jid);
- if ($check_domain) {
- if (!jabber_check_domain($parts['domain'])) {
- return false;
- }
- }
- return $parts['resource'] !== ''; // missing or present; empty ain't kosher
- } catch (Exception $e) {
- return false;
- }
-}
-
-/**
- * Checks whether a string is a syntactically valid base Jabber ID (JID).
- * A base JID won't include a resource specifier on the end; since we
- * take it off when reading input we can't really use them reliably
- * to direct outgoing messages yet (sorry guys!)
- *
- * Note that a bare domain can be a valid JID.
- *
- * @param string $jid string to check
- * @param bool $check_domain whether we should validate that domain...
- *
- * @return boolean whether the string is a valid JID
- */
-function jabber_valid_base_jid($jid, $check_domain=false)
-{
- try {
- $parts = jabber_split_jid($jid);
- if ($check_domain) {
- if (!jabber_check_domain($parts['domain'])) {
- return false;
- }
- }
- return ($parts['resource'] === null); // missing; empty ain't kosher
- } catch (Exception $e) {
- return false;
- }
-}
-
-/**
- * Normalizes a Jabber ID for comparison, dropping the resource component if any.
- *
- * @param string $jid JID to check
- * @param bool $check_domain if true, reject if the domain isn't findable
- *
- * @return string an equivalent JID in normalized (lowercase) form
- */
-
-function jabber_normalize_jid($jid)
-{
- try {
- $parts = jabber_split_jid($jid);
- if ($parts['node'] !== null) {
- return $parts['node'] . '@' . $parts['domain'];
- } else {
- return $parts['domain'];
- }
- } catch (Exception $e) {
- return null;
- }
-}
-
-/**
- * Check if this domain's got some legit DNS record
- */
-function jabber_check_domain($domain)
-{
- if (checkdnsrr("_xmpp-server._tcp." . $domain, "SRV")) {
- return true;
- }
- if (checkdnsrr($domain, "ANY")) {
- return true;
- }
- return false;
-}
-
-/**
- * the JID of the Jabber daemon for this StatusNet instance
- *
- * @return string JID of the Jabber daemon
- */
-
-function jabber_daemon_address()
-{
- return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server');
-}
-
-class Sharing_XMPP extends XMPPHP_XMPP
-{
- function getSocket()
- {
- return $this->socket;
- }
-}
-
-/**
- * Build an XMPP proxy connection that'll save outgoing messages
- * to the 'xmppout' queue to be picked up by xmppdaemon later.
- *
- * If queueing is disabled, we'll grab a live connection.
- *
- * @return XMPPHP
- */
-function jabber_proxy()
-{
- if (common_config('queue', 'enabled')) {
- $proxy = new Queued_XMPP(common_config('xmpp', 'host') ?
- common_config('xmpp', 'host') :
- common_config('xmpp', 'server'),
- common_config('xmpp', 'port'),
- common_config('xmpp', 'user'),
- common_config('xmpp', 'password'),
- common_config('xmpp', 'resource') . 'daemon',
- common_config('xmpp', 'server'),
- common_config('xmpp', 'debug') ?
- true : false,
- common_config('xmpp', 'debug') ?
- XMPPHP_Log::LEVEL_VERBOSE : null);
- return $proxy;
- } else {
- return jabber_connect();
- }
-}
-
-/**
- * Lazy-connect the configured Jabber account to the configured server;
- * if already opened, the same connection will be returned.
- *
- * In a multi-site background process, each site configuration
- * will get its own connection.
- *
- * @param string $resource Resource to connect (defaults to configured resource)
- *
- * @return XMPPHP connection to the configured server
- */
-
-function jabber_connect($resource=null)
-{
- static $connections = array();
- $site = common_config('site', 'server');
- if (empty($connections[$site])) {
- if (empty($resource)) {
- $resource = common_config('xmpp', 'resource');
- }
- $conn = new Sharing_XMPP(common_config('xmpp', 'host') ?
- common_config('xmpp', 'host') :
- common_config('xmpp', 'server'),
- common_config('xmpp', 'port'),
- common_config('xmpp', 'user'),
- common_config('xmpp', 'password'),
- $resource,
- common_config('xmpp', 'server'),
- common_config('xmpp', 'debug') ?
- true : false,
- common_config('xmpp', 'debug') ?
- XMPPHP_Log::LEVEL_VERBOSE : null
- );
-
- if (!$conn) {
- return false;
- }
- $connections[$site] = $conn;
-
- $conn->autoSubscribe();
- $conn->useEncryption(common_config('xmpp', 'encryption'));
-
- try {
- common_log(LOG_INFO, __METHOD__ . ": connecting " .
- common_config('xmpp', 'user') . '/' . $resource);
- //$conn->connect(true); // true = persistent connection
- $conn->connect(); // persistent connections break multisite
- } catch (XMPPHP_Exception $e) {
- common_log(LOG_ERR, $e->getMessage());
- return false;
- }
-
- $conn->processUntil('session_start');
- }
- return $connections[$site];
-}
-
-/**
- * Queue send for a single notice to a given Jabber address
- *
- * @param string $to JID to send the notice to
- * @param Notice $notice notice to send
- *
- * @return boolean success value
- */
-
-function jabber_send_notice($to, $notice)
-{
- $conn = jabber_proxy();
- $profile = Profile::staticGet($notice->profile_id);
- if (!$profile) {
- common_log(LOG_WARNING, 'Refusing to send notice with ' .
- 'unknown profile ' . common_log_objstring($notice),
- __FILE__);
- return false;
- }
- $msg = jabber_format_notice($profile, $notice);
- $entry = jabber_format_entry($profile, $notice);
- $conn->message($to, $msg, 'chat', null, $entry);
- $profile->free();
- return true;
-}
-
-/**
- * extra information for XMPP messages, as defined by Twitter
- *
- * @param Profile $profile Profile of the sending user
- * @param Notice $notice Notice being sent
- *
- * @return string Extra information (Atom, HTML, addresses) in string format
- */
-
-function jabber_format_entry($profile, $notice)
-{
- $entry = $notice->asAtomEntry(true, true);
-
- $xs = new XMLStringer();
- $xs->elementStart('html', array('xmlns' => 'http://jabber.org/protocol/xhtml-im'));
- $xs->elementStart('body', array('xmlns' => 'http://www.w3.org/1999/xhtml'));
- $xs->element('a', array('href' => $profile->profileurl),
- $profile->nickname);
- $xs->text(": ");
- if (!empty($notice->rendered)) {
- $xs->raw($notice->rendered);
- } else {
- $xs->raw(common_render_content($notice->content, $notice));
- }
- $xs->text(" ");
- $xs->element('a', array(
- 'href'=>common_local_url('conversation',
- array('id' => $notice->conversation)).'#notice-'.$notice->id
- ),sprintf(_('[%s]'),$notice->id));
- $xs->elementEnd('body');
- $xs->elementEnd('html');
-
- $html = $xs->getString();
-
- return $html . ' ' . $entry;
-}
-
-/**
- * sends a single text message to a given JID
- *
- * @param string $to JID to send the message to
- * @param string $body body of the message
- * @param string $type type of the message
- * @param string $subject subject of the message
- *
- * @return boolean success flag
- */
-
-function jabber_send_message($to, $body, $type='chat', $subject=null)
-{
- $conn = jabber_proxy();
- $conn->message($to, $body, $type, $subject);
- return true;
-}
-
-/**
- * sends a presence stanza on the Jabber network
- *
- * @param string $status current status, free-form string
- * @param string $show structured status value
- * @param string $to recipient of presence, null for general
- * @param string $type type of status message, related to $show
- * @param int $priority priority of the presence
- *
- * @return boolean success value
- */
-
-function jabber_send_presence($status, $show='available', $to=null,
- $type = 'available', $priority=null)
-{
- $conn = jabber_connect();
- if (!$conn) {
- return false;
- }
- $conn->presence($status, $show, $to, $type, $priority);
- return true;
-}
-
-/**
- * sends a confirmation request to a JID
- *
- * @param string $code confirmation code for confirmation URL
- * @param string $nickname nickname of confirming user
- * @param string $address JID to send confirmation to
- *
- * @return boolean success flag
- */
-
-function jabber_confirm_address($code, $nickname, $address)
-{
- $body = 'User "' . $nickname . '" on ' . common_config('site', 'name') . ' ' .
- 'has said that your Jabber ID belongs to them. ' .
- 'If that\'s true, you can confirm by clicking on this URL: ' .
- common_local_url('confirmaddress', array('code' => $code)) .
- ' . (If you cannot click it, copy-and-paste it into the ' .
- 'address bar of your browser). If that user isn\'t you, ' .
- 'or if you didn\'t request this confirmation, just ignore this message.';
-
- return jabber_send_message($address, $body);
-}
-
-/**
- * sends a "special" presence stanza on the Jabber network
- *
- * @param string $type Type of presence
- * @param string $to JID to send presence to
- * @param string $show show value for presence
- * @param string $status status value for presence
- *
- * @return boolean success flag
- *
- * @see jabber_send_presence()
- */
-
-function jabber_special_presence($type, $to=null, $show=null, $status=null)
-{
- // FIXME: why use this instead of jabber_send_presence()?
- $conn = jabber_connect();
-
- $to = htmlspecialchars($to);
- $status = htmlspecialchars($status);
-
- $out = "send($out);
-}
-
-/**
- * Queue broadcast of a notice to all subscribers and reply recipients
- *
- * This function will send a notice to all subscribers on the local server
- * who have Jabber addresses, and have Jabber notification enabled, and
- * have this subscription enabled for Jabber. It also sends the notice to
- * all recipients of @-replies who have Jabber addresses and Jabber notification
- * enabled. This is really the heart of Jabber distribution in StatusNet.
- *
- * @param Notice $notice The notice to broadcast
- *
- * @return boolean success flag
- */
-
-function jabber_broadcast_notice($notice)
-{
- if (!common_config('xmpp', 'enabled')) {
- return true;
- }
- $profile = Profile::staticGet($notice->profile_id);
-
- if (!$profile) {
- common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
- 'unknown profile ' . common_log_objstring($notice),
- __FILE__);
- return true; // not recoverable; discard.
- }
-
- $msg = jabber_format_notice($profile, $notice);
- $entry = jabber_format_entry($profile, $notice);
-
- $profile->free();
- unset($profile);
-
- $sent_to = array();
-
- $conn = jabber_proxy();
-
- $ni = $notice->whoGets();
-
- foreach ($ni as $user_id => $reason) {
- $user = User::staticGet($user_id);
- if (empty($user) ||
- empty($user->jabber) ||
- !$user->jabbernotify) {
- // either not a local user, or just not found
- continue;
- }
- switch ($reason) {
- case NOTICE_INBOX_SOURCE_REPLY:
- if (!$user->jabberreplies) {
- continue 2;
- }
- break;
- case NOTICE_INBOX_SOURCE_SUB:
- $sub = Subscription::pkeyGet(array('subscriber' => $user->id,
- 'subscribed' => $notice->profile_id));
- if (empty($sub) || !$sub->jabber) {
- continue 2;
- }
- break;
- case NOTICE_INBOX_SOURCE_GROUP:
- break;
- default:
- throw new Exception(sprintf(_("Unknown inbox source %d."), $reason));
- }
-
- common_log(LOG_INFO,
- 'Sending notice ' . $notice->id . ' to ' . $user->jabber,
- __FILE__);
- $conn->message($user->jabber, $msg, 'chat', null, $entry);
- }
-
- return true;
-}
-
-/**
- * Queue send of a notice to all public listeners
- *
- * For notices that are generated on the local system (by users), we can optionally
- * forward them to remote listeners by XMPP.
- *
- * @param Notice $notice notice to broadcast
- *
- * @return boolean success flag
- */
-
-function jabber_public_notice($notice)
-{
- // Now, users who want everything
-
- $public = common_config('xmpp', 'public');
-
- // FIXME PRIV don't send out private messages here
- // XXX: should we send out non-local messages if public,localonly
- // = false? I think not
-
- if ($public && $notice->is_local == Notice::LOCAL_PUBLIC) {
- $profile = Profile::staticGet($notice->profile_id);
-
- if (!$profile) {
- common_log(LOG_WARNING, 'Refusing to broadcast notice with ' .
- 'unknown profile ' . common_log_objstring($notice),
- __FILE__);
- return true; // not recoverable; discard.
- }
-
- $msg = jabber_format_notice($profile, $notice);
- $entry = jabber_format_entry($profile, $notice);
-
- $conn = jabber_proxy();
-
- foreach ($public as $address) {
- common_log(LOG_INFO,
- 'Sending notice ' . $notice->id .
- ' to public listener ' . $address,
- __FILE__);
- $conn->message($address, $msg, 'chat', null, $entry);
- }
- $profile->free();
- }
-
- return true;
-}
-
-/**
- * makes a plain-text formatted version of a notice, suitable for Jabber distribution
- *
- * @param Profile &$profile profile of the sending user
- * @param Notice &$notice notice being sent
- *
- * @return string plain-text version of the notice, with user nickname prefixed
- */
-
-function jabber_format_notice(&$profile, &$notice)
-{
- return $profile->nickname . ': ' . $notice->content . ' [' . $notice->id . ']';
-}
diff --git a/lib/joinform.php b/lib/joinform.php
index aa8bc20e24..0918133a55 100644
--- a/lib/joinform.php
+++ b/lib/joinform.php
@@ -88,7 +88,7 @@ class JoinForm extends Form
function formClass()
{
- return 'form_group_join';
+ return 'form_group_join ajax';
}
/**
diff --git a/lib/leaveform.php b/lib/leaveform.php
index 5469b5704c..34671f5f8d 100644
--- a/lib/leaveform.php
+++ b/lib/leaveform.php
@@ -88,7 +88,7 @@ class LeaveForm extends Form
function formClass()
{
- return 'form_group_leave';
+ return 'form_group_leave ajax';
}
/**
diff --git a/lib/logingroupnav.php b/lib/logingroupnav.php
index a309e7320f..5d1b52f795 100644
--- a/lib/logingroupnav.php
+++ b/lib/logingroupnav.php
@@ -44,21 +44,8 @@ require_once INSTALLDIR.'/lib/widget.php';
*
* @see Widget
*/
-class LoginGroupNav extends Widget
+class LoginGroupNav extends Menu
{
- var $action = null;
-
- /**
- * Construction
- *
- * @param Action $action current action, used for output
- */
- function __construct($action=null)
- {
- parent::__construct($action);
- $this->action = $action;
- }
-
/**
* Show the menu
*
@@ -79,7 +66,8 @@ class LoginGroupNav extends Widget
_('Login with a username and password'),
$action_name === 'login');
- if (!(common_config('site','closed') || common_config('site','inviteonly'))) {
+ if (!common_logged_in() &&
+ !(common_config('site','closed') || common_config('site','inviteonly'))) {
$this->action->menuItem(common_local_url('register'),
// TRANS: Menu item for registering with the StatusNet site.
_m('MENU','Register'),
diff --git a/lib/mailbox.php b/lib/mailbox.php
index 7faeb7dba3..e9e4f78c6b 100644
--- a/lib/mailbox.php
+++ b/lib/mailbox.php
@@ -92,12 +92,6 @@ class MailboxAction extends CurrentUserDesignAction
$this->showPage();
}
- function showLocalNav()
- {
- $nav = new PersonalGroupNav($this);
- $nav->show();
- }
-
function showNoticeForm()
{
$message_form = new MessageForm($this);
@@ -168,4 +162,10 @@ class MailboxAction extends CurrentUserDesignAction
{
return true;
}
+
+ function showObjectNav()
+ {
+ $mm = new MailboxMenu($this);
+ $mm->show();
+ }
}
diff --git a/lib/mailboxmenu.php b/lib/mailboxmenu.php
new file mode 100644
index 0000000000..d2d3607dce
--- /dev/null
+++ b/lib/mailboxmenu.php
@@ -0,0 +1,70 @@
+.
+ *
+ * @category Cache
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Menu of existing mailboxes
+ *
+ * @category General
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class MailboxMenu extends Menu
+{
+ function show()
+ {
+ $cur = common_current_user();
+ $nickname = $cur->nickname;
+
+ $this->out->elementStart('ul', array('class' => 'nav'));
+
+ $this->item('inbox',
+ array('nickname' => $nickname),
+ _('Inbox'),
+ _('Your incoming messages'));
+
+ $this->item('outbox',
+ array('nickname' => $nickname),
+ _('Outbox'),
+ _('Your sent messages'));
+
+ $this->out->elementEnd('ul');
+ }
+
+}
diff --git a/lib/menu.php b/lib/menu.php
new file mode 100644
index 0000000000..2713b44d50
--- /dev/null
+++ b/lib/menu.php
@@ -0,0 +1,92 @@
+.
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Superclass for menus
+ *
+ * @category General
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class Menu extends Widget
+{
+ var $action = null;
+ var $actionName = null;
+ /**
+ * Construction
+ *
+ * @param Action $action current action, used for output
+ */
+ function __construct($action=null)
+ {
+ parent::__construct($action);
+
+ $this->action = $action;
+ $this->actionName = $action->trimmed('action');
+ }
+
+ function item($actionName, $args, $label, $description, $id=null)
+ {
+ if (empty($id)) {
+ $id = $this->menuItemID($actionName);
+ }
+
+ $url = common_local_url($actionName, $args);
+
+ $this->out->menuItem($url,
+ $label,
+ $description,
+ $actionName == $this->actionName,
+ $id);
+ }
+
+ function menuItemID($actionName)
+ {
+ return sprintf('nav_%s', $actionName);
+ }
+
+ function submenu($label, $menu)
+ {
+ $this->action->elementStart('li');
+ $this->action->element('h3', null, $label);
+ $menu->show();
+ $this->action->elementEnd('li');
+ }
+}
diff --git a/lib/messageform.php b/lib/messageform.php
index 9d3f955a81..733e83cd15 100644
--- a/lib/messageform.php
+++ b/lib/messageform.php
@@ -96,7 +96,7 @@ class MessageForm extends Form
function formClass()
{
- return 'form_notice';
+ return 'form_notice ajax-notice';
}
/**
@@ -153,7 +153,7 @@ class MessageForm extends Form
$this->out->dropdown('to', _('To'), $mutual, null, false,
($this->to) ? $this->to->id : null);
- $this->out->element('textarea', array('id' => 'notice_data-text',
+ $this->out->element('textarea', array('class' => 'notice_data-text',
'cols' => 35,
'rows' => 4,
'name' => 'content'),
@@ -162,11 +162,9 @@ class MessageForm extends Form
$contentLimit = Message::maxContent();
if ($contentLimit > 0) {
- $this->out->elementStart('dl', 'form_note');
- $this->out->element('dt', null, _('Available characters'));
- $this->out->element('dd', array('id' => 'notice_text-count'),
+ $this->out->element('span',
+ array('class' => 'count'),
$contentLimit);
- $this->out->elementEnd('dl');
}
}
diff --git a/lib/microappplugin.php b/lib/microappplugin.php
new file mode 100644
index 0000000000..ab6d565157
--- /dev/null
+++ b/lib/microappplugin.php
@@ -0,0 +1,535 @@
+.
+ *
+ * @category Microapp
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Superclass for microapp plugins
+ *
+ * This class lets you define micro-applications with different kinds of activities.
+ *
+ * The applications work more-or-less like other
+ *
+ * @category Microapp
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+abstract class MicroAppPlugin extends Plugin
+{
+ /**
+ * Returns a localized string which represents this micro-app,
+ * to be shown to users selecting what type of post to make.
+ * This is paired with the key string in $this->tag().
+ *
+ * All micro-app classes must override this method.
+ *
+ * @return string
+ */
+ abstract function appTitle();
+
+ /**
+ * Returns a key string which represents this micro-app in HTML
+ * ids etc, as when offering selection of what type of post to make.
+ * This is paired with the user-visible localizable $this->appTitle().
+ *
+ * All micro-app classes must override this method.
+ */
+ abstract function tag();
+
+ /**
+ * Return a list of ActivityStreams object type URIs
+ * which this micro-app handles. Default implementations
+ * of the base class will use this list to check if a
+ * given ActivityStreams object belongs to us, via
+ * $this->isMyNotice() or $this->isMyActivity.
+ *
+ * All micro-app classes must override this method.
+ *
+ * @fixme can we confirm that these types are the same
+ * for Atom and JSON streams? Any limitations or issues?
+ *
+ * @return array of strings
+ */
+ abstract function types();
+
+ /**
+ * Given a parsed ActivityStreams activity, your plugin
+ * gets to figure out how to actually save it into a notice
+ * and any additional data structures you require.
+ *
+ * This will handle things received via AtomPub, OStatus
+ * (PuSH and Salmon transports), or ActivityStreams-based
+ * backup/restore of account data.
+ *
+ * You should be able to accept as input the output from your
+ * $this->activityObjectFromNotice(). Where applicable, try to
+ * use existing ActivityStreams structures and object types,
+ * and be liberal in accepting input from what might be other
+ * compatible apps.
+ *
+ * All micro-app classes must override this method.
+ *
+ * @fixme are there any standard options?
+ *
+ * @param Activity $activity
+ * @param Profile $actor
+ * @param array $options=array()
+ *
+ * @return Notice the resulting notice
+ */
+ abstract function saveNoticeFromActivity($activity, $actor, $options=array());
+
+ /**
+ * Given an existing Notice object, your plugin gets to
+ * figure out how to arrange it into an ActivityStreams
+ * object.
+ *
+ * This will be how your specialized notice gets output in
+ * Atom feeds and JSON-based ActivityStreams output, including
+ * account backup/restore and OStatus (PuSH and Salmon transports).
+ *
+ * You should be able to round-trip data from this format back
+ * through $this->saveNoticeFromActivity(). Where applicable, try
+ * to use existing ActivityStreams structures and object types,
+ * and consider interop with other compatible apps.
+ *
+ * All micro-app classes must override this method.
+ *
+ * @fixme this outputs an ActivityObject, not an Activity. Any compat issues?
+ *
+ * @param Notice $notice
+ *
+ * @return ActivityObject
+ */
+ abstract function activityObjectFromNotice($notice);
+
+ /**
+ * Custom HTML output for your special notice; called when a
+ * matching notice turns up in a NoticeListItem.
+ *
+ * All micro-app classes must override this method.
+ *
+ * @param Notice $notice
+ * @param HTMLOutputter $out
+ *
+ * @fixme WARNING WARNING WARNING base plugin stuff below tries to close
+ * a div that this function opens in the BookmarkPlugin child class.
+ * This is probably wrong.
+ */
+ abstract function showNotice($notice, $out);
+
+ /**
+ * When building the primary notice form, we'll fetch also some
+ * alternate forms for specialized types -- that's you!
+ *
+ * Return a custom Widget or Form object for the given output
+ * object, and it'll be included in the HTML output. Beware that
+ * your form may be initially hidden.
+ *
+ * All micro-app classes must override this method.
+ *
+ * @param HTMLOutputter $out
+ * @return Widget
+ */
+ abstract function entryForm($out);
+
+ /**
+ * When a notice is deleted, you'll be called here for a chance
+ * to clean up any related resources.
+ *
+ * All micro-app classes must override this method.
+ *
+ * @param Notice $notice
+ */
+ abstract function deleteRelated($notice);
+
+ /**
+ * Check if a given notice object should be handled by this micro-app
+ * plugin.
+ *
+ * The default implementation checks against the activity type list
+ * returned by $this->types(). You can override this method to expand
+ * your checks.
+ *
+ * @param Notice $notice
+ * @return boolean
+ */
+ function isMyNotice($notice) {
+ $types = $this->types();
+ return in_array($notice->object_type, $types);
+ }
+
+ /**
+ * Check if a given ActivityStreams activity should be handled by this
+ * micro-app plugin.
+ *
+ * The default implementation checks against the activity type list
+ * returned by $this->types(), and requires that exactly one matching
+ * object be present. You can override this method to expand
+ * your checks or to compare the activity's verb, etc.
+ *
+ * @param Activity $activity
+ * @return boolean
+ */
+ function isMyActivity($activity) {
+ $types = $this->types();
+ return (count($activity->objects) == 1 &&
+ in_array($activity->objects[0]->type, $types));
+ }
+
+ /**
+ * Called when generating Atom XML ActivityStreams output from an
+ * ActivityObject belonging to this plugin. Gives the plugin
+ * a chance to add custom output.
+ *
+ * Note that you can only add output of additional XML elements,
+ * not change existing stuff here.
+ *
+ * If output is already handled by the base Activity classes,
+ * you can leave this base implementation as a no-op.
+ *
+ * @param ActivityObject $obj
+ * @param XMLOutputter $out to add elements at end of object
+ */
+ function activityObjectOutputAtom(ActivityObject $obj, XMLOutputter $out)
+ {
+ // default is a no-op
+ }
+
+ /**
+ * Called when generating JSON ActivityStreams output from an
+ * ActivityObject belonging to this plugin. Gives the plugin
+ * a chance to add custom output.
+ *
+ * Modify the array contents to your heart's content, and it'll
+ * all get serialized out as JSON.
+ *
+ * If output is already handled by the base Activity classes,
+ * you can leave this base implementation as a no-op.
+ *
+ * @param ActivityObject $obj
+ * @param array &$out JSON-targeted array which can be modified
+ */
+ public function activityObjectOutputJson(ActivityObject $obj, array &$out)
+ {
+ // default is a no-op
+ }
+
+ /**
+ * When a notice is deleted, delete the related objects
+ * by calling the overridable $this->deleteRelated().
+ *
+ * @param Notice $notice Notice being deleted
+ *
+ * @return boolean hook value
+ */
+
+ function onNoticeDeleteRelated($notice)
+ {
+ if ($this->isMyNotice($notice)) {
+ $this->deleteRelated($notice);
+ }
+
+ return true;
+ }
+
+ /**
+ * Output the HTML for this kind of object in a list
+ *
+ * @param NoticeListItem $nli The list item being shown.
+ *
+ * @return boolean hook value
+ *
+ * @fixme WARNING WARNING WARNING this closes a 'div' that is implicitly opened in BookmarkPlugin's showNotice implementation
+ */
+
+ function onStartShowNoticeItem($nli)
+ {
+ if (!$this->isMyNotice($nli->notice)) {
+ return true;
+ }
+
+ $out = $nli->out;
+ $notice = $nli->notice;
+
+ $this->showNotice($notice, $out);
+
+ $nli->showNoticeLink();
+ $nli->showNoticeSource();
+ $nli->showNoticeLocation();
+ $nli->showContext();
+ $nli->showRepeat();
+
+ $out->elementEnd('div');
+
+ $nli->showNoticeOptions();
+
+ return false;
+ }
+
+ /**
+ * Render a notice as one of our objects
+ *
+ * @param Notice $notice Notice to render
+ * @param ActivityObject &$object Empty object to fill
+ *
+ * @return boolean hook value
+ */
+
+ function onStartActivityObjectFromNotice($notice, &$object)
+ {
+ if ($this->isMyNotice($notice)) {
+ $object = $this->activityObjectFromNotice($notice);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle a posted object from PuSH
+ *
+ * @param Activity $activity activity to handle
+ * @param Ostatus_profile $oprofile Profile for the feed
+ *
+ * @return boolean hook value
+ */
+
+ function onStartHandleFeedEntryWithProfile($activity, $oprofile)
+ {
+ if ($this->isMyActivity($activity)) {
+
+ $actor = $oprofile->checkAuthorship($activity);
+
+ if (empty($actor)) {
+ throw new ClientException(_('Can\'t get author for activity.'));
+ }
+
+ $object = $activity->objects[0];
+
+ $options = array('uri' => $object->id,
+ 'url' => $object->link,
+ 'is_local' => Notice::REMOTE_OMB,
+ 'source' => 'ostatus');
+
+ // $actor is an ostatus_profile
+ $this->saveNoticeFromActivity($activity, $actor->localProfile(), $options);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle a posted object from Salmon
+ *
+ * @param Activity $activity activity to handle
+ * @param mixed $target user or group targeted
+ *
+ * @return boolean hook value
+ */
+
+ function onStartHandleSalmonTarget($activity, $target)
+ {
+ if ($this->isMyActivity($activity)) {
+
+ $this->log(LOG_INFO, "Checking {$activity->id} as a valid Salmon slap.");
+
+ if ($target instanceof User_group) {
+ $uri = $target->getUri();
+ if (!in_array($uri, $activity->context->attention)) {
+ throw new ClientException(_("Bookmark not posted ".
+ "to this group."));
+ }
+ } else if ($target instanceof User) {
+ $uri = $target->uri;
+ $original = null;
+ if (!empty($activity->context->replyToID)) {
+ $original = Notice::staticGet('uri',
+ $activity->context->replyToID);
+ }
+ if (!in_array($uri, $activity->context->attention) &&
+ (empty($original) ||
+ $original->profile_id != $target->id)) {
+ throw new ClientException(_("Object not posted ".
+ "to this user."));
+ }
+ } else {
+ throw new ServerException(_("Don't know how to handle ".
+ "this kind of target."));
+ }
+
+ $actor = Ostatus_profile::ensureActivityObjectProfile($activity->actor);
+
+ $object = $activity->objects[0];
+
+ $options = array('uri' => $object->id,
+ 'url' => $object->link,
+ 'is_local' => Notice::REMOTE_OMB,
+ 'source' => 'ostatus');
+
+ // $actor is an ostatus_profile
+ $this->saveNoticeFromActivity($activity, $actor->localProfile(), $options);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle object posted via AtomPub
+ *
+ * @param Activity &$activity Activity that was posted
+ * @param User $user User that posted it
+ * @param Notice &$notice Resulting notice
+ *
+ * @return boolean hook value
+ */
+
+ function onStartAtomPubNewActivity(&$activity, $user, &$notice)
+ {
+ if ($this->isMyActivity($activity)) {
+
+ $options = array('source' => 'atompub');
+
+ // $user->getProfile() is a Profile
+ $this->saveNoticeFromActivity($activity,
+ $user->getProfile(),
+ $options);
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Handle object imported from a backup file
+ *
+ * @param User $user User to import for
+ * @param ActivityObject $author Original author per import file
+ * @param Activity $activity Activity to import
+ * @param boolean $trusted Is this a trusted user?
+ * @param boolean &$done Is this done (success or unrecoverable error)
+ *
+ * @return boolean hook value
+ */
+
+ function onStartImportActivity($user, $author, $activity, $trusted, &$done)
+ {
+ if ($this->isMyActivity($activity)) {
+
+ $obj = $activity->objects[0];
+
+ $options = array('uri' => $object->id,
+ 'url' => $object->link,
+ 'source' => 'restore');
+
+ // $user->getProfile() is a Profile
+ $saved = $this->saveNoticeFromActivity($activity,
+ $user->getProfile(),
+ $options);
+
+ if (!empty($saved)) {
+ $done = true;
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Event handler gives the plugin a chance to add custom
+ * Atom XML ActivityStreams output from a previously filled-out
+ * ActivityObject.
+ *
+ * The atomOutput method is called if it's one of
+ * our matching types.
+ *
+ * @param ActivityObject $obj
+ * @param XMLOutputter $out to add elements at end of object
+ * @return boolean hook return value
+ */
+ function onEndActivityObjectOutputAtom(ActivityObject $obj, XMLOutputter $out)
+ {
+ if (in_array($obj->type, $this->types())) {
+ $this->activityObjectOutputAtom($obj, $out);
+ }
+ return true;
+ }
+
+ /**
+ * Event handler gives the plugin a chance to add custom
+ * JSON ActivityStreams output from a previously filled-out
+ * ActivityObject.
+ *
+ * The activityObjectOutputJson method is called if it's one of
+ * our matching types.
+ *
+ * @param ActivityObject $obj
+ * @param array &$out JSON-targeted array which can be modified
+ * @return boolean hook return value
+ */
+ function onEndActivityObjectOutputJson(ActivityObject $obj, array &$out)
+ {
+ if (in_array($obj->type, $this->types())) {
+ $this->activityObjectOutputJson($obj, &$out);
+ }
+ return true;
+ }
+
+ function onStartShowEntryForms(&$tabs)
+ {
+ $tabs[$this->tag()] = $this->appTitle();
+ return true;
+ }
+
+ function onStartMakeEntryForm($tag, $out, &$form)
+ {
+ if ($tag == $this->tag()) {
+ $form = $this->entryForm($out);
+ return false;
+ }
+
+ return true;
+ }
+}
diff --git a/lib/mysqlschema.php b/lib/mysqlschema.php
index f9552c1dcd..c3d3501c74 100644
--- a/lib/mysqlschema.php
+++ b/lib/mysqlschema.php
@@ -72,72 +72,127 @@ class MysqlSchema extends Schema
*
* Throws an exception if the table is not found.
*
- * @param string $name Name of the table to get
+ * @param string $table Name of the table to get
*
* @return TableDef tabledef for that table.
* @throws SchemaTableMissingException
*/
- public function getTableDef($name)
+ public function getTableDef($table)
{
- $query = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS " .
- "WHERE TABLE_SCHEMA='%s' AND TABLE_NAME='%s'";
- $schema = $this->conn->dsn['database'];
- $sql = sprintf($query, $schema, $name);
- $res = $this->conn->query($sql);
+ $def = array();
+ $hasKeys = false;
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
- if ($res->numRows() == 0) {
- $res->free();
- throw new SchemaTableMissingException("No such table: $name");
+ // Pull column data from INFORMATION_SCHEMA
+ $columns = $this->fetchMetaInfo($table, 'COLUMNS', 'ORDINAL_POSITION');
+ if (count($columns) == 0) {
+ throw new SchemaTableMissingException("No such table: $table");
}
- $td = new TableDef();
+ foreach ($columns as $row) {
- $td->name = $name;
- $td->columns = array();
+ $name = $row['COLUMN_NAME'];
+ $field = array();
- $row = array();
+ // warning -- 'unsigned' attr on numbers isn't given in DATA_TYPE and friends.
+ // It is stuck in on COLUMN_TYPE though (eg 'bigint(20) unsigned')
+ $field['type'] = $type = $row['DATA_TYPE'];
- while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
-
- $cd = new ColumnDef();
-
- $cd->name = $row['COLUMN_NAME'];
-
- $packed = $row['COLUMN_TYPE'];
-
- if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) {
- $cd->type = $match[1];
- $cd->size = $match[2];
- } else {
- $cd->type = $packed;
+ if ($type == 'char' || $type == 'varchar') {
+ if ($row['CHARACTER_MAXIMUM_LENGTH'] !== null) {
+ $field['length'] = intval($row['CHARACTER_MAXIMUM_LENGTH']);
+ }
+ }
+ if ($type == 'decimal') {
+ // Other int types may report these values, but they're irrelevant.
+ // Just ignore them!
+ if ($row['NUMERIC_PRECISION'] !== null) {
+ $field['precision'] = intval($row['NUMERIC_PRECISION']);
+ }
+ if ($row['NUMERIC_SCALE'] !== null) {
+ $field['scale'] = intval($row['NUMERIC_SCALE']);
+ }
+ }
+ if ($row['IS_NULLABLE'] == 'NO') {
+ $field['not null'] = true;
+ }
+ if ($row['COLUMN_DEFAULT'] !== null) {
+ // Hack for timestamp cols
+ if ($type == 'timestamp' && $row['COLUMN_DEFAULT'] == 'CURRENT_TIMESTAMP') {
+ // skip
+ } else {
+ $field['default'] = $row['COLUMN_DEFAULT'];
+ if ($this->isNumericType($type)) {
+ $field['default'] = intval($field['default']);
+ }
+ }
+ }
+ if ($row['COLUMN_KEY'] !== null) {
+ // We'll need to look up key info...
+ $hasKeys = true;
+ }
+ if ($row['COLUMN_COMMENT'] !== null && $row['COLUMN_COMMENT'] != '') {
+ $field['description'] = $row['COLUMN_COMMENT'];
}
- $cd->nullable = ($row['IS_NULLABLE'] == 'YES') ? true : false;
- $cd->key = $row['COLUMN_KEY'];
- $cd->default = $row['COLUMN_DEFAULT'];
- $cd->extra = $row['EXTRA'];
-
- // Autoincrement is stuck into the extra column.
- // Pull it out so we don't accidentally mod it every time...
- $extra = preg_replace('/(^|\s)auto_increment(\s|$)/i', '$1$2', $cd->extra);
- if ($extra != $cd->extra) {
- $cd->extra = trim($extra);
- $cd->auto_increment = true;
+ $extra = $row['EXTRA'];
+ if ($extra) {
+ if (preg_match('/(^|\s)auto_increment(\s|$)/i', $extra)) {
+ $field['auto_increment'] = true;
+ }
+ // $row['EXTRA'] may contain 'on update CURRENT_TIMESTAMP'
+ // ^ ...... how to specify?
}
- // mysql extensions -- not (yet) used by base class
- $cd->charset = $row['CHARACTER_SET_NAME'];
- $cd->collate = $row['COLLATION_NAME'];
+ if ($row['CHARACTER_SET_NAME'] !== null) {
+ // @fixme check against defaults?
+ //$def['charset'] = $row['CHARACTER_SET_NAME'];
+ //$def['collate'] = $row['COLLATION_NAME'];
+ }
- $td->columns[] = $cd;
+ $def['fields'][$name] = $field;
}
- $res->free();
- return $td;
+ if ($hasKeys) {
+ // INFORMATION_SCHEMA's CONSTRAINTS and KEY_COLUMN_USAGE tables give
+ // good info on primary and unique keys but don't list ANY info on
+ // multi-value keys, which is lame-o. Sigh.
+ //
+ // Let's go old school and use SHOW INDEX :D
+ //
+ $keyInfo = $this->fetchIndexInfo($table);
+ $keys = array();
+ foreach ($keyInfo as $row) {
+ $name = $row['Key_name'];
+ $column = $row['Column_name'];
+
+ if (!isset($keys[$name])) {
+ $keys[$name] = array();
+ }
+ $keys[$name][] = $column;
+
+ if ($name == 'PRIMARY') {
+ $type = 'primary key';
+ } else if ($row['Non_unique'] == 0) {
+ $type = 'unique keys';
+ } else if ($row['Index_type'] == 'FULLTEXT') {
+ $type = 'fulltext indexes';
+ } else {
+ $type = 'indexes';
+ }
+ $keyTypes[$name] = $type;
+ }
+
+ foreach ($keyTypes as $name => $type) {
+ if ($type == 'primary key') {
+ // there can be only one
+ $def[$type] = $keys[$name];
+ } else {
+ $def[$type][$name] = $keys[$name];
+ }
+ }
+ }
+ return $def;
}
/**
@@ -150,127 +205,81 @@ class MysqlSchema extends Schema
function getTableProperties($table, $props)
{
- $query = "SELECT %s FROM INFORMATION_SCHEMA.TABLES " .
- "WHERE TABLE_SCHEMA='%s' AND TABLE_NAME='%s'";
- $schema = $this->conn->dsn['database'];
- $sql = sprintf($query, implode(',', $props), $schema, $table);
- $res = $this->conn->query($sql);
-
- $row = array();
- $ok = $res->fetchInto($row, DB_FETCHMODE_ASSOC);
- $res->free();
-
- if ($ok) {
- return $row;
+ $data = $this->fetchMetaInfo($table, 'TABLES');
+ if ($data) {
+ return $data[0];
} else {
throw new SchemaTableMissingException("No such table: $table");
}
}
/**
- * Gets a ColumnDef object for a single column.
+ * Pull some INFORMATION.SCHEMA data for the given table.
*
- * Throws an exception if the table is not found.
- *
- * @param string $table name of the table
- * @param string $column name of the column
- *
- * @return ColumnDef definition of the column or null
- * if not found.
+ * @param string $table
+ * @return array of arrays
*/
-
- public function getColumnDef($table, $column)
+ function fetchMetaInfo($table, $infoTable, $orderBy=null)
{
- $td = $this->getTableDef($table);
-
- foreach ($td->columns as $cd) {
- if ($cd->name == $column) {
- return $cd;
- }
+ $query = "SELECT * FROM INFORMATION_SCHEMA.%s " .
+ "WHERE TABLE_SCHEMA='%s' AND TABLE_NAME='%s'";
+ $schema = $this->conn->dsn['database'];
+ $sql = sprintf($query, $infoTable, $schema, $table);
+ if ($orderBy) {
+ $sql .= ' ORDER BY ' . $orderBy;
}
-
- return null;
+ return $this->fetchQueryData($sql);
}
/**
- * Creates a table with the given names and columns.
+ * Pull 'SHOW INDEX' data for the given table.
*
- * @param string $name Name of the table
- * @param array $columns Array of ColumnDef objects
- * for new table.
- *
- * @return boolean success flag
+ * @param string $table
+ * @return array of arrays
*/
-
- public function createTable($name, $columns)
+ function fetchIndexInfo($table)
{
- $uniques = array();
- $primary = array();
- $indices = array();
-
- $sql = "CREATE TABLE $name (\n";
-
- for ($i = 0; $i < count($columns); $i++) {
-
- $cd =& $columns[$i];
-
- if ($i > 0) {
- $sql .= ",\n";
- }
-
- $sql .= $this->_columnSql($cd);
- }
-
- $idx = $this->_indexList($columns);
-
- if ($idx['primary']) {
- $sql .= ",\nconstraint primary key (" . implode(',', $idx['primary']) . ")";
- }
-
- foreach ($idx['uniques'] as $u) {
- $key = $this->_uniqueKey($name, $u);
- $sql .= ",\nunique index $key ($u)";
- }
-
- foreach ($idx['indices'] as $i) {
- $key = $this->_key($name, $i);
- $sql .= ",\nindex $key ($i)";
- }
-
- $sql .= ") ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin; ";
-
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
+ $query = "SHOW INDEX FROM `%s`";
+ $sql = sprintf($query, $table);
+ return $this->fetchQueryData($sql);
}
/**
- * Look over a list of column definitions and list up which
- * indices will be present
+ * Append an SQL statement with an index definition for a full-text search
+ * index over one or more columns on a table.
+ *
+ * @param array $statements
+ * @param string $table
+ * @param string $name
+ * @param array $def
*/
- private function _indexList(array $columns)
+ function appendCreateFulltextIndex(array &$statements, $table, $name, array $def)
{
- $list = array('uniques' => array(),
- 'primary' => array(),
- 'indices' => array());
- foreach ($columns as $cd) {
- switch ($cd->key) {
- case 'UNI':
- $list['uniques'][] = $cd->name;
- break;
- case 'PRI':
- $list['primary'][] = $cd->name;
- break;
- case 'MUL':
- $list['indices'][] = $cd->name;
- break;
- }
+ $statements[] = "CREATE FULLTEXT INDEX $name ON $table " . $this->buildIndexList($def);
+ }
+
+ /**
+ * Close out a 'create table' SQL statement.
+ *
+ * @param string $name
+ * @param array $def
+ * @return string;
+ *
+ * @fixme ENGINE may need to be set differently in some cases,
+ * such as to support fulltext index.
+ */
+ function endCreateTable($name, array $def)
+ {
+ $engine = $this->preferredEngine($def);
+ return ") ENGINE=$engine CHARACTER SET utf8 COLLATE utf8_bin";
+ }
+
+ function preferredEngine($def)
+ {
+ if (!empty($def['fulltext indexes'])) {
+ return 'MyISAM';
}
- return $list;
+ return 'InnoDB';
}
/**
@@ -289,344 +298,46 @@ class MysqlSchema extends Schema
return "{$tableName}_{$columnName}_idx";
}
+
/**
- * Drops a table from the schema
+ * MySQL doesn't take 'DROP CONSTRAINT', need to treat unique keys as
+ * if they were indexes here.
*
- * Throws an exception if the table is not found.
- *
- * @param string $name Name of the table to drop
- *
- * @return boolean success flag
+ * @param array $phrase
+ * @param $keyName MySQL
*/
-
- public function dropTable($name)
+ function appendAlterDropUnique(array &$phrase, $keyName)
{
- $res = $this->conn->query("DROP TABLE $name");
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
+ $phrase[] = 'DROP INDEX ' . $keyName;
}
/**
- * Adds an index to a table.
- *
- * If no name is provided, a name will be made up based
- * on the table name and column names.
- *
- * Throws an exception on database error, esp. if the table
- * does not exist.
- *
- * @param string $table Name of the table
- * @param array $columnNames Name of columns to index
- * @param string $name (Optional) name of the index
- *
- * @return boolean success flag
+ * Throw some table metadata onto the ALTER TABLE if we have a mismatch
+ * in expected type, collation.
*/
-
- public function createIndex($table, $columnNames, $name=null)
+ function appendAlterExtras(array &$phrase, $tableName, array $def)
{
- if (!is_array($columnNames)) {
- $columnNames = array($columnNames);
- }
-
- if (empty($name)) {
- $name = "{$table}_".implode("_", $columnNames)."_idx";
- }
-
- $res = $this->conn->query("ALTER TABLE $table ".
- "ADD INDEX $name (".
- implode(",", $columnNames).")");
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Drops a named index from a table.
- *
- * @param string $table name of the table the index is on.
- * @param string $name name of the index
- *
- * @return boolean success flag
- */
-
- public function dropIndex($table, $name)
- {
- $res = $this->conn->query("ALTER TABLE $table DROP INDEX $name");
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Adds a column to a table
- *
- * @param string $table name of the table
- * @param ColumnDef $columndef Definition of the new
- * column.
- *
- * @return boolean success flag
- */
-
- public function addColumn($table, $columndef)
- {
- $sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef);
-
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Modifies a column in the schema.
- *
- * The name must match an existing column and table.
- *
- * @param string $table name of the table
- * @param ColumnDef $columndef new definition of the column.
- *
- * @return boolean success flag
- */
-
- public function modifyColumn($table, $columndef)
- {
- $sql = "ALTER TABLE $table MODIFY COLUMN " .
- $this->_columnSql($columndef);
-
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Drops a column from a table
- *
- * The name must match an existing column.
- *
- * @param string $table name of the table
- * @param string $columnName name of the column to drop
- *
- * @return boolean success flag
- */
-
- public function dropColumn($table, $columnName)
- {
- $sql = "ALTER TABLE $table DROP COLUMN $columnName";
-
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Ensures that a table exists with the given
- * name and the given column definitions.
- *
- * If the table does not yet exist, it will
- * create the table. If it does exist, it will
- * alter the table to match the column definitions.
- *
- * @param string $tableName name of the table
- * @param array $columns array of ColumnDef
- * objects for the table
- *
- * @return boolean success flag
- */
-
- public function ensureTable($tableName, $columns)
- {
- // XXX: DB engine portability -> toilet
-
- try {
- $td = $this->getTableDef($tableName);
- } catch (SchemaTableMissingException $e) {
- return $this->createTable($tableName, $columns);
- }
-
- $cur = $this->_names($td->columns);
- $new = $this->_names($columns);
-
- $dropIndex = array();
- $toadd = array_diff($new, $cur);
- $todrop = array_diff($cur, $new);
- $same = array_intersect($new, $cur);
- $tomod = array();
- $addIndex = array();
- $tableProps = array();
-
- foreach ($same as $m) {
- $curCol = $this->_byName($td->columns, $m);
- $newCol = $this->_byName($columns, $m);
-
- if (!$newCol->equals($curCol)) {
- $tomod[] = $newCol->name;
- continue;
- }
-
- // Earlier versions may have accidentally left tables at default
- // charsets which might be latin1 or other freakish things.
- if ($this->_isString($curCol)) {
- if ($curCol->charset != 'utf8') {
- $tomod[] = $newCol->name;
- continue;
- }
- }
- }
-
- // Find any indices we have to change...
- $curIdx = $this->_indexList($td->columns);
- $newIdx = $this->_indexList($columns);
-
- if ($curIdx['primary'] != $newIdx['primary']) {
- if ($curIdx['primary']) {
- $dropIndex[] = 'drop primary key';
- }
- if ($newIdx['primary']) {
- $keys = implode(',', $newIdx['primary']);
- $addIndex[] = "add constraint primary key ($keys)";
- }
- }
-
- $dropUnique = array_diff($curIdx['uniques'], $newIdx['uniques']);
- $addUnique = array_diff($newIdx['uniques'], $curIdx['uniques']);
- foreach ($dropUnique as $columnName) {
- $dropIndex[] = 'drop key ' . $this->_uniqueKey($tableName, $columnName);
- }
- foreach ($addUnique as $columnName) {
- $addIndex[] = 'add constraint unique key ' . $this->_uniqueKey($tableName, $columnName) . " ($columnName)";;
- }
-
- $dropMultiple = array_diff($curIdx['indices'], $newIdx['indices']);
- $addMultiple = array_diff($newIdx['indices'], $curIdx['indices']);
- foreach ($dropMultiple as $columnName) {
- $dropIndex[] = 'drop key ' . $this->_key($tableName, $columnName);
- }
- foreach ($addMultiple as $columnName) {
- $addIndex[] = 'add key ' . $this->_key($tableName, $columnName) . " ($columnName)";
- }
-
// Check for table properties: make sure we're using a sane
// engine type and charset/collation.
// @fixme make the default engine configurable?
$oldProps = $this->getTableProperties($tableName, array('ENGINE', 'TABLE_COLLATION'));
- if (strtolower($oldProps['ENGINE']) != 'innodb') {
- $tableProps['ENGINE'] = 'InnoDB';
+ $engine = $this->preferredEngine($def);
+ if (strtolower($oldProps['ENGINE']) != strtolower($engine)) {
+ $phrase[] = "ENGINE=$engine";
}
if (strtolower($oldProps['TABLE_COLLATION']) != 'utf8_bin') {
- $tableProps['DEFAULT CHARSET'] = 'utf8';
- $tableProps['COLLATE'] = 'utf8_bin';
+ $phrase[] = 'DEFAULT CHARSET=utf8';
+ $phrase[] = 'COLLATE=utf8_bin';
}
-
- if (count($dropIndex) + count($toadd) + count($todrop) + count($tomod) + count($addIndex) + count($tableProps) == 0) {
- // nothing to do
- return true;
- }
-
- // For efficiency, we want this all in one
- // query, instead of using our methods.
-
- $phrase = array();
-
- foreach ($dropIndex as $indexSql) {
- $phrase[] = $indexSql;
- }
-
- foreach ($toadd as $columnName) {
- $cd = $this->_byName($columns, $columnName);
-
- $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
- }
-
- foreach ($todrop as $columnName) {
- $phrase[] = 'DROP COLUMN ' . $columnName;
- }
-
- foreach ($tomod as $columnName) {
- $cd = $this->_byName($columns, $columnName);
-
- $phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd);
- }
-
- foreach ($addIndex as $indexSql) {
- $phrase[] = $indexSql;
- }
-
- foreach ($tableProps as $key => $val) {
- $phrase[] = "$key=$val";
- }
-
- $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
-
- common_log(LOG_DEBUG, __METHOD__ . ': ' . $sql);
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
}
/**
- * Returns the array of names from an array of
- * ColumnDef objects.
- *
- * @param array $cds array of ColumnDef objects
- *
- * @return array strings for name values
+ * Is this column a string type?
*/
-
- private function _names($cds)
+ private function _isString(array $cd)
{
- $names = array();
-
- foreach ($cds as $cd) {
- $names[] = $cd->name;
- }
-
- return $names;
- }
-
- /**
- * Get a ColumnDef from an array matching
- * name.
- *
- * @param array $cds Array of ColumnDef objects
- * @param string $name Name of the column
- *
- * @return ColumnDef matching item or null if no match.
- */
-
- private function _byName($cds, $name)
- {
- foreach ($cds as $cd) {
- if ($cd->name == $name) {
- return $cd;
- }
- }
-
- return null;
+ $strings = array('char', 'varchar', 'text');
+ return in_array(strtolower($cd['type']), $strings);
}
/**
@@ -641,43 +352,93 @@ class MysqlSchema extends Schema
* @return string correct SQL for that column
*/
- private function _columnSql($cd)
+ function columnSql(array $cd)
{
- $sql = "{$cd->name} ";
+ $line = array();
+ $line[] = parent::columnSql($cd);
- if (!empty($cd->size)) {
- $sql .= "{$cd->type}({$cd->size}) ";
- } else {
- $sql .= "{$cd->type} ";
+ // This'll have been added from our transform of 'serial' type
+ if (!empty($cd['auto_increment'])) {
+ $line[] = 'auto_increment';
}
- if ($this->_isString($cd)) {
- $sql .= " CHARACTER SET utf8 ";
+ if (!empty($cd['description'])) {
+ $line[] = 'comment';
+ $line[] = $this->quoteValue($cd['description']);
}
- if (!empty($cd->default)) {
- $sql .= "default {$cd->default} ";
- } else {
- $sql .= ($cd->nullable) ? "null " : "not null ";
- }
+ return implode(' ', $line);
+ }
+
+ function mapType($column)
+ {
+ $map = array('serial' => 'int',
+ 'integer' => 'int',
+ 'numeric' => 'decimal');
- if (!empty($cd->auto_increment)) {
- $sql .= " auto_increment ";
+ $type = $column['type'];
+ if (isset($map[$type])) {
+ $type = $map[$type];
}
- if (!empty($cd->extra)) {
- $sql .= "{$cd->extra} ";
+ if (!empty($column['size'])) {
+ $size = $column['size'];
+ if ($type == 'int' &&
+ in_array($size, array('tiny', 'small', 'medium', 'big'))) {
+ $type = $size . $type;
+ } else if (in_array($type, array('blob', 'text')) &&
+ in_array($size, array('tiny', 'medium', 'long'))) {
+ $type = $size . $type;
+ }
}
- return $sql;
+ return $type;
+ }
+
+ function typeAndSize($column)
+ {
+ if ($column['type'] == 'enum') {
+ $vals = array_map(array($this, 'quote'), $column['enum']);
+ return 'enum(' . implode(',', $vals) . ')';
+ } else if ($this->_isString($column)) {
+ $col = parent::typeAndSize($column);
+ if (!empty($column['charset'])) {
+ $col .= ' CHARSET ' . $column['charset'];
+ }
+ if (!empty($column['collate'])) {
+ $col .= ' COLLATE ' . $column['collate'];
+ }
+ return $col;
+ } else {
+ return parent::typeAndSize($column);
+ }
}
/**
- * Is this column a string type?
+ * Filter the given table definition array to match features available
+ * in this database.
+ *
+ * This lets us strip out unsupported things like comments, foreign keys,
+ * or type variants that we wouldn't get back from getTableDef().
+ *
+ * @param array $tableDef
*/
- private function _isString(ColumnDef $cd)
+ function filterDef(array $tableDef)
{
- $strings = array('char', 'varchar', 'text');
- return in_array(strtolower($cd->type), $strings);
+ foreach ($tableDef['fields'] as $name => &$col) {
+ if ($col['type'] == 'serial') {
+ $col['type'] = 'int';
+ $col['auto_increment'] = true;
+ }
+ if ($col['type'] == 'datetime' && isset($col['default']) && $col['default'] == 'CURRENT_TIMESTAMP') {
+ $col['type'] = 'timestamp';
+ }
+ $col['type'] = $this->mapType($col);
+ unset($col['size']);
+ }
+ if (!common_config('db', 'mysql_foreign_keys')) {
+ unset($tableDef['foreign keys']);
+ }
+ return $tableDef;
}
}
diff --git a/lib/noticeform.php b/lib/noticeform.php
index 271d360707..3909b088d0 100644
--- a/lib/noticeform.php
+++ b/lib/noticeform.php
@@ -48,31 +48,26 @@ require_once INSTALLDIR.'/lib/form.php';
*
* @see HTMLOutputter
*/
-
class NoticeForm extends Form
{
/**
* Current action, used for returning to this page.
*/
-
var $action = null;
/**
* Pre-filled content of the form
*/
-
var $content = null;
/**
* The current user
*/
-
var $user = null;
/**
* The notice being replied to
*/
-
var $inreplyto = null;
/**
@@ -91,9 +86,10 @@ class NoticeForm extends Form
* @param string $action action to return to, if any
* @param string $content content to pre-fill
*/
-
function __construct($out=null, $action=null, $content=null, $user=null, $inreplyto=null, $lat=null, $lon=null, $location_id=null, $location_ns=null)
{
+ $this->id_suffix = time();
+
parent::__construct($out);
$this->action = $action;
@@ -125,7 +121,7 @@ class NoticeForm extends Form
function id()
{
- return 'form_notice';
+ return 'form_notice_' . $this->id_suffix;
}
/**
@@ -136,7 +132,7 @@ class NoticeForm extends Form
function formClass()
{
- return 'form_notice';
+ return 'form_notice ajax-notice';
}
/**
@@ -157,6 +153,7 @@ class NoticeForm extends Form
*/
function formLegend()
{
+ // TRANS: Form legend for notice form.
$this->out->element('legend', null, _('Send a notice'));
}
@@ -165,15 +162,15 @@ class NoticeForm extends Form
*
* @return void
*/
-
function formData()
{
if (Event::handle('StartShowNoticeFormData', array($this))) {
$this->out->element('label', array('for' => 'notice_data-text',
'id' => 'notice_data-text-label'),
+ // TRANS: Title for notice label. %s is the user's nickname.
sprintf(_('What\'s up, %s?'), $this->user->nickname));
// XXX: vary by defined max size
- $this->out->element('textarea', array('id' => 'notice_data-text',
+ $this->out->element('textarea', array('class' => 'notice_data-text',
'cols' => 35,
'rows' => 4,
'name' => 'status_textarea'),
@@ -182,20 +179,22 @@ class NoticeForm extends Form
$contentLimit = Notice::maxContent();
if ($contentLimit > 0) {
- $this->out->elementStart('dl', 'form_note');
- $this->out->element('dt', null, _('Available characters'));
- $this->out->element('dd', array('id' => 'notice_text-count'),
+ $this->out->element('span',
+ array('class' => 'count'),
$contentLimit);
- $this->out->elementEnd('dl');
}
if (common_config('attachments', 'uploads')) {
$this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
- $this->out->element('label', array('for' => 'notice_data-attach'),_('Attach'));
- $this->out->element('input', array('id' => 'notice_data-attach',
+ $this->out->elementStart('label', array('class' => 'notice_data-attach'));
+ // TRANS: Input label in notice form for adding an attachment.
+ $this->out->text(_('Attach'));
+ $this->out->element('input', array('class' => 'notice_data-attach',
'type' => 'file',
'name' => 'attach',
- 'title' => _('Attach a file')));
+ // TRANS: Title for input field to attach a file to a notice.
+ 'title' => _('Attach a file.')));
+ $this->out->elementEnd('label');
}
if ($this->action) {
$this->out->hidden('notice_return-to', $this->action, 'returnto');
@@ -208,13 +207,32 @@ class NoticeForm extends Form
$this->out->hidden('notice_data-location_id', empty($this->location_id) ? (empty($this->profile->location_id) ? null : $this->profile->location_id) : $this->location_id, 'location_id');
$this->out->hidden('notice_data-location_ns', empty($this->location_ns) ? (empty($this->profile->location_ns) ? null : $this->profile->location_ns) : $this->location_ns, 'location_ns');
- $this->out->elementStart('div', array('id' => 'notice_data-geo_wrap',
- 'title' => common_local_url('geocode')));
- $this->out->checkbox('notice_data-geo', _('Share my location'), true);
+ $this->out->elementStart('div', array('class' => 'notice_data-geo_wrap',
+ 'data-api' => common_local_url('geocode')));
+
+ // @fixme checkbox method allows no way to change the id without changing the name
+ //$this->out->checkbox('notice_data-geo', _('Share my location'), true);
+ $this->out->elementStart('label', 'notice_data-geo');
+ $this->out->element('input', array(
+ 'name' => 'notice_data-geo',
+ 'type' => 'checkbox',
+ 'class' => 'checkbox',
+ 'id' => $this->id() . '-notice_data-geo',
+ 'checked' => true, // ?
+ ));
+ $this->out->text(' ');
+ // TRANS: Field label to add location to a notice.
+ $this->out->text(_('Share my location'));
+ $this->out->elementEnd('label');
+
$this->out->elementEnd('div');
+ // TRANS: Text to not share location for a notice in notice form.
+ $share_disable_text = _('Do not share my location');
+ // TRANS: Timeout error text for location retrieval in notice form.
+ $error_timeout_text = _('Sorry, retrieving your geo location is taking longer than expected, please try again later');
$this->out->inlineScript(' var NoticeDataGeo_text = {'.
- 'ShareDisable: ' .json_encode(_('Do not share my location')).','.
- 'ErrorTimeout: ' .json_encode(_('Sorry, retrieving your geo location is taking longer than expected, please try again later')).
+ 'ShareDisable: ' .json_encode($share_disable_text).','.
+ 'ErrorTimeout: ' .json_encode($error_timeout_text).
'}');
}
@@ -234,6 +252,7 @@ class NoticeForm extends Form
'class' => 'submit',
'name' => 'status_submit',
'type' => 'submit',
- 'value' => _m('Send button for sending notice', 'Send')));
+ // TRANS: Button text for sending notice.
+ 'value' => _m('BUTTON', 'Send')));
}
}
diff --git a/lib/noticelist.php b/lib/noticelist.php
index 7b2fbb1e7c..dbe2a0996f 100644
--- a/lib/noticelist.php
+++ b/lib/noticelist.php
@@ -129,587 +129,3 @@ class NoticeList extends Widget
}
}
-/**
- * widget for displaying a single notice
- *
- * This widget has the core smarts for showing a single notice: what to display,
- * where, and under which circumstances. Its key method is show(); this is a recipe
- * that calls all the other show*() methods to build up a single notice. The
- * ProfileNoticeListItem subclass, for example, overrides showAuthor() to skip
- * author info (since that's implicit by the data in the page).
- *
- * @category UI
- * @package StatusNet
- * @author Evan Prodromou
- * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link http://status.net/
- * @see NoticeList
- * @see ProfileNoticeListItem
- */
-
-class NoticeListItem extends Widget
-{
- /** The notice this item will show. */
-
- var $notice = null;
-
- /** The notice that was repeated. */
-
- var $repeat = null;
-
- /** The profile of the author of the notice, extracted once for convenience. */
-
- var $profile = null;
-
- /**
- * constructor
- *
- * Also initializes the profile attribute.
- *
- * @param Notice $notice The notice we'll display
- */
-
- function __construct($notice, $out=null)
- {
- parent::__construct($out);
- if (!empty($notice->repeat_of)) {
- $original = Notice::staticGet('id', $notice->repeat_of);
- if (empty($original)) { // could have been deleted
- $this->notice = $notice;
- } else {
- $this->notice = $original;
- $this->repeat = $notice;
- }
- } else {
- $this->notice = $notice;
- }
- $this->profile = $this->notice->getProfile();
- }
-
- /**
- * recipe function for displaying a single notice.
- *
- * This uses all the other methods to correctly display a notice. Override
- * it or one of the others to fine-tune the output.
- *
- * @return void
- */
-
- function show()
- {
- if (empty($this->notice)) {
- common_log(LOG_WARNING, "Trying to show missing notice; skipping.");
- return;
- } else if (empty($this->profile)) {
- common_log(LOG_WARNING, "Trying to show missing profile (" . $this->notice->profile_id . "); skipping.");
- return;
- }
-
- $this->showStart();
- if (Event::handle('StartShowNoticeItem', array($this))) {
- $this->showNotice();
- $this->showNoticeAttachments();
- $this->showNoticeInfo();
- $this->showNoticeOptions();
- Event::handle('EndShowNoticeItem', array($this));
- }
- $this->showEnd();
- }
-
- function showNotice()
- {
- $this->out->elementStart('div', 'entry-title');
- $this->showAuthor();
- $this->showContent();
- $this->out->elementEnd('div');
- }
-
- function showNoticeInfo()
- {
- $this->out->elementStart('div', 'entry-content');
- if (Event::handle('StartShowNoticeInfo', array($this))) {
- $this->showNoticeLink();
- $this->showNoticeSource();
- $this->showNoticeLocation();
- $this->showContext();
- $this->showRepeat();
- Event::handle('EndShowNoticeInfo', array($this));
- }
-
- $this->out->elementEnd('div');
- }
-
- function showNoticeOptions()
- {
- if (Event::handle('StartShowNoticeOptions', array($this))) {
- $user = common_current_user();
- if ($user) {
- $this->out->elementStart('div', 'notice-options');
- $this->showFaveForm();
- $this->showReplyLink();
- $this->showRepeatForm();
- $this->showDeleteLink();
- $this->out->elementEnd('div');
- }
- Event::handle('EndShowNoticeOptions', array($this));
- }
- }
-
- /**
- * start a single notice.
- *
- * @return void
- */
-
- function showStart()
- {
- if (Event::handle('StartOpenNoticeListItemElement', array($this))) {
- $id = (empty($this->repeat)) ? $this->notice->id : $this->repeat->id;
- $this->out->elementStart('li', array('class' => 'hentry notice',
- 'id' => 'notice-' . $id));
- Event::handle('EndOpenNoticeListItemElement', array($this));
- }
- }
-
- /**
- * show the "favorite" form
- *
- * @return void
- */
-
- function showFaveForm()
- {
- if (Event::handle('StartShowFaveForm', array($this))) {
- $user = common_current_user();
- if ($user) {
- if ($user->hasFave($this->notice)) {
- $disfavor = new DisfavorForm($this->out, $this->notice);
- $disfavor->show();
- } else {
- $favor = new FavorForm($this->out, $this->notice);
- $favor->show();
- }
- }
- Event::handle('EndShowFaveForm', array($this));
- }
- }
-
- /**
- * show the author of a notice
- *
- * By default, this shows the avatar and (linked) nickname of the author.
- *
- * @return void
- */
-
- function showAuthor()
- {
- $this->out->elementStart('span', 'vcard author');
- $attrs = array('href' => $this->profile->profileurl,
- 'class' => 'url');
- if (!empty($this->profile->fullname)) {
- $attrs['title'] = $this->profile->getFancyName();
- }
- $this->out->elementStart('a', $attrs);
- $this->showAvatar();
- $this->out->text(' ');
- $this->showNickname();
- $this->out->elementEnd('a');
- $this->out->elementEnd('span');
- }
-
- /**
- * show the avatar of the notice's author
- *
- * This will use the default avatar if no avatar is assigned for the author.
- * It makes a link to the author's profile.
- *
- * @return void
- */
-
- function showAvatar()
- {
- $avatar_size = AVATAR_STREAM_SIZE;
-
- $avatar = $this->profile->getAvatar($avatar_size);
-
- $this->out->element('img', array('src' => ($avatar) ?
- $avatar->displayUrl() :
- Avatar::defaultImage($avatar_size),
- 'class' => 'avatar photo',
- 'width' => $avatar_size,
- 'height' => $avatar_size,
- 'alt' =>
- ($this->profile->fullname) ?
- $this->profile->fullname :
- $this->profile->nickname));
- }
-
- /**
- * show the nickname of the author
- *
- * Links to the author's profile page
- *
- * @return void
- */
-
- function showNickname()
- {
- $this->out->raw('' .
- htmlspecialchars($this->profile->nickname) .
- '');
- }
-
- /**
- * show the content of the notice
- *
- * Shows the content of the notice. This is pre-rendered for efficiency
- * at save time. Some very old notices might not be pre-rendered, so
- * they're rendered on the spot.
- *
- * @return void
- */
-
- function showContent()
- {
- // FIXME: URL, image, video, audio
- $this->out->elementStart('p', array('class' => 'entry-content'));
- if ($this->notice->rendered) {
- $this->out->raw($this->notice->rendered);
- } else {
- // XXX: may be some uncooked notices in the DB,
- // we cook them right now. This should probably disappear in future
- // versions (>> 0.4.x)
- $this->out->raw(common_render_content($this->notice->content, $this->notice));
- }
- $this->out->elementEnd('p');
- }
-
- function showNoticeAttachments() {
- if (common_config('attachments', 'show_thumbs')) {
- $al = new InlineAttachmentList($this->notice, $this->out);
- $al->show();
- }
- }
-
- /**
- * show the link to the main page for the notice
- *
- * Displays a link to the page for a notice, with "relative" time. Tries to
- * get remote notice URLs correct, but doesn't always succeed.
- *
- * @return void
- */
-
- function showNoticeLink()
- {
- $noticeurl = $this->notice->bestUrl();
-
- // above should always return an URL
-
- assert(!empty($noticeurl));
-
- $this->out->elementStart('a', array('rel' => 'bookmark',
- 'class' => 'timestamp',
- 'href' => $noticeurl));
- $dt = common_date_iso8601($this->notice->created);
- $this->out->element('abbr', array('class' => 'published',
- 'title' => $dt),
- common_date_string($this->notice->created));
- $this->out->elementEnd('a');
- }
-
- /**
- * show the notice location
- *
- * shows the notice location in the correct language.
- *
- * If an URL is available, makes a link. Otherwise, just a span.
- *
- * @return void
- */
-
- function showNoticeLocation()
- {
- $id = $this->notice->id;
-
- $location = $this->notice->getLocation();
-
- if (empty($location)) {
- return;
- }
-
- $name = $location->getName();
-
- $lat = $this->notice->lat;
- $lon = $this->notice->lon;
- $latlon = (!empty($lat) && !empty($lon)) ? $lat.';'.$lon : '';
-
- if (empty($name)) {
- $latdms = $this->decimalDegreesToDMS(abs($lat));
- $londms = $this->decimalDegreesToDMS(abs($lon));
- // TRANS: Used in coordinates as abbreviation of north
- $north = _('N');
- // TRANS: Used in coordinates as abbreviation of south
- $south = _('S');
- // TRANS: Used in coordinates as abbreviation of east
- $east = _('E');
- // TRANS: Used in coordinates as abbreviation of west
- $west = _('W');
- $name = sprintf(
- _('%1$u°%2$u\'%3$u"%4$s %5$u°%6$u\'%7$u"%8$s'),
- $latdms['deg'],$latdms['min'], $latdms['sec'],($lat>0? $north:$south),
- $londms['deg'],$londms['min'], $londms['sec'],($lon>0? $east:$west));
- }
-
- $url = $location->getUrl();
-
- $this->out->text(' ');
- $this->out->elementStart('span', array('class' => 'location'));
- $this->out->text(_('at'));
- $this->out->text(' ');
- if (empty($url)) {
- $this->out->element('abbr', array('class' => 'geo',
- 'title' => $latlon),
- $name);
- } else {
- $xstr = new XMLStringer(false);
- $xstr->elementStart('a', array('href' => $url,
- 'rel' => 'external'));
- $xstr->element('abbr', array('class' => 'geo',
- 'title' => $latlon),
- $name);
- $xstr->elementEnd('a');
- $this->out->raw($xstr->getString());
- }
- $this->out->elementEnd('span');
- }
-
- /**
- * @param number $dec decimal degrees
- * @return array split into 'deg', 'min', and 'sec'
- */
- function decimalDegreesToDMS($dec)
- {
- $deg = intval($dec);
- $tempma = abs($dec) - abs($deg);
-
- $tempma = $tempma * 3600;
- $min = floor($tempma / 60);
- $sec = $tempma - ($min*60);
-
- return array("deg"=>$deg,"min"=>$min,"sec"=>$sec);
- }
-
- /**
- * Show the source of the notice
- *
- * Either the name (and link) of the API client that posted the notice,
- * or one of other other channels.
- *
- * @return void
- */
-
- function showNoticeSource()
- {
- $ns = $this->notice->getSource();
-
- if ($ns) {
- $source_name = (empty($ns->name)) ? ($ns->code ? _($ns->code) : _('web')) : _($ns->name);
- $this->out->text(' ');
- $this->out->elementStart('span', 'source');
- // FIXME: probably i18n issue. If "from" is followed by text, that should be a parameter to "from" (from %s).
- $this->out->text(_('from'));
- $this->out->text(' ');
-
- $name = $source_name;
- $url = $ns->url;
- $title = null;
-
- if (Event::handle('StartNoticeSourceLink', array($this->notice, &$name, &$url, &$title))) {
- $name = $source_name;
- $url = $ns->url;
- }
- Event::handle('EndNoticeSourceLink', array($this->notice, &$name, &$url, &$title));
-
- // if $ns->name and $ns->url are populated we have
- // configured a source attr somewhere
- if (!empty($name) && !empty($url)) {
-
- $this->out->elementStart('span', 'device');
-
- $attrs = array(
- 'href' => $url,
- 'rel' => 'external'
- );
-
- if (!empty($title)) {
- $attrs['title'] = $title;
- }
-
- $this->out->element('a', $attrs, $name);
- $this->out->elementEnd('span');
- } else {
- $this->out->element('span', 'device', $name);
- }
-
- $this->out->elementEnd('span');
- }
- }
-
- /**
- * show link to notice this notice is a reply to
- *
- * If this notice is a reply, show a link to the notice it is replying to. The
- * heavy lifting for figuring out replies happens at save time.
- *
- * @return void
- */
-
- function showContext()
- {
- if ($this->notice->hasConversation()) {
- $conv = Conversation::staticGet(
- 'id',
- $this->notice->conversation
- );
- $convurl = $conv->uri;
- if (!empty($convurl)) {
- $this->out->text(' ');
- $this->out->element(
- 'a',
- array(
- 'href' => $convurl.'#notice-'.$this->notice->id,
- 'class' => 'response'),
- _('in context')
- );
- } else {
- $msg = sprintf(
- "Couldn't find Conversation ID %d to make 'in context'"
- . "link for Notice ID %d",
- $this->notice->conversation,
- $this->notice->id
- );
- common_log(LOG_WARNING, $msg);
- }
- }
- }
-
- /**
- * show a link to the author of repeat
- *
- * @return void
- */
-
- function showRepeat()
- {
- if (!empty($this->repeat)) {
-
- $repeater = Profile::staticGet('id', $this->repeat->profile_id);
-
- $attrs = array('href' => $repeater->profileurl,
- 'class' => 'url');
-
- if (!empty($repeater->fullname)) {
- $attrs['title'] = $repeater->fullname . ' (' . $repeater->nickname . ')';
- }
-
- $this->out->elementStart('span', 'repeat vcard');
-
- $this->out->raw(_('Repeated by'));
-
- $this->out->elementStart('a', $attrs);
- $this->out->element('span', 'fn nickname', $repeater->nickname);
- $this->out->elementEnd('a');
-
- $this->out->elementEnd('span');
- }
- }
-
- /**
- * show a link to reply to the current notice
- *
- * Should either do the reply in the current notice form (if available), or
- * link out to the notice-posting form. A little flakey, doesn't always work.
- *
- * @return void
- */
-
- function showReplyLink()
- {
- if (common_logged_in()) {
- $this->out->text(' ');
- $reply_url = common_local_url('newnotice',
- array('replyto' => $this->profile->nickname, 'inreplyto' => $this->notice->id));
- $this->out->elementStart('a', array('href' => $reply_url,
- 'class' => 'notice_reply',
- 'title' => _('Reply to this notice')));
- $this->out->text(_('Reply'));
- $this->out->text(' ');
- $this->out->element('span', 'notice_id', $this->notice->id);
- $this->out->elementEnd('a');
- }
- }
-
- /**
- * if the user is the author, let them delete the notice
- *
- * @return void
- */
-
- function showDeleteLink()
- {
- $user = common_current_user();
-
- $todel = (empty($this->repeat)) ? $this->notice : $this->repeat;
-
- if (!empty($user) &&
- ($todel->profile_id == $user->id || $user->hasRight(Right::DELETEOTHERSNOTICE))) {
- $this->out->text(' ');
- $deleteurl = common_local_url('deletenotice',
- array('notice' => $todel->id));
- $this->out->element('a', array('href' => $deleteurl,
- 'class' => 'notice_delete',
- 'title' => _('Delete this notice')), _('Delete'));
- }
- }
-
- /**
- * show the form to repeat a notice
- *
- * @return void
- */
-
- function showRepeatForm()
- {
- $user = common_current_user();
- if ($user && $user->id != $this->notice->profile_id) {
- $this->out->text(' ');
- $profile = $user->getProfile();
- if ($profile->hasRepeated($this->notice->id)) {
- $this->out->element('span', array('class' => 'repeated',
- 'title' => _('Notice repeated')),
- _('Repeated'));
- } else {
- $rf = new RepeatForm($this->out, $this->notice);
- $rf->show();
- }
- }
- }
-
- /**
- * finish the notice
- *
- * Close the last elements in the notice list item
- *
- * @return void
- */
-
- function showEnd()
- {
- if (Event::handle('StartCloseNoticeListItemElement', array($this))) {
- $this->out->elementEnd('li');
- Event::handle('EndCloseNoticeListItemElement', array($this));
- }
- }
-}
diff --git a/lib/noticelistitem.php b/lib/noticelistitem.php
new file mode 100644
index 0000000000..17827d07ef
--- /dev/null
+++ b/lib/noticelistitem.php
@@ -0,0 +1,625 @@
+.
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * widget for displaying a single notice
+ *
+ * This widget has the core smarts for showing a single notice: what to display,
+ * where, and under which circumstances. Its key method is show(); this is a recipe
+ * that calls all the other show*() methods to build up a single notice. The
+ * ProfileNoticeListItem subclass, for example, overrides showAuthor() to skip
+ * author info (since that's implicit by the data in the page).
+ *
+ * @category UI
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ * @see NoticeList
+ * @see ProfileNoticeListItem
+ */
+
+class NoticeListItem extends Widget
+{
+ /** The notice this item will show. */
+
+ var $notice = null;
+
+ /** The notice that was repeated. */
+
+ var $repeat = null;
+
+ /** The profile of the author of the notice, extracted once for convenience. */
+
+ var $profile = null;
+
+ /**
+ * constructor
+ *
+ * Also initializes the profile attribute.
+ *
+ * @param Notice $notice The notice we'll display
+ */
+
+ function __construct($notice, $out=null)
+ {
+ parent::__construct($out);
+ if (!empty($notice->repeat_of)) {
+ $original = Notice::staticGet('id', $notice->repeat_of);
+ if (empty($original)) { // could have been deleted
+ $this->notice = $notice;
+ } else {
+ $this->notice = $original;
+ $this->repeat = $notice;
+ }
+ } else {
+ $this->notice = $notice;
+ }
+ $this->profile = $this->notice->getProfile();
+ }
+
+ /**
+ * recipe function for displaying a single notice.
+ *
+ * This uses all the other methods to correctly display a notice. Override
+ * it or one of the others to fine-tune the output.
+ *
+ * @return void
+ */
+
+ function show()
+ {
+ if (empty($this->notice)) {
+ common_log(LOG_WARNING, "Trying to show missing notice; skipping.");
+ return;
+ } else if (empty($this->profile)) {
+ common_log(LOG_WARNING, "Trying to show missing profile (" . $this->notice->profile_id . "); skipping.");
+ return;
+ }
+
+ $this->showStart();
+ if (Event::handle('StartShowNoticeItem', array($this))) {
+ $this->showNotice();
+ $this->showNoticeAttachments();
+ $this->showNoticeInfo();
+ $this->showNoticeOptions();
+ Event::handle('EndShowNoticeItem', array($this));
+ }
+ $this->showEnd();
+ }
+
+ function showNotice()
+ {
+ $this->out->elementStart('div', 'entry-title');
+ $this->showAuthor();
+ $this->showContent();
+ $this->out->elementEnd('div');
+ }
+
+ function showNoticeInfo()
+ {
+ $this->out->elementStart('div', 'entry-content');
+ if (Event::handle('StartShowNoticeInfo', array($this))) {
+ $this->showNoticeLink();
+ $this->showNoticeSource();
+ $this->showNoticeLocation();
+ $this->showContext();
+ $this->showRepeat();
+ Event::handle('EndShowNoticeInfo', array($this));
+ }
+
+ $this->out->elementEnd('div');
+ }
+
+ function showNoticeOptions()
+ {
+ if (Event::handle('StartShowNoticeOptions', array($this))) {
+ $user = common_current_user();
+ if ($user) {
+ $this->out->elementStart('div', 'notice-options');
+ $this->showFaveForm();
+ $this->showReplyLink();
+ $this->showRepeatForm();
+ $this->showDeleteLink();
+ $this->out->elementEnd('div');
+ }
+ Event::handle('EndShowNoticeOptions', array($this));
+ }
+ }
+
+ /**
+ * start a single notice.
+ *
+ * @return void
+ */
+
+ function showStart()
+ {
+ if (Event::handle('StartOpenNoticeListItemElement', array($this))) {
+ $id = (empty($this->repeat)) ? $this->notice->id : $this->repeat->id;
+ $this->out->elementStart('li', array('class' => 'hentry notice',
+ 'id' => 'notice-' . $id));
+ Event::handle('EndOpenNoticeListItemElement', array($this));
+ }
+ }
+
+ /**
+ * show the "favorite" form
+ *
+ * @return void
+ */
+
+ function showFaveForm()
+ {
+ if (Event::handle('StartShowFaveForm', array($this))) {
+ $user = common_current_user();
+ if ($user) {
+ if ($user->hasFave($this->notice)) {
+ $disfavor = new DisfavorForm($this->out, $this->notice);
+ $disfavor->show();
+ } else {
+ $favor = new FavorForm($this->out, $this->notice);
+ $favor->show();
+ }
+ }
+ Event::handle('EndShowFaveForm', array($this));
+ }
+ }
+
+ /**
+ * show the author of a notice
+ *
+ * By default, this shows the avatar and (linked) nickname of the author.
+ *
+ * @return void
+ */
+
+ function showAuthor()
+ {
+ $this->out->elementStart('span', 'vcard author');
+ $attrs = array('href' => $this->profile->profileurl,
+ 'class' => 'url');
+ if (!empty($this->profile->fullname)) {
+ $attrs['title'] = $this->profile->getFancyName();
+ }
+ $this->out->elementStart('a', $attrs);
+ $this->showAvatar();
+ $this->out->text(' ');
+ $this->showNickname();
+ $this->out->elementEnd('a');
+ $this->out->elementEnd('span');
+ }
+
+ /**
+ * show the avatar of the notice's author
+ *
+ * This will use the default avatar if no avatar is assigned for the author.
+ * It makes a link to the author's profile.
+ *
+ * @return void
+ */
+
+ function showAvatar()
+ {
+ $avatar_size = $this->avatarSize();
+
+ $avatar = $this->profile->getAvatar($avatar_size);
+
+ $this->out->element('img', array('src' => ($avatar) ?
+ $avatar->displayUrl() :
+ Avatar::defaultImage($avatar_size),
+ 'class' => 'avatar photo',
+ 'width' => $avatar_size,
+ 'height' => $avatar_size,
+ 'alt' =>
+ ($this->profile->fullname) ?
+ $this->profile->fullname :
+ $this->profile->nickname));
+ }
+
+ function avatarSize()
+ {
+ return AVATAR_STREAM_SIZE;
+ }
+
+ /**
+ * show the nickname of the author
+ *
+ * Links to the author's profile page
+ *
+ * @return void
+ */
+
+ function showNickname()
+ {
+ $this->out->raw('' .
+ htmlspecialchars($this->profile->nickname) .
+ '');
+ }
+
+ /**
+ * show the content of the notice
+ *
+ * Shows the content of the notice. This is pre-rendered for efficiency
+ * at save time. Some very old notices might not be pre-rendered, so
+ * they're rendered on the spot.
+ *
+ * @return void
+ */
+
+ function showContent()
+ {
+ // FIXME: URL, image, video, audio
+ $this->out->elementStart('p', array('class' => 'entry-content'));
+ if ($this->notice->rendered) {
+ $this->out->raw($this->notice->rendered);
+ } else {
+ // XXX: may be some uncooked notices in the DB,
+ // we cook them right now. This should probably disappear in future
+ // versions (>> 0.4.x)
+ $this->out->raw(common_render_content($this->notice->content, $this->notice));
+ }
+ $this->out->elementEnd('p');
+ }
+
+ function showNoticeAttachments() {
+ if (common_config('attachments', 'show_thumbs')) {
+ $al = new InlineAttachmentList($this->notice, $this->out);
+ $al->show();
+ }
+ }
+
+ /**
+ * show the link to the main page for the notice
+ *
+ * Displays a link to the page for a notice, with "relative" time. Tries to
+ * get remote notice URLs correct, but doesn't always succeed.
+ *
+ * @return void
+ */
+
+ function showNoticeLink()
+ {
+ $noticeurl = $this->notice->bestUrl();
+
+ // above should always return an URL
+
+ assert(!empty($noticeurl));
+
+ $this->out->elementStart('a', array('rel' => 'bookmark',
+ 'class' => 'timestamp',
+ 'href' => $noticeurl));
+ $dt = common_date_iso8601($this->notice->created);
+ $this->out->element('abbr', array('class' => 'published',
+ 'title' => $dt),
+ common_date_string($this->notice->created));
+ $this->out->elementEnd('a');
+ }
+
+ /**
+ * show the notice location
+ *
+ * shows the notice location in the correct language.
+ *
+ * If an URL is available, makes a link. Otherwise, just a span.
+ *
+ * @return void
+ */
+
+ function showNoticeLocation()
+ {
+ $id = $this->notice->id;
+
+ $location = $this->notice->getLocation();
+
+ if (empty($location)) {
+ return;
+ }
+
+ $name = $location->getName();
+
+ $lat = $this->notice->lat;
+ $lon = $this->notice->lon;
+ $latlon = (!empty($lat) && !empty($lon)) ? $lat.';'.$lon : '';
+
+ if (empty($name)) {
+ $latdms = $this->decimalDegreesToDMS(abs($lat));
+ $londms = $this->decimalDegreesToDMS(abs($lon));
+ // TRANS: Used in coordinates as abbreviation of north
+ $north = _('N');
+ // TRANS: Used in coordinates as abbreviation of south
+ $south = _('S');
+ // TRANS: Used in coordinates as abbreviation of east
+ $east = _('E');
+ // TRANS: Used in coordinates as abbreviation of west
+ $west = _('W');
+ $name = sprintf(
+ _('%1$u°%2$u\'%3$u"%4$s %5$u°%6$u\'%7$u"%8$s'),
+ $latdms['deg'],$latdms['min'], $latdms['sec'],($lat>0? $north:$south),
+ $londms['deg'],$londms['min'], $londms['sec'],($lon>0? $east:$west));
+ }
+
+ $url = $location->getUrl();
+
+ $this->out->text(' ');
+ $this->out->elementStart('span', array('class' => 'location'));
+ $this->out->text(_('at'));
+ $this->out->text(' ');
+ if (empty($url)) {
+ $this->out->element('abbr', array('class' => 'geo',
+ 'title' => $latlon),
+ $name);
+ } else {
+ $xstr = new XMLStringer(false);
+ $xstr->elementStart('a', array('href' => $url,
+ 'rel' => 'external'));
+ $xstr->element('abbr', array('class' => 'geo',
+ 'title' => $latlon),
+ $name);
+ $xstr->elementEnd('a');
+ $this->out->raw($xstr->getString());
+ }
+ $this->out->elementEnd('span');
+ }
+
+ /**
+ * @param number $dec decimal degrees
+ * @return array split into 'deg', 'min', and 'sec'
+ */
+ function decimalDegreesToDMS($dec)
+ {
+ $deg = intval($dec);
+ $tempma = abs($dec) - abs($deg);
+
+ $tempma = $tempma * 3600;
+ $min = floor($tempma / 60);
+ $sec = $tempma - ($min*60);
+
+ return array("deg"=>$deg,"min"=>$min,"sec"=>$sec);
+ }
+
+ /**
+ * Show the source of the notice
+ *
+ * Either the name (and link) of the API client that posted the notice,
+ * or one of other other channels.
+ *
+ * @return void
+ */
+
+ function showNoticeSource()
+ {
+ $ns = $this->notice->getSource();
+
+ if ($ns) {
+ $source_name = (empty($ns->name)) ? ($ns->code ? _($ns->code) : _('web')) : _($ns->name);
+ $this->out->text(' ');
+ $this->out->elementStart('span', 'source');
+ // FIXME: probably i18n issue. If "from" is followed by text, that should be a parameter to "from" (from %s).
+ $this->out->text(_('from'));
+ $this->out->text(' ');
+
+ $name = $source_name;
+ $url = $ns->url;
+ $title = null;
+
+ if (Event::handle('StartNoticeSourceLink', array($this->notice, &$name, &$url, &$title))) {
+ $name = $source_name;
+ $url = $ns->url;
+ }
+ Event::handle('EndNoticeSourceLink', array($this->notice, &$name, &$url, &$title));
+
+ // if $ns->name and $ns->url are populated we have
+ // configured a source attr somewhere
+ if (!empty($name) && !empty($url)) {
+
+ $this->out->elementStart('span', 'device');
+
+ $attrs = array(
+ 'href' => $url,
+ 'rel' => 'external'
+ );
+
+ if (!empty($title)) {
+ $attrs['title'] = $title;
+ }
+
+ $this->out->element('a', $attrs, $name);
+ $this->out->elementEnd('span');
+ } else {
+ $this->out->element('span', 'device', $name);
+ }
+
+ $this->out->elementEnd('span');
+ }
+ }
+
+ /**
+ * show link to notice this notice is a reply to
+ *
+ * If this notice is a reply, show a link to the notice it is replying to. The
+ * heavy lifting for figuring out replies happens at save time.
+ *
+ * @return void
+ */
+
+ function showContext()
+ {
+ if ($this->notice->hasConversation()) {
+ $conv = Conversation::staticGet(
+ 'id',
+ $this->notice->conversation
+ );
+ $convurl = $conv->uri;
+ if (!empty($convurl)) {
+ $this->out->text(' ');
+ $this->out->element(
+ 'a',
+ array(
+ 'href' => $convurl.'#notice-'.$this->notice->id,
+ 'class' => 'response'),
+ _('in context')
+ );
+ } else {
+ $msg = sprintf(
+ "Couldn't find Conversation ID %d to make 'in context'"
+ . "link for Notice ID %d",
+ $this->notice->conversation,
+ $this->notice->id
+ );
+ common_log(LOG_WARNING, $msg);
+ }
+ }
+ }
+
+ /**
+ * show a link to the author of repeat
+ *
+ * @return void
+ */
+
+ function showRepeat()
+ {
+ if (!empty($this->repeat)) {
+
+ $repeater = Profile::staticGet('id', $this->repeat->profile_id);
+
+ $attrs = array('href' => $repeater->profileurl,
+ 'class' => 'url');
+
+ if (!empty($repeater->fullname)) {
+ $attrs['title'] = $repeater->fullname . ' (' . $repeater->nickname . ')';
+ }
+
+ $this->out->elementStart('span', 'repeat vcard');
+
+ $this->out->raw(_('Repeated by'));
+
+ $this->out->elementStart('a', $attrs);
+ $this->out->element('span', 'fn nickname', $repeater->nickname);
+ $this->out->elementEnd('a');
+
+ $this->out->elementEnd('span');
+ }
+ }
+
+ /**
+ * show a link to reply to the current notice
+ *
+ * Should either do the reply in the current notice form (if available), or
+ * link out to the notice-posting form. A little flakey, doesn't always work.
+ *
+ * @return void
+ */
+
+ function showReplyLink()
+ {
+ if (common_logged_in()) {
+ $this->out->text(' ');
+ $reply_url = common_local_url('newnotice',
+ array('replyto' => $this->profile->nickname, 'inreplyto' => $this->notice->id));
+ $this->out->elementStart('a', array('href' => $reply_url,
+ 'class' => 'notice_reply',
+ 'title' => _('Reply to this notice')));
+ $this->out->text(_('Reply'));
+ $this->out->text(' ');
+ $this->out->element('span', 'notice_id', $this->notice->id);
+ $this->out->elementEnd('a');
+ }
+ }
+
+ /**
+ * if the user is the author, let them delete the notice
+ *
+ * @return void
+ */
+
+ function showDeleteLink()
+ {
+ $user = common_current_user();
+
+ $todel = (empty($this->repeat)) ? $this->notice : $this->repeat;
+
+ if (!empty($user) &&
+ ($todel->profile_id == $user->id || $user->hasRight(Right::DELETEOTHERSNOTICE))) {
+ $this->out->text(' ');
+ $deleteurl = common_local_url('deletenotice',
+ array('notice' => $todel->id));
+ $this->out->element('a', array('href' => $deleteurl,
+ 'class' => 'notice_delete',
+ 'title' => _('Delete this notice')), _('Delete'));
+ }
+ }
+
+ /**
+ * show the form to repeat a notice
+ *
+ * @return void
+ */
+
+ function showRepeatForm()
+ {
+ $user = common_current_user();
+ if ($user && $user->id != $this->notice->profile_id) {
+ $this->out->text(' ');
+ $profile = $user->getProfile();
+ if ($profile->hasRepeated($this->notice->id)) {
+ $this->out->element('span', array('class' => 'repeated',
+ 'title' => _('Notice repeated')),
+ _('Repeated'));
+ } else {
+ $rf = new RepeatForm($this->out, $this->notice);
+ $rf->show();
+ }
+ }
+ }
+
+ /**
+ * finish the notice
+ *
+ * Close the last elements in the notice list item
+ *
+ * @return void
+ */
+
+ function showEnd()
+ {
+ if (Event::handle('StartCloseNoticeListItemElement', array($this))) {
+ $this->out->elementEnd('li');
+ Event::handle('EndCloseNoticeListItemElement', array($this));
+ }
+ }
+}
diff --git a/lib/noticeplaceholderform.php b/lib/noticeplaceholderform.php
new file mode 100644
index 0000000000..788a2021d9
--- /dev/null
+++ b/lib/noticeplaceholderform.php
@@ -0,0 +1,60 @@
+.
+ *
+ * @category Form
+ * @package StatusNet
+ * @author Brion Vibber
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/form.php';
+
+/**
+ * Placeholder form for posting a notice
+ *
+ * Frequently-used form for posting a notice
+ *
+ * @category Form
+ * @package StatusNet
+ * @author Brion Vibber
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ * @see HTMLOutputter
+ */
+class NoticePlaceholderForm extends Widget
+{
+ function show()
+ {
+ // Similar to that for inline replies, but not quite!
+ $placeholder = _('Update your status...');
+ $this->out->elementStart('div', 'form_notice_placeholder');
+ $this->out->element('input', array('class' => 'placeholder',
+ 'value' => $placeholder));
+ $this->out->elementEnd('div');
+ }
+}
diff --git a/lib/nudgeform.php b/lib/nudgeform.php
index 3f13b58462..18a008122d 100644
--- a/lib/nudgeform.php
+++ b/lib/nudgeform.php
@@ -89,7 +89,7 @@ class NudgeForm extends Form
function formClass()
{
- return 'form_user_nudge';
+ return 'form_user_nudge ajax';
}
diff --git a/lib/personalgroupnav.php b/lib/personalgroupnav.php
index 533e9f43d2..2e15ca5f6a 100644
--- a/lib/personalgroupnav.php
+++ b/lib/personalgroupnav.php
@@ -2,7 +2,7 @@
/**
* StatusNet, the distributed open-source microblogging tool
*
- * Base class for all actions (~views)
+ * Menu for personal group of actions
*
* PHP version 5
*
@@ -19,11 +19,11 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
- * @category Action
+ * @category Menu
* @package StatusNet
* @author Evan Prodromou
* @author Sarven Capadisli
- * @copyright 2008 StatusNet, Inc.
+ * @copyright 2008-2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@@ -32,41 +32,20 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
-require_once INSTALLDIR.'/lib/widget.php';
-
/**
- * Base class for all actions
+ * Menu for personal group of actions
*
- * This is the base class for all actions in the package. An action is
- * more or less a "view" in an MVC framework.
- *
- * Actions are responsible for extracting and validating parameters; using
- * model classes to read and write to the database; and doing ouput.
- *
- * @category Output
+ * @category Menu
* @package StatusNet
* @author Evan Prodromou
* @author Sarven Capadisli
+ * @copyright 2008-2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
- *
- * @see HTMLOutputter
*/
-class PersonalGroupNav extends Widget
+
+class PersonalGroupNav extends Menu
{
- var $action = null;
-
- /**
- * Construction
- *
- * @param Action $action current action, used for output
- */
- function __construct($action=null)
- {
- parent::__construct($action);
- $this->action = $action;
- }
-
/**
* Show the menu
*
@@ -74,53 +53,43 @@ class PersonalGroupNav extends Widget
*/
function show()
{
- $user = null;
+ $user = common_current_user();
- // FIXME: we should probably pass this in
-
- $action = $this->action->trimmed('action');
- $nickname = $this->action->trimmed('nickname');
-
- if ($nickname) {
- $user = User::staticGet('nickname', $nickname);
- $user_profile = $user->getProfile();
- $name = $user_profile->getBestName();
- } else {
- // @fixme can this happen? is this valid?
- $user_profile = false;
- $name = $nickname;
+ if (empty($user)) {
+ throw new ServerException('Do not show personal group nav with no current user.');
}
+ $user_profile = $user->getProfile();
+ $nickname = $user->nickname;
+ $name = $user_profile->getBestName();
+
+ $action = $this->actionName;
+ $mine = ($this->action->arg('nickname') == $nickname); // @fixme kinda vague
+
$this->out->elementStart('ul', array('class' => 'nav'));
if (Event::handle('StartPersonalGroupNav', array($this))) {
$this->out->menuItem(common_local_url('all', array('nickname' =>
- $nickname)),
- // TRANS: Personal group navigation menu option when logged in for viewing timeline of self and friends.
- _m('MENU','Personal'),
- // TRANS: Tooltop for personal group navigation menu option when logged in for viewing timeline of self and friends.
- sprintf(_('%s and friends'), $name),
- $action == 'all', 'nav_timeline_personal');
- $this->out->menuItem(common_local_url('replies', array('nickname' =>
- $nickname)),
- // TRANS: Personal group navigation menu option when logged in for viewing @-replies.
- _m('MENU','Replies'),
- // TRANS: Tooltip for personal group navigation menu option when logged in for viewing @-replies.
- sprintf(_('Replies to %s'), $name),
- $action == 'replies', 'nav_timeline_replies');
+ $nickname)),
+ _('Home'),
+ sprintf(_('%s and friends'), $name),
+ $mine && $action =='all', 'nav_timeline_personal');
$this->out->menuItem(common_local_url('showstream', array('nickname' =>
- $nickname)),
- // TRANS: Personal group navigation menu option when logged in for seeing own profile.
- _m('MENU','Profile'),
- $name,
- $action == 'showstream', 'nav_profile');
+ $nickname)),
+ _('Profile'),
+ _('Your profile'),
+ $mine && $action =='showstream',
+ 'nav_profile');
+ $this->out->menuItem(common_local_url('replies', array('nickname' =>
+ $nickname)),
+ _('Replies'),
+ sprintf(_('Replies to %s'), $name),
+ $mine && $action =='replies', 'nav_timeline_replies');
$this->out->menuItem(common_local_url('showfavorites', array('nickname' =>
- $nickname)),
- // TRANS: Personal group navigation menu option when logged in for viewing own favourited notices.
- _m('MENU','Favorites'),
- // TRANS: Tooltip for personal group navigation menu option when logged in for viewing own favourited notices.
- sprintf(_('%s\'s favorite notices'), ($user_profile) ? $name : _('User')),
- $action == 'showfavorites', 'nav_timeline_favorites');
+ $nickname)),
+ _('Favorites'),
+ sprintf(_('%s\'s favorite notices'), ($user_profile) ? $name : _('User')),
+ $mine && $action =='showfavorites', 'nav_timeline_favorites');
$cur = common_current_user();
@@ -128,20 +97,12 @@ class PersonalGroupNav extends Widget
!common_config('singleuser', 'enabled')) {
$this->out->menuItem(common_local_url('inbox', array('nickname' =>
- $nickname)),
- // TRANS: Personal group navigation menu option when logged in for viewing recieved personal messages.
- _m('MENU','Inbox'),
- // TRANS: Tooltip for personal group navigation menu option when logged in for viewing recieved personal messages.
- _('Your incoming messages'),
- $action == 'inbox');
- $this->out->menuItem(common_local_url('outbox', array('nickname' =>
- $nickname)),
- // TRANS: Personal group navigation menu option when logged in for viewing senet personal messages.
- _m('MENU','Outbox'),
- // TRANS: Tooltip for personal group navigation menu option when logged in for viewing senet personal messages.
- _('Your sent messages'),
- $action == 'outbox');
+ $nickname)),
+ _('Messages'),
+ _('Your incoming messages'),
+ $mine && $action =='inbox');
}
+
Event::handle('EndPersonalGroupNav', array($this));
}
$this->out->elementEnd('ul');
diff --git a/lib/pgsqlschema.php b/lib/pgsqlschema.php
index 272f7eff68..d50e35f660 100644
--- a/lib/pgsqlschema.php
+++ b/lib/pgsqlschema.php
@@ -42,6 +42,7 @@ if (!defined('STATUSNET')) {
* @package StatusNet
* @author Evan Prodromou
* @author Brenda Wallace
+ * @author Brion Vibber
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@@ -50,167 +51,209 @@ class PgsqlSchema extends Schema
{
/**
- * Returns a TableDef object for the table
+ * Returns a table definition array for the table
* in the schema with the given name.
*
* Throws an exception if the table is not found.
*
- * @param string $name Name of the table to get
+ * @param string $table Name of the table to get
*
- * @return TableDef tabledef for that table.
+ * @return array tabledef for that table.
*/
- public function getTableDef($name)
+ public function getTableDef($table)
{
- $res = $this->conn->query("SELECT *, column_default as default, is_nullable as Null,
- udt_name as Type, column_name AS Field from INFORMATION_SCHEMA.COLUMNS where table_name = '$name'");
+ $def = array();
+ $hasKeys = false;
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
+ // Pull column data from INFORMATION_SCHEMA
+ $columns = $this->fetchMetaInfo($table, 'columns', 'ordinal_position');
+ if (count($columns) == 0) {
+ throw new SchemaTableMissingException("No such table: $table");
}
- $td = new TableDef();
+ // We'll need to match up fields by ordinal reference
+ $orderedFields = array();
- $td->name = $name;
- $td->columns = array();
+ foreach ($columns as $row) {
- if ($res->numRows() == 0 ) {
- throw new Exception('no such table'); //pretend to be the msyql error. yeah, this sucks.
+ $name = $row['column_name'];
+ $orderedFields[$row['ordinal_position']] = $name;
+
+ $field = array();
+ $field['type'] = $row['udt_name'];
+
+ if ($type == 'char' || $type == 'varchar') {
+ if ($row['character_maximum_length'] !== null) {
+ $field['length'] = intval($row['character_maximum_length']);
+ }
+ }
+ if ($type == 'numeric') {
+ // Other int types may report these values, but they're irrelevant.
+ // Just ignore them!
+ if ($row['numeric_precision'] !== null) {
+ $field['precision'] = intval($row['numeric_precision']);
+ }
+ if ($row['numeric_scale'] !== null) {
+ $field['scale'] = intval($row['numeric_scale']);
+ }
+ }
+ if ($row['is_nullable'] == 'NO') {
+ $field['not null'] = true;
+ }
+ if ($row['column_default'] !== null) {
+ $field['default'] = $row['column_default'];
+ if ($this->isNumericType($type)) {
+ $field['default'] = intval($field['default']);
+ }
+ }
+
+ $def['fields'][$name] = $field;
}
- $row = array();
- while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
- $cd = new ColumnDef();
+ // Pulling index info from pg_class & pg_index
+ // This can give us primary & unique key info, but not foreign key constraints
+ // so we exclude them and pick them up later.
+ $indexInfo = $this->getIndexInfo($table);
+ foreach ($indexInfo as $row) {
+ $keyName = $row['key_name'];
- $cd->name = $row['field'];
+ // Dig the column references out!
+ //
+ // These are inconvenient arrays with partial references to the
+ // pg_att table, but since we've already fetched up the column
+ // info on the current table, we can look those up locally.
+ $cols = array();
+ $colPositions = explode(' ', $row['indkey']);
+ foreach ($colPositions as $ord) {
+ if ($ord == 0) {
+ $cols[] = 'FUNCTION'; // @fixme
+ } else {
+ $cols[] = $orderedFields[$ord];
+ }
+ }
- $packed = $row['type'];
+ $def['indexes'][$keyName] = $cols;
+ }
- if (preg_match('/^(\w+)\((\d+)\)$/', $packed, $match)) {
- $cd->type = $match[1];
- $cd->size = $match[2];
+ // Pull constraint data from INFORMATION_SCHEMA:
+ // Primary key, unique keys, foreign keys
+ $keyColumns = $this->fetchMetaInfo($table, 'key_column_usage', 'constraint_name,ordinal_position');
+ $keys = array();
+
+ foreach ($keyColumns as $row) {
+ $keyName = $row['constraint_name'];
+ $keyCol = $row['column_name'];
+ if (!isset($keys[$keyName])) {
+ $keys[$keyName] = array();
+ }
+ $keys[$keyName][] = $keyCol;
+ }
+
+ foreach ($keys as $keyName => $cols) {
+ // name hack -- is this reliable?
+ if ($keyName == "{$table}_pkey") {
+ $def['primary key'] = $cols;
+ } else if (preg_match("/^{$table}_(.*)_fkey$/", $keyName, $matches)) {
+ $fkey = $this->getForeignKeyInfo($table, $keyName);
+ $colMap = array_combine($cols, $fkey['col_names']);
+ $def['foreign keys'][$keyName] = array($fkey['table_name'], $colMap);
} else {
- $cd->type = $packed;
+ $def['unique keys'][$keyName] = $cols;
}
-
- $cd->nullable = ($row['null'] == 'YES') ? true : false;
- $cd->key = $row['Key'];
- $cd->default = $row['default'];
- $cd->extra = $row['Extra'];
-
- $td->columns[] = $cd;
}
- return $td;
+ return $def;
}
/**
- * Gets a ColumnDef object for a single column.
+ * Pull some INFORMATION.SCHEMA data for the given table.
*
- * Throws an exception if the table is not found.
- *
- * @param string $table name of the table
- * @param string $column name of the column
- *
- * @return ColumnDef definition of the column or null
- * if not found.
+ * @param string $table
+ * @return array of arrays
*/
-
- public function getColumnDef($table, $column)
+ function fetchMetaInfo($table, $infoTable, $orderBy=null)
{
- $td = $this->getTableDef($table);
-
- foreach ($td->columns as $cd) {
- if ($cd->name == $column) {
- return $cd;
- }
+ $query = "SELECT * FROM information_schema.%s " .
+ "WHERE table_name='%s'";
+ $sql = sprintf($query, $infoTable, $table);
+ if ($orderBy) {
+ $sql .= ' ORDER BY ' . $orderBy;
}
-
- return null;
+ return $this->fetchQueryData($sql);
}
/**
- * Creates a table with the given names and columns.
- *
- * @param string $name Name of the table
- * @param array $columns Array of ColumnDef objects
- * for new table.
- *
- * @return boolean success flag
+ * Pull some PG-specific index info
+ * @param string $table
+ * @return array of arrays
*/
-
- public function createTable($name, $columns)
+ function getIndexInfo($table)
{
- $uniques = array();
- $primary = array();
- $indices = array();
- $onupdate = array();
-
- $sql = "CREATE TABLE $name (\n";
-
- for ($i = 0; $i < count($columns); $i++) {
-
- $cd =& $columns[$i];
-
- if ($i > 0) {
- $sql .= ",\n";
- }
-
- $sql .= $this->_columnSql($cd);
- switch ($cd->key) {
- case 'UNI':
- $uniques[] = $cd->name;
- break;
- case 'PRI':
- $primary[] = $cd->name;
- break;
- case 'MUL':
- $indices[] = $cd->name;
- break;
- }
- }
-
- if (count($primary) > 0) { // it really should be...
- $sql .= ",\n PRIMARY KEY (" . implode(',', $primary) . ")";
- }
-
- $sql .= "); ";
-
-
- foreach ($uniques as $u) {
- $sql .= "\n CREATE index {$name}_{$u}_idx ON {$name} ($u); ";
- }
-
- foreach ($indices as $i) {
- $sql .= "CREATE index {$name}_{$i}_idx ON {$name} ($i)";
- }
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage(). ' SQL was '. $sql);
- }
-
- return true;
+ $query = 'SELECT ' .
+ '(SELECT relname FROM pg_class WHERE oid=indexrelid) AS key_name, ' .
+ '* FROM pg_index ' .
+ 'WHERE indrelid=(SELECT oid FROM pg_class WHERE relname=\'%s\') ' .
+ 'AND indisprimary=\'f\' AND indisunique=\'f\' ' .
+ 'ORDER BY indrelid, indexrelid';
+ $sql = sprintf($query, $table);
+ return $this->fetchQueryData($sql);
}
/**
- * Drops a table from the schema
- *
- * Throws an exception if the table is not found.
- *
- * @param string $name Name of the table to drop
- *
- * @return boolean success flag
+ * Column names from the foreign table can be resolved with a call to getTableColumnNames()
+ * @param $table
+ * @return array array of rows with keys: fkey_name, table_name, table_id, col_names (array of strings)
*/
-
- public function dropTable($name)
+ function getForeignKeyInfo($table, $constraint_name)
{
- $res = $this->conn->query("DROP TABLE $name");
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
+ // In a sane world, it'd be easier to query the column names directly.
+ // But it's pretty hard to work with arrays such as col_indexes in direct SQL here.
+ $query = 'SELECT ' .
+ '(SELECT relname FROM pg_class WHERE oid=confrelid) AS table_name, ' .
+ 'confrelid AS table_id, ' .
+ '(SELECT indkey FROM pg_index WHERE indexrelid=conindid) AS col_indexes ' .
+ 'FROM pg_constraint ' .
+ 'WHERE conrelid=(SELECT oid FROM pg_class WHERE relname=\'%s\') ' .
+ 'AND conname=\'%s\' ' .
+ 'AND contype=\'f\'';
+ $sql = sprintf($query, $table, $constraint_name);
+ $data = $this->fetchQueryData($sql);
+ if (count($data) < 1) {
+ throw new Exception("Could not find foreign key " . $constraint_name . " on table " . $table);
}
- return true;
+ $row = $data[0];
+ return array(
+ 'table_name' => $row['table_name'],
+ 'col_names' => $this->getTableColumnNames($row['table_id'], $row['col_indexes'])
+ );
+ }
+
+ /**
+ *
+ * @param int $table_id
+ * @param array $col_indexes
+ * @return array of strings
+ */
+ function getTableColumnNames($table_id, $col_indexes)
+ {
+ $indexes = array_map('intval', explode(' ', $col_indexes));
+ $query = 'SELECT attnum AS col_index, attname AS col_name ' .
+ 'FROM pg_attribute where attrelid=%d ' .
+ 'AND attnum IN (%s)';
+ $sql = sprintf($query, $table_id, implode(',', $indexes));
+ $data = $this->fetchQueryData($sql);
+
+ $byId = array();
+ foreach ($data as $row) {
+ $byId[$row['col_index']] = $row['col_name'];
+ }
+
+ $out = array();
+ foreach ($indexes as $id) {
+ $out[] = $byId[$id];
+ }
+ return $out;
}
/**
@@ -229,262 +272,6 @@ class PgsqlSchema extends Schema
return $type;
}
- /**
- * Adds an index to a table.
- *
- * If no name is provided, a name will be made up based
- * on the table name and column names.
- *
- * Throws an exception on database error, esp. if the table
- * does not exist.
- *
- * @param string $table Name of the table
- * @param array $columnNames Name of columns to index
- * @param string $name (Optional) name of the index
- *
- * @return boolean success flag
- */
-
- public function createIndex($table, $columnNames, $name=null)
- {
- if (!is_array($columnNames)) {
- $columnNames = array($columnNames);
- }
-
- if (empty($name)) {
- $name = "$table_".implode("_", $columnNames)."_idx";
- }
-
- $res = $this->conn->query("ALTER TABLE $table ".
- "ADD INDEX $name (".
- implode(",", $columnNames).")");
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Drops a named index from a table.
- *
- * @param string $table name of the table the index is on.
- * @param string $name name of the index
- *
- * @return boolean success flag
- */
-
- public function dropIndex($table, $name)
- {
- $res = $this->conn->query("ALTER TABLE $table DROP INDEX $name");
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Adds a column to a table
- *
- * @param string $table name of the table
- * @param ColumnDef $columndef Definition of the new
- * column.
- *
- * @return boolean success flag
- */
-
- public function addColumn($table, $columndef)
- {
- $sql = "ALTER TABLE $table ADD COLUMN " . $this->_columnSql($columndef);
-
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Modifies a column in the schema.
- *
- * The name must match an existing column and table.
- *
- * @param string $table name of the table
- * @param ColumnDef $columndef new definition of the column.
- *
- * @return boolean success flag
- */
-
- public function modifyColumn($table, $columndef)
- {
- $sql = "ALTER TABLE $table ALTER COLUMN TYPE " .
- $this->_columnSql($columndef);
-
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Drops a column from a table
- *
- * The name must match an existing column.
- *
- * @param string $table name of the table
- * @param string $columnName name of the column to drop
- *
- * @return boolean success flag
- */
-
- public function dropColumn($table, $columnName)
- {
- $sql = "ALTER TABLE $table DROP COLUMN $columnName";
-
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Ensures that a table exists with the given
- * name and the given column definitions.
- *
- * If the table does not yet exist, it will
- * create the table. If it does exist, it will
- * alter the table to match the column definitions.
- *
- * @param string $tableName name of the table
- * @param array $columns array of ColumnDef
- * objects for the table
- *
- * @return boolean success flag
- */
-
- public function ensureTable($tableName, $columns)
- {
- // XXX: DB engine portability -> toilet
-
- try {
- $td = $this->getTableDef($tableName);
-
- } catch (Exception $e) {
- if (preg_match('/no such table/', $e->getMessage())) {
- return $this->createTable($tableName, $columns);
- } else {
- throw $e;
- }
- }
-
- $cur = $this->_names($td->columns);
- $new = $this->_names($columns);
-
- $toadd = array_diff($new, $cur);
- $todrop = array_diff($cur, $new);
- $same = array_intersect($new, $cur);
- $tomod = array();
- foreach ($same as $m) {
- $curCol = $this->_byName($td->columns, $m);
- $newCol = $this->_byName($columns, $m);
-
-
- if (!$newCol->equals($curCol)) {
- // BIG GIANT TODO!
- // stop it detecting different types and trying to modify on every page request
-// $tomod[] = $newCol->name;
- }
- }
- if (count($toadd) + count($todrop) + count($tomod) == 0) {
- // nothing to do
- return true;
- }
-
- // For efficiency, we want this all in one
- // query, instead of using our methods.
-
- $phrase = array();
-
- foreach ($toadd as $columnName) {
- $cd = $this->_byName($columns, $columnName);
-
- $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
- }
-
- foreach ($todrop as $columnName) {
- $phrase[] = 'DROP COLUMN ' . $columnName;
- }
-
- foreach ($tomod as $columnName) {
- $cd = $this->_byName($columns, $columnName);
-
- /* brute force */
- $phrase[] = 'DROP COLUMN ' . $columnName;
- $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
- }
-
- $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
- }
-
- return true;
- }
-
- /**
- * Returns the array of names from an array of
- * ColumnDef objects.
- *
- * @param array $cds array of ColumnDef objects
- *
- * @return array strings for name values
- */
-
- private function _names($cds)
- {
- $names = array();
-
- foreach ($cds as $cd) {
- $names[] = $cd->name;
- }
-
- return $names;
- }
-
- /**
- * Get a ColumnDef from an array matching
- * name.
- *
- * @param array $cds Array of ColumnDef objects
- * @param string $name Name of the column
- *
- * @return ColumnDef matching item or null if no match.
- */
-
- private function _byName($cds, $name)
- {
- foreach ($cds as $cd) {
- if ($cd->name == $name) {
- return $cd;
- }
- }
-
- return null;
- }
-
/**
* Return the proper SQL for creating or
* altering a column.
@@ -492,41 +279,177 @@ class PgsqlSchema extends Schema
* Appropriate for use in CREATE TABLE or
* ALTER TABLE statements.
*
- * @param ColumnDef $cd column to create
+ * @param array $cd column to create
*
* @return string correct SQL for that column
*/
- private function _columnSql($cd)
+
+ function columnSql(array $cd)
{
- $sql = "{$cd->name} ";
- $type = $this->_columnTypeTranslation($cd->type);
+ $line = array();
+ $line[] = parent::columnSql($cd);
- //handle those mysql enum fields that postgres doesn't support
- if (preg_match('!^enum!', $type)) {
- $allowed_values = preg_replace('!^enum!', '', $type);
- $sql .= " text check ({$cd->name} in $allowed_values)";
- return $sql;
+ /*
+ if ($table['foreign keys'][$name]) {
+ foreach ($table['foreign keys'][$name] as $foreignTable => $foreignColumn) {
+ $line[] = 'references';
+ $line[] = $this->quoteIdentifier($foreignTable);
+ $line[] = '(' . $this->quoteIdentifier($foreignColumn) . ')';
+ }
}
- if (!empty($cd->auto_increment)) {
- $type = "bigserial"; // FIXME: creates the wrong name for the sequence for some internal sequence-lookup function, so better fix this to do the real 'create sequence' dance.
+ */
+
+ return implode(' ', $line);
+ }
+
+ /**
+ * Append phrase(s) to an array of partial ALTER TABLE chunks in order
+ * to alter the given column from its old state to a new one.
+ *
+ * @param array $phrase
+ * @param string $columnName
+ * @param array $old previous column definition as found in DB
+ * @param array $cd current column definition
+ */
+ function appendAlterModifyColumn(array &$phrase, $columnName, array $old, array $cd)
+ {
+ $prefix = 'ALTER COLUMN ' . $this->quoteIdentifier($columnName) . ' ';
+
+ $oldType = $this->mapType($old);
+ $newType = $this->mapType($cd);
+ if ($oldType != $newType) {
+ $phrase[] = $prefix . 'TYPE ' . $newType;
}
- if (!empty($cd->size)) {
- $sql .= "{$type}({$cd->size}) ";
+ if (!empty($old['not null']) && empty($cd['not null'])) {
+ $phrase[] = $prefix . 'DROP NOT NULL';
+ } else if (empty($old['not null']) && !empty($cd['not null'])) {
+ $phrase[] = $prefix . 'SET NOT NULL';
+ }
+
+ if (isset($old['default']) && !isset($cd['default'])) {
+ $phrase[] = $prefix . 'DROP DEFAULT';
+ } else if (!isset($old['default']) && isset($cd['default'])) {
+ $phrase[] = $prefix . 'SET DEFAULT ' . $this->quoteDefaultValue($cd);
+ }
+ }
+
+ /**
+ * Append an SQL statement to drop an index from a table.
+ * Note that in PostgreSQL, index names are DB-unique.
+ *
+ * @param array $statements
+ * @param string $table
+ * @param string $name
+ * @param array $def
+ */
+ function appendDropIndex(array &$statements, $table, $name)
+ {
+ $statements[] = "DROP INDEX $name";
+ }
+
+ /**
+ * Quote a db/table/column identifier if necessary.
+ *
+ * @param string $name
+ * @return string
+ */
+ function quoteIdentifier($name)
+ {
+ return $this->conn->quoteIdentifier($name);
+ }
+
+ function mapType($column)
+ {
+ $map = array('serial' => 'bigserial', // FIXME: creates the wrong name for the sequence for some internal sequence-lookup function, so better fix this to do the real 'create sequence' dance.
+ 'numeric' => 'decimal',
+ 'datetime' => 'timestamp',
+ 'blob' => 'bytea');
+
+ $type = $column['type'];
+ if (isset($map[$type])) {
+ $type = $map[$type];
+ }
+
+ if ($type == 'int') {
+ if (!empty($column['size'])) {
+ $size = $column['size'];
+ if ($size == 'small') {
+ return 'int2';
+ } else if ($size == 'big') {
+ return 'int8';
+ }
+ }
+ return 'int4';
+ }
+
+ return $type;
+ }
+
+ // @fixme need name... :P
+ function typeAndSize($column)
+ {
+ if ($column['type'] == 'enum') {
+ $vals = array_map(array($this, 'quote'), $column['enum']);
+ return "text check ($name in " . implode(',', $vals) . ')';
} else {
- $sql .= "{$type} ";
+ return parent::typeAndSize($column);
}
+ }
- if (!empty($cd->default)) {
- $sql .= "default {$cd->default} ";
- } else {
- $sql .= ($cd->nullable) ? "null " : "not null ";
+ /**
+ * Filter the given table definition array to match features available
+ * in this database.
+ *
+ * This lets us strip out unsupported things like comments, foreign keys,
+ * or type variants that we wouldn't get back from getTableDef().
+ *
+ * @param array $tableDef
+ */
+ function filterDef(array $tableDef)
+ {
+ foreach ($tableDef['fields'] as $name => &$col) {
+ // No convenient support for field descriptions
+ unset($col['description']);
+
+ /*
+ if (isset($col['size'])) {
+ // Don't distinguish between tinyint and int.
+ if ($col['size'] == 'tiny' && $col['type'] == 'int') {
+ unset($col['size']);
+ }
+ }
+ */
+ $col['type'] = $this->mapType($col);
+ unset($col['size']);
}
+ if (!empty($tableDef['primary key'])) {
+ $tableDef['primary key'] = $this->filterKeyDef($tableDef['primary key']);
+ }
+ if (!empty($tableDef['unique keys'])) {
+ foreach ($tableDef['unique keys'] as $i => $def) {
+ $tableDef['unique keys'][$i] = $this->filterKeyDef($def);
+ }
+ }
+ return $tableDef;
+ }
-// if (!empty($cd->extra)) {
-// $sql .= "{$cd->extra} ";
-// }
-
- return $sql;
+ /**
+ * Filter the given key/index definition to match features available
+ * in this database.
+ *
+ * @param array $def
+ * @return array
+ */
+ function filterKeyDef(array $def)
+ {
+ // PostgreSQL doesn't like prefix lengths specified on keys...?
+ foreach ($def as $i => $item)
+ {
+ if (is_array($item)) {
+ $def[$i] = $item[0];
+ }
+ }
+ return $def;
}
}
diff --git a/lib/plugindisableform.php b/lib/plugindisableform.php
new file mode 100644
index 0000000000..3cbabdb2ce
--- /dev/null
+++ b/lib/plugindisableform.php
@@ -0,0 +1,93 @@
+.
+ *
+ * @category Form
+ * @package StatusNet
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Form for joining a group
+ *
+ * @category Form
+ * @package StatusNet
+ * @author Brion Vibber
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ * @see PluginEnableForm
+ */
+
+class PluginDisableForm extends PluginEnableForm
+{
+ /**
+ * ID of the form
+ *
+ * @return string ID of the form
+ */
+
+ function id()
+ {
+ return 'plugin-disable-' . $this->plugin;
+ }
+
+ /**
+ * class of the form
+ *
+ * @return string of the form class
+ */
+
+ function formClass()
+ {
+ return 'form_plugin_disable';
+ }
+
+ /**
+ * Action of the form
+ *
+ * @return string URL of the action
+ */
+
+ function action()
+ {
+ return common_local_url('plugindisable',
+ array('plugin' => $this->plugin));
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ // TRANS: Plugin admin panel controls
+ $this->out->submit('submit', _m('plugin', 'Disable'));
+ }
+
+}
diff --git a/lib/pluginenableform.php b/lib/pluginenableform.php
new file mode 100644
index 0000000000..8683ffd0be
--- /dev/null
+++ b/lib/pluginenableform.php
@@ -0,0 +1,114 @@
+.
+ *
+ * @category Form
+ * @package StatusNet
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+require_once INSTALLDIR.'/lib/form.php';
+
+/**
+ * Form for joining a group
+ *
+ * @category Form
+ * @package StatusNet
+ * @author Brion Vibber
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ * @see PluginDisableForm
+ */
+
+class PluginEnableForm extends Form
+{
+ /**
+ * Plugin to enable/disable
+ */
+
+ var $plugin = null;
+
+ /**
+ * Constructor
+ *
+ * @param HTMLOutputter $out output channel
+ * @param string $plugin plugin to enable/disable
+ */
+
+ function __construct($out=null, $plugin=null)
+ {
+ parent::__construct($out);
+
+ $this->plugin = $plugin;
+ }
+
+ /**
+ * ID of the form
+ *
+ * @return string ID of the form
+ */
+
+ function id()
+ {
+ return 'plugin-enable-' . $this->plugin;
+ }
+
+ /**
+ * class of the form
+ *
+ * @return string of the form class
+ */
+
+ function formClass()
+ {
+ return 'form_plugin_enable';
+ }
+
+ /**
+ * Action of the form
+ *
+ * @return string URL of the action
+ */
+
+ function action()
+ {
+ return common_local_url('pluginenable',
+ array('plugin' => $this->plugin));
+ }
+
+ /**
+ * Action elements
+ *
+ * @return void
+ */
+
+ function formActions()
+ {
+ // TRANS: Plugin admin panel controls
+ $this->out->submit('submit', _m('plugin', 'Enable'));
+ }
+}
diff --git a/lib/pluginlist.php b/lib/pluginlist.php
new file mode 100644
index 0000000000..07a17ba397
--- /dev/null
+++ b/lib/pluginlist.php
@@ -0,0 +1,213 @@
+.
+ *
+ * @category Settings
+ * @package StatusNet
+ * @author Brion Vibber
+ * @copyright 2010 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+require INSTALLDIR . "/lib/pluginenableform.php";
+require INSTALLDIR . "/lib/plugindisableform.php";
+
+/**
+ * Plugin list
+ *
+ * @category Admin
+ * @package StatusNet
+ * @author Brion Vibber
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+class PluginList extends Widget
+{
+ var $plugins = array();
+
+ function __construct($plugins, $out)
+ {
+ parent::__construct($out);
+ $this->plugins = $plugins;
+ }
+
+ function show()
+ {
+ $this->startList();
+ $this->showPlugins();
+ $this->endList();
+ }
+
+ function startList()
+ {
+ $this->out->elementStart('table', 'plugin_list');
+ }
+
+ function endList()
+ {
+ $this->out->elementEnd('table');
+ }
+
+ function showPlugins()
+ {
+ foreach ($this->plugins as $plugin) {
+ $pli = $this->newListItem($plugin);
+ $pli->show();
+ }
+ }
+
+ function newListItem($plugin)
+ {
+ return new PluginListItem($plugin, $this->out);
+ }
+}
+
+class PluginListItem extends Widget
+{
+ /** Current plugin. */
+ var $plugin = null;
+
+ /** Local cache for plugin version info */
+ protected static $versions = false;
+
+ function __construct($plugin, $out)
+ {
+ parent::__construct($out);
+ $this->plugin = $plugin;
+ }
+
+ function show()
+ {
+ $meta = $this->metaInfo();
+
+ $this->out->elementStart('tr', array('id' => 'plugin-' . $this->plugin));
+
+ // Name and controls
+ $this->out->elementStart('td');
+ $this->out->elementStart('div');
+ if (!empty($meta['homepage'])) {
+ $this->out->elementStart('a', array('href' => $meta['homepage']));
+ }
+ $this->out->text($this->plugin);
+ if (!empty($meta['homepage'])) {
+ $this->out->elementEnd('a');
+ }
+ $this->out->elementEnd('div');
+
+ $form = $this->getControlForm();
+ $form->show();
+
+ $this->out->elementEnd('td');
+
+ // Version and authors
+ $this->out->elementStart('td');
+ if (!empty($meta['version'])) {
+ $this->out->elementStart('div');
+ $this->out->text($meta['version']);
+ $this->out->elementEnd('div');
+ }
+ if (!empty($meta['author'])) {
+ $this->out->elementStart('div');
+ $this->out->text($meta['author']);
+ $this->out->elementEnd('div');
+ }
+ $this->out->elementEnd('td');
+
+ // Description
+ $this->out->elementStart('td');
+ if (!empty($meta['rawdescription'])) {
+ $this->out->raw($meta['rawdescription']);
+ }
+ $this->out->elementEnd('td');
+
+ $this->out->elementEnd('tr');
+ }
+
+ /**
+ * Pull up the appropriate control form for this plugin, depending
+ * on its current state.
+ *
+ * @return Form
+ */
+ protected function getControlForm()
+ {
+ $key = 'disable-' . $this->plugin;
+ if (common_config('plugins', $key)) {
+ return new PluginEnableForm($this->out, $this->plugin);
+ } else {
+ return new PluginDisableForm($this->out, $this->plugin);
+ }
+ }
+
+ /**
+ * Grab metadata about this plugin...
+ * Warning: horribly inefficient and may explode!
+ * Doesn't work for disabled plugins either.
+ *
+ * @fixme pull structured data from plugin source
+ */
+ function metaInfo()
+ {
+ $versions = self::getPluginVersions();
+ $found = false;
+
+ foreach ($versions as $info) {
+ // hack for URL shorteners... "LilUrl (ur1.ca)" etc
+ list($name, ) = explode(' ', $info['name']);
+
+ if ($name == $this->plugin) {
+ if ($found) {
+ // hack for URL shorteners...
+ $found['rawdescription'] .= " \n" . $info['rawdescription'];
+ } else {
+ $found = $info;
+ }
+ }
+ }
+
+ if ($found) {
+ return $found;
+ } else {
+ return array('name' => $this->plugin,
+ 'rawdescription' => _m('plugin-description',
+ '(Plugin descriptions unavailable when disabled.)'));
+ }
+ }
+
+ /**
+ * Lazy-load the set of active plugin version info
+ * @return array
+ */
+ protected static function getPluginVersions()
+ {
+ if (!is_array(self::$versions)) {
+ $versions = array();
+ Event::handle('PluginVersion', array(&$versions));
+ self::$versions = $versions;
+ }
+ return self::$versions;
+ }
+}
diff --git a/lib/primarynav.php b/lib/primarynav.php
new file mode 100644
index 0000000000..7748d7a1c7
--- /dev/null
+++ b/lib/primarynav.php
@@ -0,0 +1,94 @@
+.
+ *
+ * @category Menu
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Primary, top-level menu
+ *
+ * @category General
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class PrimaryNav extends Menu
+{
+ function show()
+ {
+ $user = common_current_user();
+ $this->action->elementStart('ul', array('class' => 'nav'));
+ if (Event::handle('StartPrimaryNav', array($this->action))) {
+ if (!empty($user)) {
+ $this->action->menuItem(common_local_url('profilesettings'),
+ _m('Settings'),
+ _m('Change your personal settings'),
+ false,
+ 'nav_account');
+ if ($user->hasRight(Right::CONFIGURESITE)) {
+ $this->action->menuItem(common_local_url('siteadminpanel'),
+ _m('Admin'),
+ _m('Site configuration'),
+ false,
+ 'nav_admin');
+ }
+ $this->action->menuItem(common_local_url('logout'),
+ _m('Logout'),
+ _m('Logout from the site'),
+ false,
+ 'nav_logout');
+ } else {
+ $this->action->menuItem(common_local_url('login'),
+ _m('Login'),
+ _m('Login to the site'),
+ false,
+ 'nav_login');
+ }
+
+ if (!empty($user) || !common_config('site', 'private')) {
+ $this->action->menuItem(common_local_url('noticesearch'),
+ _m('Search'),
+ _m('Search the site'),
+ false,
+ 'nav_search');
+ }
+
+ Event::handle('EndPrimaryNav', array($this->action));
+ }
+
+ $this->action->elementEnd('ul');
+ }
+}
diff --git a/lib/profileblock.php b/lib/profileblock.php
new file mode 100644
index 0000000000..19e5a386ba
--- /dev/null
+++ b/lib/profileblock.php
@@ -0,0 +1,116 @@
+.
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Class comment
+ *
+ * @category General
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+abstract class ProfileBlock extends Widget
+{
+ abstract function avatar();
+ abstract function name();
+ abstract function url();
+ abstract function location();
+ abstract function homepage();
+ abstract function description();
+
+ function show()
+ {
+ $this->out->elementStart('div', 'profile_block section');
+
+ $size = $this->avatarSize();
+
+ $this->out->element('img', array('src' => $this->avatar(),
+ 'class' => 'profile_block_avatar',
+ 'alt' => $this->name(),
+ 'width' => $size,
+ 'height' => $size));
+
+ $name = $this->name();
+
+ if (!empty($name)) {
+ $this->out->elementStart('p', 'profile_block_name');
+ $url = $this->url();
+ if (!empty($url)) {
+ $this->out->element('a', array('href' => $url),
+ $name);
+ } else {
+ $this->out->text($name);
+ }
+ $this->out->elementEnd('p');
+ }
+
+ $location = $this->location();
+
+ if (!empty($location)) {
+ $this->out->element('p', 'profile_block_location', $location);
+ }
+
+ $homepage = $this->homepage();
+
+ if (!empty($homepage)) {
+ $this->out->element('a', 'profile_block_homepage', $homepage);
+ }
+
+ $description = $this->description();
+
+ if (!empty($description)) {
+ $this->out->element('p',
+ 'profile_block_description',
+ $description);
+ }
+
+ $this->showActions();
+
+ $this->out->elementEnd('div');
+ }
+
+ function avatarSize()
+ {
+ return AVATAR_PROFILE_SIZE;
+ }
+
+ function showActions()
+ {
+ }
+}
diff --git a/lib/publicgroupnav.php b/lib/publicgroupnav.php
index ae9cbdebb4..bd2ad53124 100644
--- a/lib/publicgroupnav.php
+++ b/lib/publicgroupnav.php
@@ -46,22 +46,8 @@ require_once INSTALLDIR.'/lib/widget.php';
* @see Widget
*/
-class PublicGroupNav extends Widget
+class PublicGroupNav extends Menu
{
- var $action = null;
-
- /**
- * Construction
- *
- * @param Action $action current action, used for output
- */
-
- function __construct($action=null)
- {
- parent::__construct($action);
- $this->action = $action;
- }
-
/**
* Show the menu
*
diff --git a/lib/queuehandler.php b/lib/queuehandler.php
index 2909cd83b1..2194dd1618 100644
--- a/lib/queuehandler.php
+++ b/lib/queuehandler.php
@@ -36,20 +36,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
class QueueHandler
{
- /**
- * Return transport keyword which identifies items this queue handler
- * services; must be defined for all subclasses.
- *
- * Must be 8 characters or less to fit in the queue_item database.
- * ex "email", "jabber", "sms", "irc", ...
- *
- * @return string
- */
- function transport()
- {
- return null;
- }
-
/**
* Here's the meat of your queue handler -- you're handed a Notice
* or other object, which you may do as you will with.
diff --git a/lib/queuemanager.php b/lib/queuemanager.php
index 5b59444bc2..1fa3417829 100644
--- a/lib/queuemanager.php
+++ b/lib/queuemanager.php
@@ -156,21 +156,14 @@ abstract class QueueManager extends IoManager
}
/**
- * Encode an object or variable for queued storage.
- * Notice objects are currently stored as an id reference;
- * other items are serialized.
+ * Encode an object for queued storage.
*
* @param mixed $item
* @return string
*/
protected function encode($item)
{
- if ($item instanceof Notice) {
- // Backwards compat
- return $item->id;
- } else {
- return serialize($item);
- }
+ return serialize($item);
}
/**
@@ -182,25 +175,7 @@ abstract class QueueManager extends IoManager
*/
protected function decode($frame)
{
- if (is_numeric($frame)) {
- // Back-compat for notices...
- return Notice::staticGet(intval($frame));
- } elseif (substr($frame, 0, 1) == '<') {
- // Back-compat for XML source
- return $frame;
- } else {
- // Deserialize!
- #$old = error_reporting();
- #error_reporting($old & ~E_NOTICE);
- $out = unserialize($frame);
- #error_reporting($old);
-
- if ($out === false && $frame !== 'b:0;') {
- common_log(LOG_ERR, "Couldn't unserialize queued frame: $frame");
- return false;
- }
- return $out;
- }
+ return unserialize($frame);
}
/**
@@ -274,16 +249,6 @@ abstract class QueueManager extends IoManager
// Broadcasting profile updates to OMB remote subscribers
$this->connect('profile', 'ProfileQueueHandler');
- // XMPP output handlers...
- if (common_config('xmpp', 'enabled')) {
- // Delivery prep, read by queuedaemon.php:
- $this->connect('jabber', 'JabberQueueHandler');
- $this->connect('public', 'PublicQueueHandler');
-
- // Raw output, read by xmppdaemon.php:
- $this->connect('xmppout', 'XmppOutQueueHandler', 'xmpp');
- }
-
// For compat with old plugins not registering their own handlers.
$this->connect('plugin', 'PluginQueueHandler');
}
diff --git a/lib/queuemonitor.php b/lib/queuemonitor.php
index 1c306a6298..3dc0ea65aa 100644
--- a/lib/queuemonitor.php
+++ b/lib/queuemonitor.php
@@ -36,7 +36,7 @@ class QueueMonitor
* Only explicitly listed thread/site/queue owners will be incremented.
*
* @param string $key counter name
- * @param array $owners list of owner keys like 'queue:jabber' or 'site:stat01'
+ * @param array $owners list of owner keys like 'queue:xmpp' or 'site:stat01'
*/
public function stats($key, $owners=array())
{
diff --git a/lib/router.php b/lib/router.php
index e956b02c63..ccc4b09781 100644
--- a/lib/router.php
+++ b/lib/router.php
@@ -257,7 +257,7 @@ class Router
// settings
foreach (array('profile', 'avatar', 'password', 'im', 'oauthconnections',
- 'oauthapps', 'email', 'sms', 'userdesign', 'other') as $s) {
+ 'oauthapps', 'email', 'sms', 'userdesign', 'url') as $s) {
$m->connect('settings/'.$s, array('action' => $s.'settings'));
}
@@ -407,6 +407,11 @@ class Router
// statuses API
+ $m->connect('api',
+ array('action' => 'Redirect',
+ 'nextAction' => 'doc',
+ 'args' => array('title' => 'api')));
+
$m->connect('api/statuses/public_timeline.:format',
array('action' => 'ApiTimelinePublic',
'format' => '(xml|json|rss|atom|as)'));
@@ -755,6 +760,12 @@ class Router
$m->connect('api/statusnet/groups/create.:format',
array('action' => 'ApiGroupCreate',
'format' => '(xml|json)'));
+
+ $m->connect('api/statusnet/groups/update/:id.:format',
+ array('action' => 'ApiGroupProfileUpdate',
+ 'id' => '[a-zA-Z0-9]+',
+ 'format' => '(xml|json)'));
+
// Tags
$m->connect('api/statusnet/tags/timeline/:tag.:format',
array('action' => 'ApiTimelineTag',
@@ -782,15 +793,23 @@ class Router
// Admin
- $m->connect('admin/site', array('action' => 'siteadminpanel'));
- $m->connect('admin/design', array('action' => 'designadminpanel'));
- $m->connect('admin/user', array('action' => 'useradminpanel'));
- $m->connect('admin/access', array('action' => 'accessadminpanel'));
- $m->connect('admin/paths', array('action' => 'pathsadminpanel'));
- $m->connect('admin/sessions', array('action' => 'sessionsadminpanel'));
- $m->connect('admin/sitenotice', array('action' => 'sitenoticeadminpanel'));
- $m->connect('admin/snapshot', array('action' => 'snapshotadminpanel'));
- $m->connect('admin/license', array('action' => 'licenseadminpanel'));
+ $m->connect('panel/site', array('action' => 'siteadminpanel'));
+ $m->connect('panel/design', array('action' => 'designadminpanel'));
+ $m->connect('panel/user', array('action' => 'useradminpanel'));
+ $m->connect('panel/access', array('action' => 'accessadminpanel'));
+ $m->connect('panel/paths', array('action' => 'pathsadminpanel'));
+ $m->connect('panel/sessions', array('action' => 'sessionsadminpanel'));
+ $m->connect('panel/sitenotice', array('action' => 'sitenoticeadminpanel'));
+ $m->connect('panel/snapshot', array('action' => 'snapshotadminpanel'));
+ $m->connect('panel/license', array('action' => 'licenseadminpanel'));
+
+ $m->connect('panel/plugins', array('action' => 'pluginsadminpanel'));
+ $m->connect('panel/plugins/enable/:plugin',
+ array('action' => 'pluginenable'),
+ array('plugin' => '[A-Za-z0-9_]+'));
+ $m->connect('panel/plugins/disable/:plugin',
+ array('action' => 'plugindisable'),
+ array('plugin' => '[A-Za-z0-9_]+'));
$m->connect('getfile/:filename',
array('action' => 'getfile'),
@@ -957,6 +976,12 @@ class Router
array('action' => 'AtomPubMembershipFeed'),
array('profile' => '[0-9]+'));
+ // URL shortening
+
+ $m->connect('url/:id',
+ array('action' => 'redirecturl',
+ 'id' => '[0-9]+'));
+
// user stuff
Event::handle('RouterInitialized', array($m));
diff --git a/lib/schema.php b/lib/schema.php
index e5def514e3..2e27955881 100644
--- a/lib/schema.php
+++ b/lib/schema.php
@@ -41,6 +41,7 @@ if (!defined('STATUSNET')) {
* @category Database
* @package StatusNet
* @author Evan Prodromou
+ * @author Brion Vibber
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@@ -118,65 +119,216 @@ class Schema
/**
* Creates a table with the given names and columns.
*
- * @param string $name Name of the table
- * @param array $columns Array of ColumnDef objects
- * for new table.
+ * @param string $tableName Name of the table
+ * @param array $def Table definition array listing fields and indexes.
*
* @return boolean success flag
*/
- public function createTable($name, $columns)
+ public function createTable($tableName, $def)
{
- $uniques = array();
- $primary = array();
- $indices = array();
+ $statements = $this->buildCreateTable($tableName, $def);
+ return $this->runSqlSet($statements);
+ }
- $sql = "CREATE TABLE $name (\n";
+ /**
+ * Build a set of SQL statements to create a table with the given
+ * name and columns.
+ *
+ * @param string $name Name of the table
+ * @param array $def Table definition array
+ *
+ * @return boolean success flag
+ */
+ public function buildCreateTable($name, $def)
+ {
+ $def = $this->validateDef($name, $def);
+ $def = $this->filterDef($def);
+ $sql = array();
- for ($i = 0; $i < count($columns); $i++) {
+ foreach ($def['fields'] as $col => $colDef) {
+ $this->appendColumnDef($sql, $col, $colDef);
+ }
- $cd =& $columns[$i];
+ // Primary, unique, and foreign keys are constraints, so go within
+ // the CREATE TABLE statement normally.
+ if (!empty($def['primary key'])) {
+ $this->appendPrimaryKeyDef($sql, $def['primary key']);
+ }
- if ($i > 0) {
- $sql .= ",\n";
- }
-
- $sql .= $this->_columnSql($cd);
-
- switch ($cd->key) {
- case 'UNI':
- $uniques[] = $cd->name;
- break;
- case 'PRI':
- $primary[] = $cd->name;
- break;
- case 'MUL':
- $indices[] = $cd->name;
- break;
+ if (!empty($def['unique keys'])) {
+ foreach ($def['unique keys'] as $col => $colDef) {
+ $this->appendUniqueKeyDef($sql, $col, $colDef);
}
}
- if (count($primary) > 0) { // it really should be...
- $sql .= ",\nconstraint primary key (" . implode(',', $primary) . ")";
+ if (!empty($def['foreign keys'])) {
+ foreach ($def['foreign keys'] as $keyName => $keyDef) {
+ $this->appendForeignKeyDef($sql, $keyName, $keyDef);
+ }
}
- foreach ($uniques as $u) {
- $sql .= ",\nunique index {$name}_{$u}_idx ($u)";
+ // Wrap the CREATE TABLE around the main body chunks...
+ $statements = array();
+ $statements[] = $this->startCreateTable($name, $def) . "\n" .
+ implode($sql, ",\n") . "\n" .
+ $this->endCreateTable($name, $def);
+
+ // Multi-value indexes are advisory and for best portability
+ // should be created as separate statements.
+ if (!empty($def['indexes'])) {
+ foreach ($def['indexes'] as $col => $colDef) {
+ $this->appendCreateIndex($statements, $name, $col, $colDef);
+ }
+ }
+ if (!empty($def['fulltext indexes'])) {
+ foreach ($def['fulltext indexes'] as $col => $colDef) {
+ $this->appendCreateFulltextIndex($statements, $name, $col, $colDef);
+ }
}
- foreach ($indices as $i) {
- $sql .= ",\nindex {$name}_{$i}_idx ($i)";
+ return $statements;
+ }
+
+ /**
+ * Set up a 'create table' SQL statement.
+ *
+ * @param string $name table name
+ * @param array $def table definition
+ * @param $string
+ */
+ function startCreateTable($name, array $def)
+ {
+ return 'CREATE TABLE ' . $this->quoteIdentifier($name) . ' (';
+ }
+
+ /**
+ * Close out a 'create table' SQL statement.
+ *
+ * @param string $name table name
+ * @param array $def table definition
+ * @return string
+ */
+ function endCreateTable($name, array $def)
+ {
+ return ')';
+ }
+
+ /**
+ * Append an SQL fragment with a column definition in a CREATE TABLE statement.
+ *
+ * @param array $sql
+ * @param string $name
+ * @param array $def
+ */
+ function appendColumnDef(array &$sql, $name, array $def)
+ {
+ $sql[] = "$name " . $this->columnSql($def);
+ }
+
+ /**
+ * Append an SQL fragment with a constraint definition for a primary
+ * key in a CREATE TABLE statement.
+ *
+ * @param array $sql
+ * @param array $def
+ */
+ function appendPrimaryKeyDef(array &$sql, array $def)
+ {
+ $sql[] = "PRIMARY KEY " . $this->buildIndexList($def);
+ }
+
+ /**
+ * Append an SQL fragment with a constraint definition for a unique
+ * key in a CREATE TABLE statement.
+ *
+ * @param array $sql
+ * @param string $name
+ * @param array $def
+ */
+ function appendUniqueKeyDef(array &$sql, $name, array $def)
+ {
+ $sql[] = "CONSTRAINT $name UNIQUE " . $this->buildIndexList($def);
+ }
+
+ /**
+ * Append an SQL fragment with a constraint definition for a foreign
+ * key in a CREATE TABLE statement.
+ *
+ * @param array $sql
+ * @param string $name
+ * @param array $def
+ */
+ function appendForeignKeyDef(array &$sql, $name, array $def)
+ {
+ if (count($def) != 2) {
+ throw new Exception("Invalid foreign key def for $name: " . var_export($def, true));
}
+ list($refTable, $map) = $def;
+ $srcCols = array_keys($map);
+ $refCols = array_values($map);
+ $sql[] = "CONSTRAINT $name FOREIGN KEY " .
+ $this->buildIndexList($srcCols) .
+ " REFERENCES " .
+ $this->quoteIdentifier($refTable) .
+ " " .
+ $this->buildIndexList($refCols);
+ }
- $sql .= "); ";
+ /**
+ * Append an SQL statement with an index definition for an advisory
+ * index over one or more columns on a table.
+ *
+ * @param array $statements
+ * @param string $table
+ * @param string $name
+ * @param array $def
+ */
+ function appendCreateIndex(array &$statements, $table, $name, array $def)
+ {
+ $statements[] = "CREATE INDEX $name ON $table " . $this->buildIndexList($def);
+ }
- $res = $this->conn->query($sql);
+ /**
+ * Append an SQL statement with an index definition for a full-text search
+ * index over one or more columns on a table.
+ *
+ * @param array $statements
+ * @param string $table
+ * @param string $name
+ * @param array $def
+ */
+ function appendCreateFulltextIndex(array &$statements, $table, $name, array $def)
+ {
+ throw new Exception("Fulltext index not supported in this database");
+ }
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
+ /**
+ * Append an SQL statement to drop an index from a table.
+ *
+ * @param array $statements
+ * @param string $table
+ * @param string $name
+ * @param array $def
+ */
+ function appendDropIndex(array &$statements, $table, $name)
+ {
+ $statements[] = "DROP INDEX $name ON " . $this->quoteIdentifier($table);
+ }
+
+ function buildIndexList(array $def)
+ {
+ // @fixme
+ return '(' . implode(',', array_map(array($this, 'buildIndexItem'), $def)) . ')';
+ }
+
+ function buildIndexItem($def)
+ {
+ if (is_array($def)) {
+ list($name, $size) = $def;
+ return $this->quoteIdentifier($name) . '(' . intval($size) . ')';
}
-
- return true;
+ return $this->quoteIdentifier($def);
}
/**
@@ -223,7 +375,7 @@ class Schema
}
if (empty($name)) {
- $name = "$table_".implode("_", $columnNames)."_idx";
+ $name = "{$table}_".implode("_", $columnNames)."_idx";
}
$res = $this->conn->query("ALTER TABLE $table ".
@@ -338,46 +490,80 @@ class Schema
* alter the table to match the column definitions.
*
* @param string $tableName name of the table
- * @param array $columns array of ColumnDef
- * objects for the table
+ * @param array $def Table definition array
*
* @return boolean success flag
*/
- public function ensureTable($tableName, $columns)
+ public function ensureTable($tableName, $def)
{
- // XXX: DB engine portability -> toilet
+ $statements = $this->buildEnsureTable($tableName, $def);
+ return $this->runSqlSet($statements);
+ }
+ /**
+ * Run a given set of SQL commands on the connection in sequence.
+ * Empty input is ok.
+ *
+ * @fixme if multiple statements, wrap in a transaction?
+ * @param array $statements
+ * @return boolean success flag
+ */
+ function runSqlSet(array $statements)
+ {
+ $ok = true;
+ foreach ($statements as $sql) {
+ if (defined('DEBUG_INSTALLER')) {
+ echo "" . htmlspecialchars($sql) . " \n";
+ }
+ $res = $this->conn->query($sql);
+
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+ }
+ return $ok;
+ }
+
+ /**
+ * Check a table's status, and if needed build a set
+ * of SQL statements which change it to be consistent
+ * with the given table definition.
+ *
+ * If the table does not yet exist, statements will
+ * be returned to create the table. If it does exist,
+ * statements will be returned to alter the table to
+ * match the column definitions.
+ *
+ * @param string $tableName name of the table
+ * @param array $columns array of ColumnDef
+ * objects for the table
+ *
+ * @return array of SQL statements
+ */
+
+ function buildEnsureTable($tableName, array $def)
+ {
try {
- $td = $this->getTableDef($tableName);
- } catch (Exception $e) {
- if (preg_match('/no such table/', $e->getMessage())) {
- return $this->createTable($tableName, $columns);
- } else {
- throw $e;
- }
+ $old = $this->getTableDef($tableName);
+ } catch (SchemaTableMissingException $e) {
+ return $this->buildCreateTable($tableName, $def);
}
- $cur = $this->_names($td->columns);
- $new = $this->_names($columns);
+ // Filter the DB-independent table definition to match the current
+ // database engine's features and limitations.
+ $def = $this->validateDef($tableName, $def);
+ $def = $this->filterDef($def);
- $toadd = array_diff($new, $cur);
- $todrop = array_diff($cur, $new);
- $same = array_intersect($new, $cur);
- $tomod = array();
+ $statements = array();
+ $fields = $this->diffArrays($old, $def, 'fields', array($this, 'columnsEqual'));
+ $uniques = $this->diffArrays($old, $def, 'unique keys');
+ $indexes = $this->diffArrays($old, $def, 'indexes');
+ $foreign = $this->diffArrays($old, $def, 'foreign keys');
- foreach ($same as $m) {
- $curCol = $this->_byName($td->columns, $m);
- $newCol = $this->_byName($columns, $m);
-
- if (!$newCol->equals($curCol)) {
- $tomod[] = $newCol->name;
- }
- }
-
- if (count($toadd) + count($todrop) + count($tomod) == 0) {
- // nothing to do
- return true;
+ // Drop any obsolete or modified indexes ahead...
+ foreach ($indexes['del'] + $indexes['mod'] as $indexName) {
+ $this->appendDropIndex($statements, $tableName, $indexName);
}
// For efficiency, we want this all in one
@@ -385,31 +571,200 @@ class Schema
$phrase = array();
- foreach ($toadd as $columnName) {
- $cd = $this->_byName($columns, $columnName);
-
- $phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
+ foreach ($foreign['del'] + $foreign['mod'] as $keyName) {
+ $this->appendAlterDropForeign($phrase, $keyName);
}
- foreach ($todrop as $columnName) {
- $phrase[] = 'DROP COLUMN ' . $columnName;
+ foreach ($uniques['del'] + $uniques['mod'] as $keyName) {
+ $this->appendAlterDropUnique($phrase, $keyName);
}
- foreach ($tomod as $columnName) {
- $cd = $this->_byName($columns, $columnName);
-
- $phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd);
+ foreach ($fields['add'] as $columnName) {
+ $this->appendAlterAddColumn($phrase, $columnName,
+ $def['fields'][$columnName]);
}
- $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
-
- $res = $this->conn->query($sql);
-
- if (PEAR::isError($res)) {
- throw new Exception($res->getMessage());
+ foreach ($fields['mod'] as $columnName) {
+ $this->appendAlterModifyColumn($phrase, $columnName,
+ $old['fields'][$columnName],
+ $def['fields'][$columnName]);
}
- return true;
+ foreach ($fields['del'] as $columnName) {
+ $this->appendAlterDropColumn($phrase, $columnName);
+ }
+
+ foreach ($uniques['mod'] + $uniques['add'] as $keyName) {
+ $this->appendAlterAddUnique($phrase, $keyName, $def['unique keys'][$keyName]);
+ }
+
+ foreach ($foreign['mod'] + $foreign['add'] as $keyName) {
+ $this->appendAlterAddForeign($phrase, $keyName, $def['foreign keys'][$keyName]);
+ }
+
+ $this->appendAlterExtras($phrase, $tableName, $def);
+
+ if (count($phrase) > 0) {
+ $sql = 'ALTER TABLE ' . $tableName . ' ' . implode(",\n", $phrase);
+ $statements[] = $sql;
+ }
+
+ // Now create any indexes...
+ foreach ($indexes['mod'] + $indexes['add'] as $indexName) {
+ $this->appendCreateIndex($statements, $tableName, $indexName, $def['indexes'][$indexName]);
+ }
+
+ return $statements;
+ }
+
+ function diffArrays($oldDef, $newDef, $section, $compareCallback=null)
+ {
+ $old = isset($oldDef[$section]) ? $oldDef[$section] : array();
+ $new = isset($newDef[$section]) ? $newDef[$section] : array();
+
+ $oldKeys = array_keys($old);
+ $newKeys = array_keys($new);
+
+ $toadd = array_diff($newKeys, $oldKeys);
+ $todrop = array_diff($oldKeys, $newKeys);
+ $same = array_intersect($newKeys, $oldKeys);
+ $tomod = array();
+ $tokeep = array();
+
+ // Find which fields have actually changed definition
+ // in a way that we need to tweak them for this DB type.
+ foreach ($same as $name) {
+ if ($compareCallback) {
+ $same = call_user_func($compareCallback, $old[$name], $new[$name]);
+ } else {
+ $same = ($old[$name] == $new[$name]);
+ }
+ if ($same) {
+ $tokeep[] = $name;
+ continue;
+ }
+ $tomod[] = $name;
+ }
+ return array('add' => $toadd,
+ 'del' => $todrop,
+ 'mod' => $tomod,
+ 'keep' => $tokeep,
+ 'count' => count($toadd) + count($todrop) + count($tomod));
+ }
+
+ /**
+ * Append phrase(s) to an array of partial ALTER TABLE chunks in order
+ * to add the given column definition to the table.
+ *
+ * @param array $phrase
+ * @param string $columnName
+ * @param array $cd
+ */
+ function appendAlterAddColumn(array &$phrase, $columnName, array $cd)
+ {
+ $phrase[] = 'ADD COLUMN ' .
+ $this->quoteIdentifier($columnName) .
+ ' ' .
+ $this->columnSql($cd);
+ }
+
+ /**
+ * Append phrase(s) to an array of partial ALTER TABLE chunks in order
+ * to alter the given column from its old state to a new one.
+ *
+ * @param array $phrase
+ * @param string $columnName
+ * @param array $old previous column definition as found in DB
+ * @param array $cd current column definition
+ */
+ function appendAlterModifyColumn(array &$phrase, $columnName, array $old, array $cd)
+ {
+ $phrase[] = 'MODIFY COLUMN ' .
+ $this->quoteIdentifier($columnName) .
+ ' ' .
+ $this->columnSql($cd);
+ }
+
+ /**
+ * Append phrase(s) to an array of partial ALTER TABLE chunks in order
+ * to drop the given column definition from the table.
+ *
+ * @param array $phrase
+ * @param string $columnName
+ */
+ function appendAlterDropColumn(array &$phrase, $columnName)
+ {
+ $phrase[] = 'DROP COLUMN ' . $this->quoteIdentifier($columnName);
+ }
+
+ function appendAlterAddUnique(array &$phrase, $keyName, array $def)
+ {
+ $sql = array();
+ $sql[] = 'ADD';
+ $this->appendUniqueKeyDef($sql, $keyName, $def);
+ $phrase[] = implode(' ', $sql);
+ }
+
+ function appendAlterAddForeign(array &$phrase, $keyName, array $def)
+ {
+ $sql = array();
+ $sql[] = 'ADD';
+ $this->appendForeignKeyDef($sql, $keyName, $def);
+ $phrase[] = implode(' ', $sql);
+ }
+
+ function appendAlterDropUnique(array &$phrase, $keyName)
+ {
+ $phrase[] = 'DROP CONSTRAINT ' . $keyName;
+ }
+
+ function appendAlterDropForeign(array &$phrase, $keyName)
+ {
+ $phrase[] = 'DROP FOREIGN KEY ' . $keyName;
+ }
+
+ function appendAlterExtras(array &$phrase, $tableName, array $def)
+ {
+ // no-op
+ }
+
+ /**
+ * Quote a db/table/column identifier if necessary.
+ *
+ * @param string $name
+ * @return string
+ */
+ function quoteIdentifier($name)
+ {
+ return $name;
+ }
+
+ function quoteDefaultValue($cd)
+ {
+ if ($cd['type'] == 'datetime' && $cd['default'] == 'CURRENT_TIMESTAMP') {
+ return $cd['default'];
+ } else {
+ return $this->quoteValue($cd['default']);
+ }
+ }
+
+ function quoteValue($val)
+ {
+ return $this->conn->quoteSmart($val);
+ }
+
+ /**
+ * Check if two column definitions are equivalent.
+ * The default implementation checks _everything_ but in many cases
+ * you may be able to discard a bunch of equivalencies.
+ *
+ * @param array $a
+ * @param array $b
+ * @return boolean
+ */
+ function columnsEqual(array $a, array $b)
+ {
+ return !array_diff_assoc($a, $b) && !array_diff_assoc($b, $a);
}
/**
@@ -421,7 +776,7 @@ class Schema
* @return array strings for name values
*/
- private function _names($cds)
+ protected function _names($cds)
{
$names = array();
@@ -442,7 +797,7 @@ class Schema
* @return ColumnDef matching item or null if no match.
*/
- private function _byName($cds, $name)
+ protected function _byName($cds, $name)
{
foreach ($cds as $cd) {
if ($cd->name == $name) {
@@ -465,32 +820,194 @@ class Schema
* @return string correct SQL for that column
*/
- private function _columnSql($cd)
+ function columnSql(array $cd)
{
- $sql = "{$cd->name} ";
+ $line = array();
+ $line[] = $this->typeAndSize($cd);
- if (!empty($cd->size)) {
- $sql .= "{$cd->type}({$cd->size}) ";
- } else {
- $sql .= "{$cd->type} ";
+ if (isset($cd['default'])) {
+ $line[] = 'default';
+ $line[] = $this->quoteDefaultValue($cd);
+ } else if (!empty($cd['not null'])) {
+ // Can't have both not null AND default!
+ $line[] = 'not null';
}
- if (!empty($cd->default)) {
- $sql .= "default {$cd->default} ";
- } else {
- $sql .= ($cd->nullable) ? "null " : "not null ";
- }
-
- if (!empty($cd->auto_increment)) {
- $sql .= " auto_increment ";
- }
-
- if (!empty($cd->extra)) {
- $sql .= "{$cd->extra} ";
- }
-
- return $sql;
+ return implode(' ', $line);
}
+
+ /**
+ *
+ * @param string $column canonical type name in defs
+ * @return string native DB type name
+ */
+ function mapType($column)
+ {
+ return $column;
+ }
+
+ function typeAndSize($column)
+ {
+ //$type = $this->mapType($column);
+ $type = $column['type'];
+ if (isset($column['size'])) {
+ $type = $column['size'] . $type;
+ }
+ $lengths = array();
+
+ if (isset($column['precision'])) {
+ $lengths[] = $column['precision'];
+ if (isset($column['scale'])) {
+ $lengths[] = $column['scale'];
+ }
+ } else if (isset($column['length'])) {
+ $lengths[] = $column['length'];
+ }
+
+ if ($lengths) {
+ return $type . '(' . implode(',', $lengths) . ')';
+ } else {
+ return $type;
+ }
+ }
+
+ /**
+ * Convert an old-style set of ColumnDef objects into the current
+ * Drupal-style schema definition array, for backwards compatibility
+ * with plugins written for 0.9.x.
+ *
+ * @param string $tableName
+ * @param array $defs: array of ColumnDef objects
+ * @return array
+ */
+ protected function oldToNew($tableName, array $defs)
+ {
+ $table = array();
+ $prefixes = array(
+ 'tiny',
+ 'small',
+ 'medium',
+ 'big',
+ );
+ foreach ($defs as $cd) {
+ $column = array();
+ $column['type'] = $cd->type;
+ foreach ($prefixes as $prefix) {
+ if (substr($cd->type, 0, strlen($prefix)) == $prefix) {
+ $column['type'] = substr($cd->type, strlen($prefix));
+ $column['size'] = $prefix;
+ break;
+ }
+ }
+
+ if ($cd->size) {
+ if ($cd->type == 'varchar' || $cd->type == 'char') {
+ $column['length'] = $cd->size;
+ }
+ }
+ if (!$cd->nullable) {
+ $column['not null'] = true;
+ }
+ if ($cd->auto_increment) {
+ $column['type'] = 'serial';
+ }
+ if ($cd->default) {
+ $column['default'] = $cd->default;
+ }
+ $table['fields'][$cd->name] = $column;
+
+ if ($cd->key == 'PRI') {
+ // If multiple columns are defined as primary key,
+ // we'll pile them on in sequence.
+ if (!isset($table['primary key'])) {
+ $table['primary key'] = array();
+ }
+ $table['primary key'][] = $cd->name;
+ } else if ($cd->key == 'MUL') {
+ // Individual multiple-value indexes are only per-column
+ // using the old ColumnDef syntax.
+ $idx = "{$tableName}_{$cd->name}_idx";
+ $table['indexes'][$idx] = array($cd->name);
+ } else if ($cd->key == 'UNI') {
+ // Individual unique-value indexes are only per-column
+ // using the old ColumnDef syntax.
+ $idx = "{$tableName}_{$cd->name}_idx";
+ $table['unique keys'][$idx] = array($cd->name);
+ }
+ }
+
+ return $table;
+ }
+
+ /**
+ * Filter the given table definition array to match features available
+ * in this database.
+ *
+ * This lets us strip out unsupported things like comments, foreign keys,
+ * or type variants that we wouldn't get back from getTableDef().
+ *
+ * @param array $tableDef
+ */
+ function filterDef(array $tableDef)
+ {
+ return $tableDef;
+ }
+
+ /**
+ * Validate a table definition array, checking for basic structure.
+ *
+ * If necessary, converts from an old-style array of ColumnDef objects.
+ *
+ * @param string $tableName
+ * @param array $def: table definition array
+ * @return array validated table definition array
+ *
+ * @throws Exception on wildly invalid input
+ */
+ function validateDef($tableName, array $def)
+ {
+ if (isset($def[0]) && $def[0] instanceof ColumnDef) {
+ $def = $this->oldToNew($tableName, $def);
+ }
+
+ // A few quick checks :D
+ if (!isset($def['fields'])) {
+ throw new Exception("Invalid table definition for $tableName: no fields.");
+ }
+
+ return $def;
+ }
+
+ function isNumericType($type)
+ {
+ $type = strtolower($type);
+ $known = array('int', 'serial', 'numeric');
+ return in_array($type, $known);
+ }
+
+ /**
+ * Pull info from the query into a fun-fun array of dooooom
+ *
+ * @param string $sql
+ * @return array of arrays
+ */
+ protected function fetchQueryData($sql)
+ {
+ $res = $this->conn->query($sql);
+ if (PEAR::isError($res)) {
+ throw new Exception($res->getMessage());
+ }
+
+ $out = array();
+ $row = array();
+ while ($res->fetchInto($row, DB_FETCHMODE_ASSOC)) {
+ $out[] = $row;
+ }
+ $res->free();
+
+ return $out;
+ }
+
}
class SchemaTableMissingException extends Exception
diff --git a/lib/schemaupdater.php b/lib/schemaupdater.php
new file mode 100644
index 0000000000..1960a06930
--- /dev/null
+++ b/lib/schemaupdater.php
@@ -0,0 +1,142 @@
+.
+ *
+ * @category Database
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2009 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ exit(1);
+}
+
+class SchemaUpdater
+{
+ public function __construct($schema)
+ {
+ $this->schema = $schema;
+ $this->checksums = $this->getChecksums();
+ }
+
+ /**
+ * @param string $tableName
+ * @param array $tableDef
+ */
+ public function register($tableName, array $tableDef)
+ {
+ $this->tables[$tableName] = $tableDef;
+ }
+
+ /**
+ * Go ping em!
+ *
+ * @fixme handle tables that belong on different database servers...?
+ */
+ public function checkSchema()
+ {
+ $checksums = $this->checksums;
+ foreach ($this->tables as $table => $def) {
+ $checksum = $this->checksum($def);
+ if (empty($checksums[$table])) {
+ common_log(LOG_DEBUG, "No previous schema_version for $table: updating to $checksum");
+ } else if ($checksums[$table] == $checksum) {
+ common_log(LOG_DEBUG, "Last schema_version for $table up to date: $checksum");
+ continue;
+ } else {
+ common_log(LOG_DEBUG, "Last schema_version for $table is {$checksums[$table]}: updating to $checksum");
+ }
+ //$this->conn->query('BEGIN');
+ $this->schema->ensureTable($table, $def);
+ $this->saveChecksum($table, $checksum);
+ //$this->conn->commit();
+ }
+ }
+
+ /**
+ * Calculate a checksum for this table definition array.
+ *
+ * @param array $def
+ * @return string
+ */
+ public function checksum(array $def)
+ {
+ $flat = serialize($def);
+ return sha1($flat);
+ }
+
+ /**
+ * Pull all known table checksums into an array for easy lookup.
+ *
+ * @return array: associative array of table names to checksum strings
+ */
+ protected function getChecksums()
+ {
+ $checksums = array();
+
+ PEAR::pushErrorHandling(PEAR_ERROR_EXCEPTION);
+ try {
+ $sv = new Schema_version();
+ $sv->find();
+ while ($sv->fetch()) {
+ $checksums[$sv->table_name] = $sv->checksum;
+ }
+
+ return $checksums;
+ } catch (Exception $e) {
+ // no dice!
+ common_log(LOG_DEBUG, "Possibly schema_version table doesn't exist yet.");
+ }
+ PEAR::popErrorHandling();
+
+ return $checksums;
+ }
+
+ /**
+ * Save or update current available checksums.
+ *
+ * @param string $table
+ * @param string $checksum
+ */
+ protected function saveChecksum($table, $checksum)
+ {
+ PEAR::pushErrorHandling(PEAR_ERROR_EXCEPTION);
+ try {
+ $sv = new Schema_version();
+ $sv->table_name = $table;
+ $sv->checksum = $checksum;
+ $sv->modified = common_sql_now();
+ if (isset($this->checksums[$table])) {
+ $sv->update();
+ } else {
+ $sv->insert();
+ }
+ } catch (Exception $e) {
+ // no dice!
+ common_log(LOG_DEBUG, "Possibly schema_version table doesn't exist yet.");
+ }
+ PEAR::popErrorHandling();
+ $this->checksums[$table] = $checksum;
+ }
+}
diff --git a/lib/search_engines.php b/lib/search_engines.php
index 19703e03fd..7f1684a3e7 100644
--- a/lib/search_engines.php
+++ b/lib/search_engines.php
@@ -41,8 +41,35 @@ class SearchEngine
function set_sort_mode($mode)
{
- if ('chron' === $mode)
- return $this->target->orderBy('created desc');
+ switch ($mode) {
+ case 'chron':
+ return $this->target->orderBy('created DESC');
+ break;
+ case 'reverse_chron':
+ return $this->target->orderBy('created ASC');
+ break;
+ case 'nickname_desc':
+ if ($this->table != 'profile') {
+ throw new Exception(
+ 'nickname_desc sort mode can only be use when searching profile.'
+ );
+ } else {
+ return $this->target->orderBy('nickname DESC');
+ }
+ break;
+ case 'nickname_asc':
+ if ($this->table != 'profile') {
+ throw new Exception(
+ 'nickname_desc sort mode can only be use when searching profile.'
+ );
+ } else {
+ return $this->target->orderBy('nickname ASC');
+ }
+ break;
+ default:
+ return $this->target->orderBy('created DESC');
+ break;
+ }
}
}
diff --git a/lib/searchaction.php b/lib/searchaction.php
index 6d7f46cd6e..7038424fab 100644
--- a/lib/searchaction.php
+++ b/lib/searchaction.php
@@ -70,7 +70,7 @@ class SearchAction extends Action
* @return void
* @see SearchGroupNav
*/
- function showLocalNav()
+ function showObjectNav()
{
$nav = new SearchGroupNav($this, $this->trimmed('q'));
$nav->show();
@@ -165,12 +165,8 @@ You can also try your search on other engines:
E_O_T
), $qe, $qe, $qe, $qe, $qe);
}
- $this->elementStart('dl', array('id' => 'help_search', 'class' => 'help'));
- // TRANS: Definition list item with instructions on how to get (better) search results.
- $this->element('dt', null, _('Search help'));
- $this->elementStart('dd', 'instructions');
+ $this->elementStart('div', 'help instructions');
$this->raw(common_markup_to_html($message));
- $this->elementEnd('dd');
$this->elementEnd('div');
}
}
diff --git a/lib/searchgroupnav.php b/lib/searchgroupnav.php
index e843dc096c..cfe8fde353 100644
--- a/lib/searchgroupnav.php
+++ b/lib/searchgroupnav.php
@@ -2,7 +2,7 @@
/**
* StatusNet, the distributed open-source microblogging tool
*
- * Menu for search actions
+ * Menu for search group of actions
*
* PHP version 5
*
@@ -22,7 +22,7 @@
* @category Menu
* @package StatusNet
* @author Evan Prodromou
- * @copyright 2008 StatusNet, Inc.
+ * @copyright 2008-2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@@ -31,12 +31,10 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
-require_once INSTALLDIR.'/lib/widget.php';
-
/**
* Menu for public group of actions
*
- * @category Output
+ * @category Menu
* @package StatusNet
* @author Evan Prodromou
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
@@ -45,9 +43,8 @@ require_once INSTALLDIR.'/lib/widget.php';
* @see Widget
*/
-class SearchGroupNav extends Widget
+class SearchGroupNav extends Menu
{
- var $action = null;
var $q = null;
/**
@@ -59,7 +56,6 @@ class SearchGroupNav extends Widget
function __construct($action=null, $q = null)
{
parent::__construct($action);
- $this->action = $action;
$this->q = $q;
}
diff --git a/lib/secondarynav.php b/lib/secondarynav.php
new file mode 100644
index 0000000000..542de51ac1
--- /dev/null
+++ b/lib/secondarynav.php
@@ -0,0 +1,90 @@
+.
+ *
+ * @category Cache
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * Secondary menu, shown at the bottom of all pages
+ *
+ * @category General
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+class SecondaryNav extends Menu
+{
+ function show()
+ {
+ $this->out->elementStart('ul', array('class' => 'nav',
+ 'id' => 'site_nav_global_secondary'));
+ if (Event::handle('StartSecondaryNav', array($this->action))) {
+ $this->out->menuItem(common_local_url('doc', array('title' => 'help')),
+ // TRANS: Secondary navigation menu option leading to help on StatusNet.
+ _('Help'));
+ $this->out->menuItem(common_local_url('doc', array('title' => 'about')),
+ // TRANS: Secondary navigation menu option leading to text about StatusNet site.
+ _('About'));
+ $this->out->menuItem(common_local_url('doc', array('title' => 'faq')),
+ // TRANS: Secondary navigation menu option leading to Frequently Asked Questions.
+ _('FAQ'));
+ $bb = common_config('site', 'broughtby');
+ if (!empty($bb)) {
+ $this->out->menuItem(common_local_url('doc', array('title' => 'tos')),
+ // TRANS: Secondary navigation menu option leading to Terms of Service.
+ _('TOS'));
+ }
+ $this->out->menuItem(common_local_url('doc', array('title' => 'privacy')),
+ // TRANS: Secondary navigation menu option leading to privacy policy.
+ _('Privacy'));
+ $this->out->menuItem(common_local_url('doc', array('title' => 'source')),
+ // TRANS: Secondary navigation menu option. Leads to information about StatusNet and its license.
+ _('Source'));
+ $this->out->menuItem(common_local_url('version'),
+ // TRANS: Secondary navigation menu option leading to version information on the StatusNet site.
+ _('Version'));
+ $this->out->menuItem(common_local_url('doc', array('title' => 'contact')),
+ // TRANS: Secondary navigation menu option leading to e-mail contact information on the
+ // TRANS: StatusNet site, where to report bugs, ...
+ _('Contact'));
+ $this->out->menuItem(common_local_url('doc', array('title' => 'badge')),
+ // TRANS: Secondary navigation menu option. Leads to information about embedding a timeline widget.
+ _('Badge'));
+ Event::handle('EndSecondaryNav', array($this->action));
+ }
+ $this->out->elementEnd('ul');
+ }
+}
\ No newline at end of file
diff --git a/lib/settingsaction.php b/lib/settingsaction.php
index c3669868d4..c7113d15c2 100644
--- a/lib/settingsaction.php
+++ b/lib/settingsaction.php
@@ -150,4 +150,32 @@ class SettingsAction extends CurrentUserDesignAction
return '';
}
+ /**
+ * Show the local navigation menu
+ *
+ * This is the same for all settings, so we show it here.
+ *
+ * @return void
+ */
+
+ function showLocalNav()
+ {
+ $menu = new SettingsNav($this);
+ $menu->show();
+ }
+
+ /**
+ * Show notice form.
+ *
+ * @return nothing
+ */
+
+ function showNoticeForm()
+ {
+ return;
+ }
+
+ function showProfileBlock()
+ {
+ }
}
diff --git a/lib/settingsnav.php b/lib/settingsnav.php
new file mode 100644
index 0000000000..2987e36ea9
--- /dev/null
+++ b/lib/settingsnav.php
@@ -0,0 +1,139 @@
+.
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @copyright 2010,2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+ // This check helps protect against security problems;
+ // your code file can't be executed directly from the web.
+ exit(1);
+}
+
+/**
+ * A widget for showing the settings group local nav menu
+ *
+ * @category Widget
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ *
+ * @see HTMLOutputter
+ */
+
+class SettingsNav extends Menu
+{
+ /**
+ * Show the menu
+ *
+ * @return void
+ */
+
+ function show()
+ {
+ $actionName = $this->action->trimmed('action');
+ $user = common_current_user();
+ $nickname = $user->nickname;
+ $name = $user->getProfile()->getBestName();
+
+ // Stub section w/ home link
+ $this->action->elementStart('ul');
+ $this->action->element('h3', null, _('Home'));
+ $this->action->elementStart('ul', 'nav');
+ $this->out->menuItem(common_local_url('all', array('nickname' =>
+ $nickname)),
+ _('Home'),
+ sprintf(_('%s and friends'), $name),
+ $this->action == 'all', 'nav_timeline_personal');
+
+ $this->action->elementEnd('ul');
+ $this->action->elementEnd('ul');
+
+ $this->action->elementStart('ul');
+ $this->action->element('h3', null, _('Settings'));
+ $this->action->elementStart('ul', array('class' => 'nav'));
+
+ if (Event::handle('StartAccountSettingsNav', array(&$this->action))) {
+ $this->action->menuItem(common_local_url('profilesettings'),
+ _('Profile'),
+ _('Change your profile settings'),
+ $actionName == 'profilesettings');
+
+ $this->action->menuItem(common_local_url('avatarsettings'),
+ _('Avatar'),
+ _('Upload an avatar'),
+ $actionName == 'avatarsettings');
+
+ $this->action->menuItem(common_local_url('passwordsettings'),
+ _('Password'),
+ _('Change your password'),
+ $actionName == 'passwordsettings');
+
+ $this->action->menuItem(common_local_url('emailsettings'),
+ _('Email'),
+ _('Change email handling'),
+ $actionName == 'emailsettings');
+
+ $this->action->menuItem(common_local_url('userdesignsettings'),
+ _('Design'),
+ _('Design your profile'),
+ $actionName == 'userdesignsettings');
+
+ $this->action->menuItem(common_local_url('urlsettings'),
+ _('URL'),
+ _('URL shorteners'),
+ $actionName == 'urlsettings');
+
+ Event::handle('EndAccountSettingsNav', array(&$this->action));
+
+ if (common_config('xmpp', 'enabled')) {
+ $this->action->menuItem(common_local_url('imsettings'),
+ _m('IM'),
+ _('Updates by instant messenger (IM)'),
+ $actionName == 'imsettings');
+ }
+
+ if (common_config('sms', 'enabled')) {
+ $this->action->menuItem(common_local_url('smssettings'),
+ _m('SMS'),
+ _('Updates by SMS'),
+ $actionName == 'smssettings');
+ }
+
+ $this->action->menuItem(common_local_url('oauthconnectionssettings'),
+ _('Connections'),
+ _('Authorized connected applications'),
+ $actionName == 'oauthconnectionsettings');
+
+ Event::handle('EndConnectSettingsNav', array(&$this->action));
+ }
+
+ $this->action->elementEnd('ul');
+ $this->action->elementEnd('ul');
+ }
+}
diff --git a/lib/spawningdaemon.php b/lib/spawningdaemon.php
index 2f9f6e32e3..ea09b6fb2f 100644
--- a/lib/spawningdaemon.php
+++ b/lib/spawningdaemon.php
@@ -204,7 +204,7 @@ abstract class SpawningDaemon extends Daemon
// Reconnect main memcached, or threads will stomp on
// each other and corrupt their requests.
- $cache = common_memcache();
+ $cache = Cache::instance();
if ($cache) {
$cache->reconnect();
}
diff --git a/lib/statusnet.php b/lib/statusnet.php
index 85b46bbb3f..648369ec44 100644
--- a/lib/statusnet.php
+++ b/lib/statusnet.php
@@ -31,6 +31,7 @@ class StatusNet
{
protected static $have_config;
protected static $is_api;
+ protected static $is_ajax;
protected static $plugins = array();
/**
@@ -176,6 +177,11 @@ class StatusNet
{
// Load default plugins
foreach (common_config('plugins', 'default') as $name => $params) {
+ $key = 'disable-' . $name;
+ if (common_config('plugins', $key)) {
+ continue;
+ }
+
if (is_null($params)) {
addPlugin($name);
} else if (is_array($params)) {
@@ -225,6 +231,16 @@ class StatusNet
self::$is_api = $mode;
}
+ public function isAjax()
+ {
+ return self::$is_ajax;
+ }
+
+ public function setAjax($mode)
+ {
+ self::$is_ajax = $mode;
+ }
+
/**
* Build default configuration array
* @return array
@@ -240,7 +256,7 @@ class StatusNet
* Establish default configuration based on given or default server and path
* Sets global $_server, $_path, and $config
*/
- protected static function initDefaults($server, $path)
+ public static function initDefaults($server, $path)
{
global $_server, $_path, $config;
@@ -356,7 +372,6 @@ class StatusNet
}
// Backwards compatibility
-
if (array_key_exists('memcached', $config)) {
if ($config['memcached']['enabled']) {
addPlugin('Memcache', array('servers' => $config['memcached']['server']));
@@ -366,6 +381,21 @@ class StatusNet
$config['cache']['base'] = $config['memcached']['base'];
}
}
+ if (array_key_exists('xmpp', $config)) {
+ if ($config['xmpp']['enabled']) {
+ addPlugin('xmpp', array(
+ 'server' => $config['xmpp']['server'],
+ 'port' => $config['xmpp']['port'],
+ 'user' => $config['xmpp']['user'],
+ 'resource' => $config['xmpp']['resource'],
+ 'encryption' => $config['xmpp']['encryption'],
+ 'password' => $config['xmpp']['password'],
+ 'host' => $config['xmpp']['host'],
+ 'debug' => $config['xmpp']['debug'],
+ 'public' => $config['xmpp']['public']
+ ));
+ }
+ }
}
/**
diff --git a/lib/stompqueuemanager.php b/lib/stompqueuemanager.php
index fc98c77d40..1d9a5ad207 100644
--- a/lib/stompqueuemanager.php
+++ b/lib/stompqueuemanager.php
@@ -578,7 +578,7 @@ class StompQueueManager extends QueueManager
function incDeliveryCount($msgId)
{
$count = 0;
- $cache = common_memcache();
+ $cache = Cache::instance();
if ($cache) {
$key = 'statusnet:stomp:message-retries:' . $msgId;
$count = $cache->increment($key);
diff --git a/lib/subgroupnav.php b/lib/subgroupnav.php
index 2748b59e18..ee4b0a8dff 100644
--- a/lib/subgroupnav.php
+++ b/lib/subgroupnav.php
@@ -22,7 +22,7 @@
* @category Subs
* @package StatusNet
* @author Evan Prodromou
- * @copyright 2008-2009 StatusNet, Inc.
+ * @copyright 2008-2011 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
@@ -43,9 +43,8 @@ require_once INSTALLDIR.'/lib/widget.php';
* @link http://status.net/
*/
-class SubGroupNav extends Widget
+class SubGroupNav extends Menu
{
- var $action = null;
var $user = null;
/**
@@ -57,7 +56,6 @@ class SubGroupNav extends Widget
function __construct($action=null, $user=null)
{
parent::__construct($action);
- $this->action = $action;
$this->user = $user;
}
@@ -76,6 +74,12 @@ class SubGroupNav extends Widget
if (Event::handle('StartSubGroupNav', array($this))) {
+ $this->out->menuItem(common_local_url('showstream', array('nickname' =>
+ $this->user->nickname)),
+ _('Profile'),
+ (empty($profile)) ? $this->user->nickname : $profile->getBestName(),
+ $action == 'showstream',
+ 'nav_profile');
$this->out->menuItem(common_local_url('subscriptions',
array('nickname' =>
$this->user->nickname)),
diff --git a/lib/subscribeform.php b/lib/subscribeform.php
index ae2a6db61c..1cc5b4e48e 100644
--- a/lib/subscribeform.php
+++ b/lib/subscribeform.php
@@ -89,7 +89,7 @@ class SubscribeForm extends Form
function formClass()
{
- return 'form_user_subscribe';
+ return 'form_user_subscribe ajax';
}
diff --git a/lib/subscriptionlist.php b/lib/subscriptionlist.php
index fc8f33f2ec..3ca4603948 100644
--- a/lib/subscriptionlist.php
+++ b/lib/subscriptionlist.php
@@ -98,8 +98,6 @@ class SubscriptionListItem extends ProfileListItem
{
$tags = Profile_tag::getTags($this->owner->id, $this->profile->id);
- $this->out->elementStart('dl', 'entity_tags');
- $this->out->elementStart('dt');
if ($this->isOwn()) {
$this->out->element('a', array('href' => common_local_url('tagother',
array('id' => $this->profile->id))),
@@ -107,10 +105,8 @@ class SubscriptionListItem extends ProfileListItem
} else {
$this->out->text(_('Tags'));
}
- $this->out->elementEnd('dt');
- $this->out->elementStart('dd');
if ($tags) {
- $this->out->elementStart('ul', 'tags xoxo');
+ $this->out->elementStart('ul', 'tags xoxo entity_tags');
foreach ($tags as $tag) {
$this->out->elementStart('li');
// Avoid space by using raw output.
@@ -126,7 +122,5 @@ class SubscriptionListItem extends ProfileListItem
} else {
$this->out->text(_('(None)'));
}
- $this->out->elementEnd('dd');
- $this->out->elementEnd('dl');
}
}
diff --git a/lib/theme.php b/lib/theme.php
index 5caa046c20..b5f2b58cf2 100644
--- a/lib/theme.php
+++ b/lib/theme.php
@@ -56,6 +56,9 @@ class Theme
var $name = null;
var $dir = null;
var $path = null;
+ protected $metadata = null; // access via getMetadata() lazy-loader
+ protected $externals = null;
+ protected $deps = null;
/**
* Constructor
@@ -199,9 +202,12 @@ class Theme
*/
function getDeps()
{
- $chain = $this->doGetDeps(array($this->name));
- array_pop($chain); // Drop us back off
- return $chain;
+ if ($this->deps === null) {
+ $chain = $this->doGetDeps(array($this->name));
+ array_pop($chain); // Drop us back off
+ $this->deps = $chain;
+ }
+ return $this->deps;
}
protected function doGetDeps($chain)
@@ -233,6 +239,20 @@ class Theme
* @return associative array of strings
*/
function getMetadata()
+ {
+ if ($this->metadata == null) {
+ $this->metadata = $this->doGetMetadata();
+ }
+ return $this->metadata;
+ }
+
+ /**
+ * Pull data from the theme's theme.ini file.
+ * @fixme calling getFile will fall back to default theme, this may be unsafe.
+ *
+ * @return associative array of strings
+ */
+ private function doGetMetadata()
{
$iniFile = $this->getFile('theme.ini');
if (file_exists($iniFile)) {
@@ -242,6 +262,32 @@ class Theme
}
}
+ /**
+ * Get list of any external URLs required by this theme and any
+ * dependencies. These are lazy-loaded from theme.ini.
+ *
+ * @return array of URL strings
+ */
+ function getExternals()
+ {
+ if ($this->externals == null) {
+ $data = $this->getMetadata();
+ if (!empty($data['external'])) {
+ $ext = (array)$data['external'];
+ } else {
+ $ext = array();
+ }
+
+ if (!empty($data['include'])) {
+ $theme = new Theme($data['include']);
+ $ext = array_merge($ext, $theme->getExternals());
+ }
+
+ $this->externals = array_unique($ext);
+ }
+ return $this->externals;
+ }
+
/**
* Gets the full path of a file in a theme dir based on its relative name
*
diff --git a/lib/threadednoticelist.php b/lib/threadednoticelist.php
new file mode 100644
index 0000000000..919c912831
--- /dev/null
+++ b/lib/threadednoticelist.php
@@ -0,0 +1,319 @@
+.
+ *
+ * @category UI
+ * @package StatusNet
+ * @author Brion Vibber
+ * @copyright 2011 StatusNet, Inc.
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * widget for displaying a list of notices
+ *
+ * There are a number of actions that display a list of notices, in
+ * reverse chronological order. This widget abstracts out most of the
+ * code for UI for notice lists. It's overridden to hide some
+ * data for e.g. the profile page.
+ *
+ * @category UI
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ * @see Notice
+ * @see NoticeListItem
+ * @see ProfileNoticeList
+ */
+
+class ThreadedNoticeList extends NoticeList
+{
+ /**
+ * show the list of notices
+ *
+ * "Uses up" the stream by looping through it. So, probably can't
+ * be called twice on the same list.
+ *
+ * @return int count of notices listed.
+ */
+
+ function show()
+ {
+ $this->out->elementStart('div', array('id' =>'notices_primary'));
+ $this->out->element('h2', null, _('Notices'));
+ $this->out->elementStart('ol', array('class' => 'notices threaded-notices xoxo'));
+
+ $cnt = 0;
+ $conversations = array();
+ while ($this->notice->fetch() && $cnt <= NOTICES_PER_PAGE) {
+ $cnt++;
+
+ if ($cnt > NOTICES_PER_PAGE) {
+ break;
+ }
+
+ $convo = $this->notice->conversation;
+ if (!empty($conversations[$convo])) {
+ // Seen this convo already -- skip!
+ continue;
+ }
+ $conversations[$convo] = true;
+
+ // Get the convo's root notice
+ // @fixme stream goes in wrong direction, this needs sane caching
+ //$notice = Notice::conversationStream($convo, 0, 1);
+ //$notice->fetch();
+ $notice = new Notice();
+ $notice->conversation = $this->notice->conversation;
+ $notice->orderBy('CREATED');
+ $notice->limit(1);
+ $notice->find(true);
+
+ try {
+ $item = $this->newListItem($notice);
+ $item->show();
+ } catch (Exception $e) {
+ // we log exceptions and continue
+ common_log(LOG_ERR, $e->getMessage());
+ continue;
+ }
+ }
+
+ $this->out->elementEnd('ol');
+ $this->out->elementEnd('div');
+
+ return $cnt;
+ }
+
+ /**
+ * returns a new list item for the current notice
+ *
+ * Recipe (factory?) method; overridden by sub-classes to give
+ * a different list item class.
+ *
+ * @param Notice $notice the current notice
+ *
+ * @return NoticeListItem a list item for displaying the notice
+ */
+
+ function newListItem($notice)
+ {
+ return new ThreadedNoticeListItem($notice, $this->out);
+ }
+}
+
+/**
+ * widget for displaying a single notice
+ *
+ * This widget has the core smarts for showing a single notice: what to display,
+ * where, and under which circumstances. Its key method is show(); this is a recipe
+ * that calls all the other show*() methods to build up a single notice. The
+ * ProfileNoticeListItem subclass, for example, overrides showAuthor() to skip
+ * author info (since that's implicit by the data in the page).
+ *
+ * @category UI
+ * @package StatusNet
+ * @author Evan Prodromou
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ * @see NoticeList
+ * @see ProfileNoticeListItem
+ */
+
+class ThreadedNoticeListItem extends NoticeListItem
+{
+ const INITIAL_ITEMS = 3;
+
+ function showContext()
+ {
+ // Silence!
+ }
+
+ /**
+ * finish the notice
+ *
+ * Close the last elements in the notice list item
+ *
+ * @return void
+ */
+
+ function showEnd()
+ {
+ if (!$this->repeat) {
+ $notice = Notice::conversationStream($this->notice->conversation, 0, self::INITIAL_ITEMS + 2);
+ $notices = array();
+ $cnt = 0;
+ $moreCutoff = null;
+ while ($notice->fetch()) {
+ if ($notice->id == $this->notice->id) {
+ // Skip!
+ continue;
+ }
+ $cnt++;
+ if ($cnt > self::INITIAL_ITEMS) {
+ // boo-yah
+ $moreCutoff = clone($notice);
+ break;
+ }
+ $notices[] = clone($notice); // *grumble* inefficient as hell
+ }
+
+ if ($notices) {
+ $this->out->elementStart('ul', 'notices threaded-replies xoxo');
+ if ($moreCutoff) {
+ $item = new ThreadedNoticeListMoreItem($moreCutoff, $this->out);
+ $item->show();
+ }
+ foreach (array_reverse($notices) as $notice) {
+ $item = new ThreadedNoticeListSubItem($notice, $this->out);
+ $item->show();
+ }
+ // @fixme do a proper can-post check that's consistent
+ // with the JS side
+ if (common_current_user()) {
+ $item = new ThreadedNoticeListReplyItem($notice, $this->out);
+ $item->show();
+ }
+ $this->out->elementEnd('ul');
+ }
+ }
+
+ parent::showEnd();
+ }
+}
+
+class ThreadedNoticeListSubItem extends NoticeListItem
+{
+
+ function avatarSize()
+ {
+ return AVATAR_STREAM_SIZE; // @fixme would like something in between
+ }
+
+ function showNoticeLocation()
+ {
+ //
+ }
+
+ function showNoticeSource()
+ {
+ //
+ }
+
+ function showContext()
+ {
+ //
+ }
+}
+
+/**
+ * Placeholder for loading more replies...
+ */
+class ThreadedNoticeListMoreItem extends NoticeListItem
+{
+
+ /**
+ * recipe function for displaying a single notice.
+ *
+ * This uses all the other methods to correctly display a notice. Override
+ * it or one of the others to fine-tune the output.
+ *
+ * @return void
+ */
+
+ function show()
+ {
+ $this->showStart();
+ $this->showMiniForm();
+ $this->showEnd();
+ }
+
+ /**
+ * start a single notice.
+ *
+ * @return void
+ */
+
+ function showStart()
+ {
+ $this->out->elementStart('li', array('class' => 'notice-reply-comments'));
+ }
+
+ function showMiniForm()
+ {
+ $id = $this->notice->conversation;
+ $url = common_local_url('conversation', array('id' => $id)) . '#notice-' . $this->notice->id;
+
+ $notice = new Notice();
+ $notice->conversation = $id;
+ $n = $notice->count() - 1;
+ $msg = sprintf(_m('Show %d reply', 'Show all %d replies', $n), $n);
+
+ $this->out->element('a', array('href' => $url), $msg);
+ }
+}
+
+
+/**
+ * Placeholder for reply form...
+ * Same as get added at runtime via SN.U.NoticeInlineReplyPlaceholder
+ */
+class ThreadedNoticeListReplyItem extends NoticeListItem
+{
+
+ /**
+ * recipe function for displaying a single notice.
+ *
+ * This uses all the other methods to correctly display a notice. Override
+ * it or one of the others to fine-tune the output.
+ *
+ * @return void
+ */
+
+ function show()
+ {
+ $this->showStart();
+ $this->showMiniForm();
+ $this->showEnd();
+ }
+
+ /**
+ * start a single notice.
+ *
+ * @return void
+ */
+
+ function showStart()
+ {
+ $this->out->elementStart('li', array('class' => 'notice-reply-placeholder'));
+ }
+
+ function showMiniForm()
+ {
+ $this->out->element('input', array('class' => 'placeholder',
+ 'value' => _('Write a reply...')));
+ }
+}
\ No newline at end of file
diff --git a/lib/unsubscribeform.php b/lib/unsubscribeform.php
index cb6a87515f..a8e6915d6c 100644
--- a/lib/unsubscribeform.php
+++ b/lib/unsubscribeform.php
@@ -89,7 +89,7 @@ class UnsubscribeForm extends Form
function formClass()
{
- return 'form_user_unsubscribe';
+ return 'form_user_unsubscribe ajax';
}
/**
diff --git a/lib/urlshortenerplugin.php b/lib/urlshortenerplugin.php
new file mode 100644
index 0000000000..8acfac26f4
--- /dev/null
+++ b/lib/urlshortenerplugin.php
@@ -0,0 +1,155 @@
+.
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+if (!defined('STATUSNET') && !defined('LACONICA')) {
+ exit(1);
+}
+
+/**
+ * Superclass for plugins that do URL shortening
+ *
+ * @category Plugin
+ * @package StatusNet
+ * @author Craig Andrews
+ * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link http://status.net/
+ */
+
+abstract class UrlShortenerPlugin extends Plugin
+{
+ public $shortenerName;
+ public $freeService = false;
+
+ // Url Shortener plugins should implement some (or all)
+ // of these methods
+
+ /**
+ * Make an URL shorter.
+ *
+ * @param string $url URL to shorten
+ *
+ * @return string shortened version of the url, or null on failure
+ */
+
+ protected abstract function shorten($url);
+
+ /**
+ * Utility to get the data at an URL
+ *
+ * @param string $url URL to fetch
+ *
+ * @return string response body
+ *
+ * @todo rename to code-standard camelCase httpGet()
+ */
+
+ protected function http_get($url)
+ {
+ $request = HTTPClient::start();
+ $response = $request->get($url);
+ return $response->getBody();
+ }
+
+ /**
+ * Utility to post a request and get a response URL
+ *
+ * @param string $url URL to fetch
+ * @param array $data post parameters
+ *
+ * @return string response body
+ *
+ * @todo rename to code-standard httpPost()
+ */
+
+ protected function http_post($url, $data)
+ {
+ $request = HTTPClient::start();
+ $response = $request->post($url, null, $data);
+ return $response->getBody();
+ }
+
+ // Hook handlers
+
+ /**
+ * Called when all plugins have been initialized
+ *
+ * @return boolean hook value
+ */
+
+ function onInitializePlugin()
+ {
+ if (!isset($this->shortenerName)) {
+ throw new Exception("must specify a shortenerName");
+ }
+ return true;
+ }
+
+ /**
+ * Called when a showing the URL shortener drop-down box
+ *
+ * Properties of the shortening service currently only
+ * include whether it's a free service.
+ *
+ * @param array &$shorteners array mapping shortener name to properties
+ *
+ * @return boolean hook value
+ */
+
+ function onGetUrlShorteners(&$shorteners)
+ {
+ $shorteners[$this->shortenerName] =
+ array('freeService' => $this->freeService);
+ return true;
+ }
+
+ /**
+ * Called to shorten an URL
+ *
+ * @param string $url URL to shorten
+ * @param string $shortenerName Shortening service. Don't handle if it's
+ * not you!
+ * @param string &$shortenedUrl URL after shortening; out param.
+ *
+ * @return boolean hook value
+ */
+
+ function onStartShortenUrl($url, $shortenerName, &$shortenedUrl)
+ {
+ if ($shortenerName == $this->shortenerName) {
+ $result = $this->shorten($url);
+ if (isset($result) && $result != null && $result !== false) {
+ $shortenedUrl = $result;
+ common_log(LOG_INFO,
+ __CLASS__ . ": $this->shortenerName ".
+ "shortened $url to $shortenedUrl");
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/lib/util.php b/lib/util.php
index e5b0c86e06..536fff4dec 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -157,22 +157,38 @@ function common_timezone()
return common_config('site', 'timezone');
}
+function common_valid_language($lang)
+{
+ if ($lang) {
+ // Validate -- we don't want to end up with a bogus code
+ // left over from some old junk.
+ foreach (common_config('site', 'languages') as $code => $info) {
+ if ($info['lang'] == $lang) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
function common_language()
{
+ // Allow ?uselang=xx override, very useful for debugging
+ // and helping translators check usage and context.
+ if (isset($_GET['uselang'])) {
+ $uselang = strval($_GET['uselang']);
+ if (common_valid_language($uselang)) {
+ return $uselang;
+ }
+ }
+
// If there is a user logged in and they've set a language preference
// then return that one...
if (_have_config() && common_logged_in()) {
$user = common_current_user();
- $user_language = $user->language;
- if ($user->language) {
- // Validate -- we don't want to end up with a bogus code
- // left over from some old junk.
- foreach (common_config('site', 'languages') as $code => $info) {
- if ($info['lang'] == $user_language) {
- return $user_language;
- }
- }
+ if (common_valid_language($user->language)) {
+ return $user->language;
}
}
@@ -1015,9 +1031,15 @@ function common_linkify($url) {
*/
function common_shorten_links($text, $always = false, User $user=null)
{
- $maxLength = Notice::maxContent();
- if (!$always && ($maxLength == 0 || mb_strlen($text) <= $maxLength)) return $text;
- return common_replace_urls_callback($text, array('File_redirection', 'makeShort'), $user);
+ $user = common_current_user();
+
+ $maxLength = User_urlshortener_prefs::maxNoticeLength($user);
+
+ if ($always || mb_strlen($text) > $maxLength) {
+ return common_replace_urls_callback($text, array('File_redirection', 'forceShort'), $user);
+ } else {
+ return common_replace_urls_callback($text, array('File_redirection', 'makeShort'), $user);
+ }
}
/**
@@ -1432,14 +1454,8 @@ function common_redirect($url, $code=307)
exit;
}
-function common_broadcast_notice($notice, $remote=false)
-{
- // DO NOTHING!
-}
+// Stick the notice on the queue
-/**
- * Stick the notice on the queue.
- */
function common_enqueue_notice($notice)
{
static $localTransports = array('omb',
@@ -1453,18 +1469,9 @@ function common_enqueue_notice($notice)
$transports[] = 'plugin';
}
- $xmpp = common_config('xmpp', 'enabled');
-
- if ($xmpp) {
- $transports[] = 'jabber';
- }
-
// We can skip these for gatewayed notices.
if ($notice->isLocal()) {
$transports = array_merge($transports, $localTransports);
- if ($xmpp) {
- $transports[] = 'public';
- }
}
if (Event::handle('StartEnqueueNotice', array($notice, &$transports))) {
@@ -2003,21 +2010,6 @@ function common_session_token()
return $_SESSION['token'];
}
-function common_cache_key($extra)
-{
- return Cache::key($extra);
-}
-
-function common_keyize($str)
-{
- return Cache::keyize($str);
-}
-
-function common_memcache()
-{
- return Cache::instance();
-}
-
function common_license_terms($uri)
{
if(preg_match('/creativecommons.org\/licenses\/([^\/]+)/', $uri, $matches)) {
@@ -2058,33 +2050,46 @@ function common_database_tablename($tablename)
/**
* Shorten a URL with the current user's configured shortening service,
* or ur1.ca if configured, or not at all if no shortening is set up.
- * Length is not considered.
*
- * @param string $long_url
+ * @param string $long_url original URL
* @param User $user to specify a particular user's options
+ * @param boolean $force Force shortening (used when notice is too long)
* @return string may return the original URL if shortening failed
*
* @fixme provide a way to specify a particular shortener
*/
-function common_shorten_url($long_url, User $user=null)
+function common_shorten_url($long_url, User $user=null, $force = false)
{
$long_url = trim($long_url);
- if (empty($user)) {
- // Current web session
- $user = common_current_user();
- }
- if (empty($user)) {
- // common current user does not find a user when called from the XMPP daemon
- // therefore we'll set one here fix, so that XMPP given URLs may be shortened
- $shortenerName = 'ur1.ca';
- } else {
- $shortenerName = $user->urlshorteningservice;
+
+ $user = common_current_user();
+
+ $maxUrlLength = User_urlshortener_prefs::maxUrlLength($user);
+
+ // $force forces shortening even if it's not strictly needed
+ // I doubt URL shortening is ever 'strictly' needed. - ESP
+
+ if (mb_strlen($long_url) < $maxUrlLength && !$force) {
+ return $long_url;
}
- if(Event::handle('StartShortenUrl', array($long_url,$shortenerName,&$shortenedUrl))){
- //URL wasn't shortened, so return the long url
- return $long_url;
- }else{
+ $shortenerName = User_urlshortener_prefs::urlShorteningService($user);
+
+ if (Event::handle('StartShortenUrl',
+ array($long_url, $shortenerName, &$shortenedUrl))) {
+ if ($shortenerName == 'internal') {
+ $f = File::processNew($long_url);
+ if (empty($f)) {
+ return $long_url;
+ } else {
+ $shortenedUrl = common_local_url('redirecturl',
+ array('id' => $f->id));
+ return $shortenedUrl;
+ }
+ } else {
+ return $long_url;
+ }
+ } else {
//URL was shortened, so return the result
return trim($shortenedUrl);
}
diff --git a/lib/xmppmanager.php b/lib/xmppmanager.php
deleted file mode 100644
index 585d044c74..0000000000
--- a/lib/xmppmanager.php
+++ /dev/null
@@ -1,491 +0,0 @@
-.
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
-
-/**
- * XMPP background connection manager for XMPP-using queue handlers,
- * allowing them to send outgoing messages on the right connection.
- *
- * Input is handled during socket select loop, keepalive pings during idle.
- * Any incoming messages will be forwarded to the main XmppDaemon process,
- * which handles direct user interaction.
- *
- * In a multi-site queuedaemon.php run, one connection will be instantiated
- * for each site being handled by the current process that has XMPP enabled.
- */
-class XmppManager extends IoManager
-{
- protected $site = null;
- protected $pingid = 0;
- protected $lastping = null;
- protected $conn = null;
-
- static protected $singletons = array();
-
- const PING_INTERVAL = 120;
-
- /**
- * Fetch the singleton XmppManager for the current site.
- * @return mixed XmppManager, or false if unneeded
- */
- public static function get()
- {
- if (common_config('xmpp', 'enabled')) {
- $site = StatusNet::currentSite();
- if (empty(self::$singletons[$site])) {
- self::$singletons[$site] = new XmppManager();
- }
- return self::$singletons[$site];
- } else {
- return false;
- }
- }
-
- /**
- * Tell the i/o master we need one instance for each supporting site
- * being handled in this process.
- */
- public static function multiSite()
- {
- return IoManager::INSTANCE_PER_SITE;
- }
-
- function __construct()
- {
- $this->site = StatusNet::currentSite();
- $this->resource = common_config('xmpp', 'resource') . 'daemon';
- }
-
- /**
- * Initialize connection to server.
- * @return boolean true on success
- */
- public function start($master)
- {
- parent::start($master);
- $this->switchSite();
-
- require_once INSTALLDIR . "/lib/jabber.php";
-
- # Low priority; we don't want to receive messages
-
- common_log(LOG_INFO, "INITIALIZE");
- $this->conn = jabber_connect($this->resource);
-
- if (empty($this->conn)) {
- common_log(LOG_ERR, "Couldn't connect to server.");
- return false;
- }
-
- $this->log(LOG_DEBUG, "Initializing stanza handlers.");
-
- $this->conn->addEventHandler('message', 'handle_message', $this);
- $this->conn->addEventHandler('presence', 'handle_presence', $this);
- $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this);
-
- $this->conn->setReconnectTimeout(600);
- // @todo Needs i18n?
- jabber_send_presence("Send me a message to post a notice", 'available', null, 'available', 100);
-
- return !is_null($this->conn);
- }
-
- /**
- * Message pump is triggered on socket input, so we only need an idle()
- * call often enough to trigger our outgoing pings.
- */
- function timeout()
- {
- return self::PING_INTERVAL;
- }
-
- /**
- * Lists the XMPP connection socket to allow i/o master to wake
- * when input comes in here as well as from the queue source.
- *
- * @return array of resources
- */
- public function getSockets()
- {
- if ($this->conn) {
- return array($this->conn->getSocket());
- } else {
- return array();
- }
- }
-
- /**
- * Process XMPP events that have come in over the wire.
- * Side effects: may switch site configuration
- * @fixme may kill process on XMPP error
- * @param resource $socket
- */
- public function handleInput($socket)
- {
- $this->switchSite();
-
- # Process the queue for as long as needed
- try {
- if ($this->conn) {
- assert($socket === $this->conn->getSocket());
-
- common_log(LOG_DEBUG, "Servicing the XMPP queue.");
- $this->stats('xmpp_process');
- $this->conn->processTime(0);
- }
- } catch (XMPPHP_Exception $e) {
- common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
- die($e->getMessage());
- }
- }
-
- /**
- * Idle processing for io manager's execution loop.
- * Send keepalive pings to server.
- *
- * Side effect: kills process on exception from XMPP library.
- *
- * @fixme non-dying error handling
- */
- public function idle($timeout=0)
- {
- if ($this->conn) {
- $now = time();
- if (empty($this->lastping) || $now - $this->lastping > self::PING_INTERVAL) {
- $this->switchSite();
- try {
- $this->sendPing();
- $this->lastping = $now;
- } catch (XMPPHP_Exception $e) {
- common_log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage());
- die($e->getMessage());
- }
- }
- }
- }
-
- /**
- * For queue handlers to pass us a message to push out,
- * if we're active.
- *
- * @fixme should this be blocking etc?
- *
- * @param string $msg XML stanza to send
- * @return boolean success
- */
- public function send($msg)
- {
- if ($this->conn && !$this->conn->isDisconnected()) {
- $bytes = $this->conn->send($msg);
- if ($bytes > 0) {
- $this->conn->processTime(0);
- return true;
- } else {
- common_log(LOG_ERR, __METHOD__ . ' failed: 0 bytes sent');
- return false;
- }
- } else {
- // Can't send right now...
- common_log(LOG_ERR, __METHOD__ . ' failed: XMPP server connection currently down');
- return false;
- }
- }
-
- /**
- * Send a keepalive ping to the XMPP server.
- */
- protected function sendPing()
- {
- $jid = jabber_daemon_address().'/'.$this->resource;
- $server = common_config('xmpp', 'server');
-
- if (!isset($this->pingid)) {
- $this->pingid = 0;
- } else {
- $this->pingid++;
- }
-
- common_log(LOG_DEBUG, "Sending ping #{$this->pingid}");
-
- $this->conn->send("");
- }
-
- /**
- * Callback for Jabber reconnect event
- * @param $pl
- */
- function handle_reconnect(&$pl)
- {
- common_log(LOG_NOTICE, 'XMPP reconnected');
-
- $this->conn->processUntil('session_start');
- $this->conn->presence(null, 'available', null, 'available', 100);
- }
-
-
- function get_user($from)
- {
- $user = User::staticGet('jabber', jabber_normalize_jid($from));
- return $user;
- }
-
- /**
- * XMPP callback for handling message input...
- * @param array $pl XMPP payload
- */
- function handle_message(&$pl)
- {
- $from = jabber_normalize_jid($pl['from']);
-
- if ($pl['type'] != 'chat') {
- $this->log(LOG_WARNING, "Ignoring message of type ".$pl['type']." from $from: " . $pl['xml']->toString());
- return;
- }
-
- if (mb_strlen($pl['body']) == 0) {
- $this->log(LOG_WARNING, "Ignoring message with empty body from $from: " . $pl['xml']->toString());
- return;
- }
-
- // Forwarded from another daemon for us to handle; this shouldn't
- // happen any more but we might get some legacy items.
- if ($this->is_self($from)) {
- $this->log(LOG_INFO, "Got forwarded notice from self ($from).");
- $from = $this->get_ofrom($pl);
- $this->log(LOG_INFO, "Originally sent by $from.");
- if (is_null($from) || $this->is_self($from)) {
- $this->log(LOG_INFO, "Ignoring notice originally sent by $from.");
- return;
- }
- }
-
- $user = $this->get_user($from);
-
- // For common_current_user to work
- global $_cur;
- $_cur = $user;
-
- if (!$user) {
- // TRANS: %s is the URL to the StatusNet site's Instant Messaging settings.
- $this->from_site($from, sprintf(_('Unknown user. Go to %s ' .
- 'to add your address to your account'),common_local_url('imsettings')));
- $this->log(LOG_WARNING, 'Message from unknown user ' . $from);
- return;
- }
- if ($this->handle_command($user, $pl['body'])) {
- $this->log(LOG_INFO, "Command message by $from handled.");
- return;
- } else if ($this->is_autoreply($pl['body'])) {
- $this->log(LOG_INFO, 'Ignoring auto reply from ' . $from);
- return;
- } else if ($this->is_otr($pl['body'])) {
- $this->log(LOG_INFO, 'Ignoring OTR from ' . $from);
- return;
- } else {
-
- $this->log(LOG_INFO, 'Posting a notice from ' . $user->nickname);
-
- $this->add_notice($user, $pl);
- }
-
- $user->free();
- unset($user);
- unset($_cur);
-
- unset($pl['xml']);
- $pl['xml'] = null;
-
- $pl = null;
- unset($pl);
- }
-
- function is_self($from)
- {
- return preg_match('/^'.strtolower(jabber_daemon_address()).'/', strtolower($from));
- }
-
- function get_ofrom($pl)
- {
- $xml = $pl['xml'];
- $addresses = $xml->sub('addresses');
- if (!$addresses) {
- $this->log(LOG_WARNING, 'Forwarded message without addresses');
- return null;
- }
- $address = $addresses->sub('address');
- if (!$address) {
- $this->log(LOG_WARNING, 'Forwarded message without address');
- return null;
- }
- if (!array_key_exists('type', $address->attrs)) {
- $this->log(LOG_WARNING, 'No type for forwarded message');
- return null;
- }
- $type = $address->attrs['type'];
- if ($type != 'ofrom') {
- $this->log(LOG_WARNING, 'Type of forwarded message is not ofrom');
- return null;
- }
- if (!array_key_exists('jid', $address->attrs)) {
- $this->log(LOG_WARNING, 'No jid for forwarded message');
- return null;
- }
- $jid = $address->attrs['jid'];
- if (!$jid) {
- $this->log(LOG_WARNING, 'Could not get jid from address');
- return null;
- }
- $this->log(LOG_DEBUG, 'Got message forwarded from jid ' . $jid);
- return $jid;
- }
-
- function is_autoreply($txt)
- {
- if (preg_match('/[\[\(]?[Aa]uto[-\s]?[Rr]e(ply|sponse)[\]\)]/', $txt)) {
- return true;
- } else if (preg_match('/^System: Message wasn\'t delivered. Offline storage size was exceeded.$/', $txt)) {
- return true;
- } else {
- return false;
- }
- }
-
- function is_otr($txt)
- {
- if (preg_match('/^\?OTR/', $txt)) {
- return true;
- } else {
- return false;
- }
- }
-
- function from_site($address, $msg)
- {
- $text = '['.common_config('site', 'name') . '] ' . $msg;
- jabber_send_message($address, $text);
- }
-
- function handle_command($user, $body)
- {
- $inter = new CommandInterpreter();
- $cmd = $inter->handle_command($user, $body);
- if ($cmd) {
- $chan = new XMPPChannel($this->conn);
- $cmd->execute($chan);
- return true;
- } else {
- return false;
- }
- }
-
- function add_notice(&$user, &$pl)
- {
- $body = trim($pl['body']);
- $content_shortened = $user->shortenLinks($body);
- if (Notice::contentTooLong($content_shortened)) {
- $from = jabber_normalize_jid($pl['from']);
- // TRANS: Response to XMPP source when it sent too long a message.
- // TRANS: %1$d the maximum number of allowed characters (used for plural), %2$d is the sent number.
- $this->from_site($from, sprintf(_m('Message too long. Maximum is %1$d character, you sent %2$d.',
- 'Message too long. Maximum is %1$d characters, you sent %2$d.',
- Notice::maxContent()),
- Notice::maxContent(),
- mb_strlen($content_shortened)));
- return;
- }
-
- try {
- $notice = Notice::saveNew($user->id, $content_shortened, 'xmpp');
- } catch (Exception $e) {
- $this->log(LOG_ERR, $e->getMessage());
- $this->from_site($user->jabber, $e->getMessage());
- return;
- }
-
- common_broadcast_notice($notice);
- $this->log(LOG_INFO,
- 'Added notice ' . $notice->id . ' from user ' . $user->nickname);
- $notice->free();
- unset($notice);
- }
-
- function handle_presence(&$pl)
- {
- $from = jabber_normalize_jid($pl['from']);
- switch ($pl['type']) {
- case 'subscribe':
- # We let anyone subscribe
- $this->subscribed($from);
- $this->log(LOG_INFO,
- 'Accepted subscription from ' . $from);
- break;
- case 'subscribed':
- case 'unsubscribed':
- case 'unsubscribe':
- $this->log(LOG_INFO,
- 'Ignoring "' . $pl['type'] . '" from ' . $from);
- break;
- default:
- if (!$pl['type']) {
- $user = User::staticGet('jabber', $from);
- if (!$user) {
- $this->log(LOG_WARNING, 'Presence from unknown user ' . $from);
- return;
- }
- if ($user->updatefrompresence) {
- $this->log(LOG_INFO, 'Updating ' . $user->nickname .
- ' status from presence.');
- $this->add_notice($user, $pl);
- }
- $user->free();
- unset($user);
- }
- break;
- }
- unset($pl['xml']);
- $pl['xml'] = null;
-
- $pl = null;
- unset($pl);
- }
-
- function log($level, $msg)
- {
- $text = 'XMPPDaemon('.$this->resource.'): '.$msg;
- common_log($level, $text);
- }
-
- function subscribed($to)
- {
- jabber_special_presence('subscribed', $to);
- }
-
- /**
- * Make sure we're on the right site configuration
- */
- protected function switchSite()
- {
- if ($this->site != StatusNet::currentSite()) {
- common_log(LOG_DEBUG, __METHOD__ . ": switching to site $this->site");
- $this->stats('switch');
- StatusNet::switchSite($this->site);
- }
- }
-}
diff --git a/lib/xmppoutqueuehandler.php b/lib/xmppoutqueuehandler.php
deleted file mode 100644
index a4c9bbc4d6..0000000000
--- a/lib/xmppoutqueuehandler.php
+++ /dev/null
@@ -1,54 +0,0 @@
-.
- */
-
-/**
- * Queue handler for pre-processed outgoing XMPP messages.
- * Formatted XML stanzas will have been pushed into the queue
- * via the Queued_XMPP connection proxy, probably from some
- * other queue processor.
- *
- * Here, the XML stanzas are simply pulled out of the queue and
- * pushed out over the wire; an XmppManager is needed to set up
- * and maintain the actual server connection.
- *
- * This queue will be run via XmppDaemon rather than QueueDaemon.
- *
- * @author Brion Vibber
- */
-class XmppOutQueueHandler extends QueueHandler
-{
- function transport() {
- return 'xmppout';
- }
-
- /**
- * Take a previously-queued XMPP stanza and send it out ot the server.
- * @param string $msg
- * @return boolean true on success
- */
- function handle($msg)
- {
- assert(is_string($msg));
-
- $xmpp = XmppManager::get();
- $ok = $xmpp->send($msg);
-
- return $ok;
- }
-}
diff --git a/locale/ar/LC_MESSAGES/statusnet.po b/locale/ar/LC_MESSAGES/statusnet.po
index eb9802fc22..a2767e3bf0 100644
--- a/locale/ar/LC_MESSAGES/statusnet.po
+++ b/locale/ar/LC_MESSAGES/statusnet.po
@@ -12,100 +12,82 @@ msgid ""
msgstr ""
"Project-Id-Version: StatusNet - Core\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-02-24 10:32+0000\n"
-"PO-Revision-Date: 2011-02-24 10:36:00+0000\n"
+"POT-Creation-Date: 2011-03-17 12:35+0000\n"
+"PO-Revision-Date: 2011-03-17 12:37:44+0000\n"
"Language-Team: Arabic \n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: MediaWiki 1.18alpha (r82712); Translate extension (2011-02-01)\n"
+"X-Generator: MediaWiki 1.18alpha (r84120); Translate extension (2011-03-11)\n"
"X-Translation-Project: translatewiki.net at http://translatewiki.net\n"
"X-Language-Code: ar\n"
"X-Message-Group: #out-statusnet-core\n"
"Plural-Forms: nplurals=6; plural=(n == 0) ? 0 : ( (n == 1) ? 1 : ( (n == "
"2) ? 2 : ( (n%100 >= 3 && n%100 <= 10) ? 3 : ( (n%100 >= 11 && n%100 <= "
"99) ? 4 : 5 ) ) ) );\n"
-"X-POT-Import-Date: 2011-02-17 11:39:54+0000\n"
+"X-POT-Import-Date: 2011-03-17 10:19:27+0000\n"
#. TRANS: Page title for Access admin panel that allows configuring site access.
#. TRANS: Menu item for site administration
-#: actions/accessadminpanel.php:53 lib/adminpanelaction.php:363
msgid "Access"
msgstr "نفاذ"
#. TRANS: Page notice.
-#: actions/accessadminpanel.php:64
msgid "Site access settings"
msgstr "إعدادات الوصول إلى الموقع"
#. TRANS: Form legend for registration form.
-#: actions/accessadminpanel.php:151
msgid "Registration"
msgstr "تسجيل"
#. TRANS: Checkbox instructions for admin setting "Private".
-#: actions/accessadminpanel.php:155
msgid "Prohibit anonymous users (not logged in) from viewing site?"
msgstr "أأمنع المستخدمين المجهولين (غير الوالجين) من عرض الموقع؟"
#. TRANS: Checkbox label for prohibiting anonymous users from viewing site.
-#: actions/accessadminpanel.php:157
msgctxt "LABEL"
msgid "Private"
msgstr "خاص"
#. TRANS: Checkbox instructions for admin setting "Invite only".
-#: actions/accessadminpanel.php:164
msgid "Make registration invitation only."
msgstr "اجعل التسجيل عبر الدعوة فقط."
#. TRANS: Checkbox label for configuring site as invite only.
-#: actions/accessadminpanel.php:166
msgid "Invite only"
msgstr "بالدعوة فقط"
#. TRANS: Checkbox instructions for admin setting "Closed" (no new registrations).
-#: actions/accessadminpanel.php:173
msgid "Disable new registrations."
msgstr "عطّل التسجيل الجديد."
#. TRANS: Checkbox label for disabling new user registrations.
-#: actions/accessadminpanel.php:175
msgid "Closed"
msgstr "مُغلق"
#. TRANS: Title for button to save access settings in site admin panel.
-#: actions/accessadminpanel.php:191
msgid "Save access settings"
msgstr "حفظ إعدادت الوصول"
#. TRANS: Tooltip for button to save access settings in site admin panel.
#. TRANS: Button text for saving theme settings.
#. TRANS: Button label to save e-mail preferences.
-#. TRANS: Button label to save Instant Messaging preferences.
-#. TRANS: Button text for saving "Other settings" in profile.
+#. TRANS: Button label to save IM preferences.
+#. TRANS: Button text in the license admin panel.
#. TRANS: Button text to store form data in the Paths admin panel.
#. TRANS: Button to save input in profile settings.
#. TRANS: Button text for saving site notice in admin panel.
#. TRANS: Button label to save SMS preferences.
#. TRANS: Save button for settings for a profile in a subscriptions list.
+#. TRANS: Button text for saving "Other settings" in profile.
#. TRANS: Button text to save user settings in user admin panel.
#. TRANS: Button label in the "Edit application" form.
#. TRANS: Button text on profile design page to save settings.
-#: actions/accessadminpanel.php:193 actions/designadminpanel.php:732
-#: actions/emailsettings.php:250 actions/imsettings.php:183
-#: actions/othersettings.php:136 actions/pathsadminpanel.php:512
-#: actions/profilesettings.php:197 actions/sitenoticeadminpanel.php:197
-#: actions/smssettings.php:205 actions/subscriptions.php:259
-#: actions/useradminpanel.php:298 lib/applicationeditform.php:355
-#: lib/designsettings.php:270 lib/groupeditform.php:201
msgctxt "BUTTON"
msgid "Save"
msgstr "احفظ"
#. TRANS: Server error when page not found (404).
#. TRANS: Server error when page not found (404)
-#: actions/all.php:68 actions/public.php:98 actions/replies.php:93
-#: actions/showfavorites.php:140 actions/tag.php:52
msgid "No such page."
msgstr "لا صفحة كهذه."
@@ -124,6 +106,7 @@ msgstr "لا صفحة كهذه."
#. TRANS: Client error displayed when checking group membership for a non-existing user.
#. TRANS: Client error displayed when trying to have a non-existing user join a group.
#. TRANS: Client error displayed when trying to have a non-existing user leave a group.
+#. TRANS: Client error displayed when not providing a user or an invalid user.
#. TRANS: Client error displayed when updating a status for a non-existing user.
#. TRANS: Client error displayed when requesting a list of followers for a non-existing user.
#. TRANS: Client error displayed when requesting most recent favourite notices by a user for a non-existing user.
@@ -136,6 +119,8 @@ msgstr "لا صفحة كهذه."
#. TRANS: Client error displayed when requesting Friends of a Friend feed without providing a user nickname.
#. TRANS: Client error displayed when requesting Friends of a Friend feed for an object that is not a user.
#. TRANS: Client error displayed when trying to get a user hCard for a non-existing user.
+#. TRANS: Client error displayed trying to make a micro summary without providing a valid user.
+#. TRANS: Client error displayed trying to send a direct message to a non-existing user.
#. TRANS: Client error displayed trying to use "one time password login" without using an existing user.
#. TRANS: Client error displayed when providing a non-existing nickname in a RSS 1.0 action.
#. TRANS: Client error displayed when trying to display favourite notices for a non-existing user.
@@ -143,33 +128,10 @@ msgstr "لا صفحة كهذه."
#. TRANS: Client error displayed providing a non-existing nickname.
#. TRANS: Error text shown when trying to send a direct message to a user that does not exist.
#. TRANS: Client error displayed when calling a profile action without specifying a user.
-#: actions/all.php:80 actions/allrss.php:69
-#: actions/apiaccountupdatedeliverydevice.php:110
-#: actions/apiaccountupdateprofile.php:103
-#: actions/apiaccountupdateprofilebackgroundimage.php:118
-#: actions/apiaccountupdateprofileimage.php:104 actions/apiatomservice.php:59
-#: actions/apiblockcreate.php:96 actions/apiblockdestroy.php:94
-#: actions/apidirectmessage.php:75 actions/apidirectmessagenew.php:72
-#: actions/apigroupcreate.php:111 actions/apigroupismember.php:89
-#: actions/apigroupjoin.php:98 actions/apigroupleave.php:98
-#: actions/apigrouplist.php:70 actions/apistatusesupdate.php:230
-#: actions/apisubscriptions.php:85 actions/apitimelinefavorites.php:70
-#: actions/apitimelinefriends.php:173 actions/apitimelinehome.php:78
-#: actions/apitimelinementions.php:77 actions/apitimelineuser.php:79
-#: actions/avatarbynickname.php:79 actions/favoritesrss.php:72
-#: actions/foaf.php:42 actions/foaf.php:61 actions/hcard.php:67
-#: actions/microsummary.php:62 actions/newmessage.php:116 actions/otp.php:78
-#: actions/remotesubscribe.php:144 actions/remotesubscribe.php:153
-#: actions/replies.php:73 actions/repliesrss.php:38 actions/rsd.php:113
-#: actions/showfavorites.php:106 actions/userbyid.php:75
-#: actions/usergroups.php:93 actions/userrss.php:40 actions/userxrd.php:59
-#: actions/xrds.php:71 lib/command.php:497 lib/galleryaction.php:59
-#: lib/mailbox.php:80 lib/profileaction.php:77
msgid "No such user."
msgstr "لا مستخدم كهذا."
#. TRANS: Page title. %1$s is user nickname, %2$d is page number
-#: actions/all.php:91
#, php-format
msgid "%1$s and friends, page %2$d"
msgstr "%1$s والأصدقاء, الصفحة %2$d"
@@ -178,51 +140,43 @@ msgstr "%1$s والأصدقاء, الصفحة %2$d"
#. TRANS: H1 text for page. %s is a user nickname.
#. TRANS: Message is used as link title. %s is a user nickname.
#. TRANS: Timeline title for user and friends. %s is a user nickname.
-#. TRANS: Tooltop for personal group navigation menu option when logged in for viewing timeline of self and friends.
-#: actions/all.php:94 actions/all.php:191 actions/allrss.php:117
-#: actions/apitimelinefriends.php:207 actions/apitimelinehome.php:113
-#: lib/personalgroupnav.php:102
#, php-format
msgid "%s and friends"
msgstr "%s والأصدقاء"
#. TRANS: %s is user nickname.
-#: actions/all.php:108
#, php-format
msgid "Feed for friends of %s (RSS 1.0)"
msgstr "تغذية أصدقاء %s (آرإسإس 1.0)"
#. TRANS: %s is user nickname.
-#: actions/all.php:117
#, php-format
msgid "Feed for friends of %s (RSS 2.0)"
msgstr "تغذية أصدقاء %s (آرإسإس 2.0)"
#. TRANS: %s is user nickname.
-#: actions/all.php:126
#, php-format
msgid "Feed for friends of %s (Atom)"
msgstr "تغذية أصدقاء %s (أتوم)"
#. TRANS: Empty list message. %s is a user nickname.
-#: actions/all.php:139
#, php-format
msgid ""
"This is the timeline for %s and friends but no one has posted anything yet."
-msgstr ""
+msgstr "هذا هو السجل الزمني ل%s وأصدقائه لكن لم يرسل أحد شيئًا إلى الآن."
#. TRANS: Encouragement displayed on logged in user's empty timeline.
#. TRANS: This message contains Markdown links. Keep "](" together.
-#: actions/all.php:146
#, php-format
msgid ""
"Try subscribing to more people, [join a group](%%action.groups%%) or post "
"something yourself."
msgstr ""
+"حاول أن تشترك بمزيد من الأشخاص أو أن [تنضم إلى مجموعة](%%action.groups%%) أو "
+"أن تكتبت شيئا."
#. TRANS: %1$s is user nickname, %2$s is user nickname, %2$s is user nickname prefixed with "@".
#. TRANS: This message contains Markdown links. Keep "](" together.
-#: actions/all.php:150
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) from their profile or [post something "
@@ -233,7 +187,6 @@ msgstr ""
#. TRANS: %s is a user nickname. This message contains Markdown links. Keep "](" together.
#. TRANS: Second sentence of empty message for anonymous users. %s is a user nickname.
#. TRANS: This message contains a Markdown link. Keep "](" together.
-#: actions/all.php:155 actions/replies.php:210 actions/showstream.php:227
#, fuzzy, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and then nudge %s or "
@@ -243,14 +196,11 @@ msgstr ""
"register%%%%) وترسل شيئًا شيقًا ليضيفه إلى مفضلته. :)"
#. TRANS: H1 text for page when viewing a list for self.
-#: actions/all.php:188
msgid "You and friends"
msgstr "أنت والأصدقاء"
#. TRANS: Message is used as link description. %1$s is a username, %2$s is a site name.
#. TRANS: Message is used as a subtitle. %1$s is a user nickname, %2$s is a site name.
-#: actions/allrss.php:122 actions/apitimelinefriends.php:213
-#: actions/apitimelinehome.php:119
#, fuzzy, php-format
msgid "Updates from %1$s and friends on %2$s!"
msgstr "الإشعارات التي فضلها %1$s في %2$s!"
@@ -269,74 +219,32 @@ msgstr "الإشعارات التي فضلها %1$s في %2$s!"
#. TRANS: Client error displayed trying to execute an unknown API method checking group membership.
#. TRANS: Client error displayed trying to execute an unknown API method listing the latest 20 groups.
#. TRANS: Client error displayed trying to execute an unknown API method showing group membership.
+#. TRANS: Client error displayed when using an unsupported API format.
#. TRANS: Client error displayed trying to execute an unknown API method showing a group.
#. TRANS: Client error displayed trying to execute an unknown API method testing API connectivity.
#. TRANS: Client error displayed trying to execute an unknown API method deleting a status.
#. TRANS: Client error displayed when trying to handle an unknown API method.
-#: actions/apiaccountratelimitstatus.php:69
-#: actions/apiaccountupdatedeliverydevice.php:92
-#: actions/apiaccountupdateprofile.php:94
-#: actions/apiaccountupdateprofilebackgroundimage.php:92
-#: actions/apiaccountupdateprofilecolors.php:115
-#: actions/apiaccountverifycredentials.php:68 actions/apidirectmessage.php:157
-#: actions/apifavoritecreate.php:98 actions/apifavoritedestroy.php:98
-#: actions/apifriendshipscreate.php:99 actions/apifriendshipsdestroy.php:99
-#: actions/apifriendshipsshow.php:124 actions/apigroupcreate.php:138
-#: actions/apigroupismember.php:115 actions/apigroupjoin.php:160
-#: actions/apigroupleave.php:145 actions/apigrouplist.php:134
-#: actions/apigrouplistall.php:120 actions/apigroupmembership.php:105
-#: actions/apigroupshow.php:114 actions/apihelptest.php:84
-#: actions/apistatusesdestroy.php:101 actions/apistatusesretweets.php:110
-#: actions/apistatusesshow.php:105 actions/apistatusnetconfig.php:138
-#: actions/apistatusnetversion.php:91 actions/apisubscriptions.php:109
-#: actions/apitimelinefavorites.php:174 actions/apitimelinefriends.php:268
-#: actions/apitimelinegroup.php:147 actions/apitimelinehome.php:173
-#: actions/apitimelinementions.php:174 actions/apitimelinepublic.php:239
-#: actions/apitimelineretweetedtome.php:118
-#: actions/apitimelineretweetsofme.php:150 actions/apitimelinetag.php:159
-#: actions/apitimelineuser.php:206 actions/apiusershow.php:100
msgid "API method not found."
msgstr "لم يتم العثور على وسيلة API."
#. TRANS: Client error message. POST is a HTTP command. It should not be translated.
#. TRANS: Client error. POST is a HTTP command. It should not be translated.
-#: actions/apiaccountupdatedeliverydevice.php:83
-#: actions/apiaccountupdateprofile.php:85
-#: actions/apiaccountupdateprofilebackgroundimage.php:83
-#: actions/apiaccountupdateprofilecolors.php:106
-#: actions/apiaccountupdateprofileimage.php:80 actions/apiblockcreate.php:87
-#: actions/apiblockdestroy.php:85 actions/apidirectmessagenew.php:107
-#: actions/apifavoritecreate.php:88 actions/apifavoritedestroy.php:88
-#: actions/apifriendshipscreate.php:89 actions/apifriendshipsdestroy.php:89
-#: actions/apigroupcreate.php:102 actions/apigroupjoin.php:89
-#: actions/apigroupleave.php:89 actions/apimediaupload.php:66
-#: actions/apistatusesretweet.php:63 actions/apistatusesupdate.php:194
msgid "This method requires a POST."
msgstr "تتطلب هذه الطريقة POST."
#. TRANS: Client error displayed when no valid device parameter is provided for a user's delivery device setting.
-#: actions/apiaccountupdatedeliverydevice.php:103
msgid ""
"You must specify a parameter named 'device' with a value of one of: sms, im, "
"none."
msgstr ""
#. TRANS: Server error displayed when a user's delivery device cannot be updated.
-#. TRANS: Server error displayed when a user update to the database fails in the contact address confirmation action.
#. TRANS: Server error thrown on database error updating e-mail preferences.
#. TRANS: Server error thrown on database error removing a registered e-mail address.
-#. TRANS: Server error thrown on database error updating Instant Messaging preferences.
-#. TRANS: Server error thrown on database error removing a registered Instant Messaging address.
-#. TRANS: Server error displayed when "Other" settings in user profile could not be updated on the server.
#. TRANS: Server error thrown when user profile settings could not be updated.
#. TRANS: Server error thrown on database error updating SMS preferences.
#. TRANS: Server error thrown on database error removing a registered SMS phone number.
-#: actions/apiaccountupdatedeliverydevice.php:130
-#: actions/confirmaddress.php:118 actions/emailsettings.php:352
-#: actions/emailsettings.php:498 actions/imsettings.php:276
-#: actions/imsettings.php:432 actions/othersettings.php:186
-#: actions/profilesettings.php:322 actions/smssettings.php:301
-#: actions/smssettings.php:454
+#. TRANS: Server error displayed when "Other" settings in user profile could not be updated on the server.
msgid "Could not update user."
msgstr "تعذّر تحديث المستخدم."
@@ -349,32 +257,17 @@ msgstr "تعذّر تحديث المستخدم."
#. TRANS: Server error displayed when requesting Friends of a Friend feed for a user for which the profile could not be found.
#. TRANS: Server error displayed when trying to get a user hCard for a user without a profile.
#. TRANS: Server error displayed when calling a profile action while the specified user does not have a profile.
-#: actions/apiaccountupdateprofile.php:111
-#: actions/apiaccountupdateprofilebackgroundimage.php:199
-#: actions/apiaccountupdateprofilecolors.php:183
-#: actions/apiaccountupdateprofileimage.php:131
-#: actions/apiuserprofileimage.php:88 actions/apiusershow.php:108
-#: actions/avatarbynickname.php:85 actions/foaf.php:69 actions/hcard.php:75
-#: actions/replies.php:80 actions/usergroups.php:100 lib/galleryaction.php:66
-#: lib/profileaction.php:85
msgid "User has no profile."
msgstr "ليس للمستخدم ملف شخصي."
#. TRANS: Server error displayed if a user profile could not be saved.
#. TRANS: Server error thrown when user profile settings could not be saved.
-#: actions/apiaccountupdateprofile.php:147 actions/profilesettings.php:418
msgid "Could not save profile."
msgstr "لم يمكن حفظ الملف."
#. TRANS: Client error displayed when the number of bytes in a POST request exceeds a limit.
#. TRANS: %s is the number of bytes of the CONTENT_LENGTH.
#. TRANS: Form validation error in design settings form. POST should remain untranslated.
-#: actions/apiaccountupdateprofilebackgroundimage.php:108
-#: actions/apiaccountupdateprofileimage.php:95 actions/apimediaupload.php:81
-#: actions/apistatusesupdate.php:210 actions/avatarsettings.php:269
-#: actions/designadminpanel.php:120 actions/editapplication.php:121
-#: actions/newapplication.php:102 actions/newnotice.php:95
-#: lib/designsettings.php:298
#, php-format
msgid ""
"The server was unable to handle that much POST data (%s byte) due to its "
@@ -394,26 +287,15 @@ msgstr[5] ""
#. TRANS: Client error displayed when a database error occurs inserting profile colours.
#. TRANS: Client error displayed when a database error occurs updating profile colours.
#. TRANS: Form validation error displayed when group design settings could not be saved because of an application issue.
-#: actions/apiaccountupdateprofilebackgroundimage.php:138
-#: actions/apiaccountupdateprofilebackgroundimage.php:149
-#: actions/apiaccountupdateprofilecolors.php:160
-#: actions/apiaccountupdateprofilecolors.php:171
-#: actions/groupdesignsettings.php:285 actions/groupdesignsettings.php:296
-#: actions/userdesignsettings.php:201 actions/userdesignsettings.php:211
-#: actions/userdesignsettings.php:253 actions/userdesignsettings.php:263
msgid "Unable to save your design settings."
msgstr "تعذّر حفظ إعدادات تصميمك."
#. TRANS: Error displayed when updating design settings fails.
#. TRANS: Client error displayed when a database error occurs updating profile colours.
-#: actions/apiaccountupdateprofilebackgroundimage.php:191
-#: actions/apiaccountupdateprofilecolors.php:139
-#: actions/userdesignsettings.php:179
msgid "Could not update your design."
msgstr "تعذّر تحديث تصميمك."
#. TRANS: Title for Atom feed.
-#: actions/apiatomservice.php:85
msgctxt "ATOM"
msgid "Main"
msgstr ""
@@ -422,9 +304,6 @@ msgstr ""
#. TRANS: Message is used as link title. %s is a user nickname.
#. TRANS: Title in atom group notice feed. %s is a group name.
#. TRANS: Title in atom user notice feed. %s is a user name.
-#: actions/apiatomservice.php:93 actions/grouprss.php:138
-#: actions/userrss.php:94 lib/atomgroupnoticefeed.php:63
-#: lib/atomusernoticefeed.php:88
#, php-format
msgid "%s timeline"
msgstr "مسار %s الزمني"
@@ -434,8 +313,6 @@ msgstr "مسار %s الزمني"
#. TRANS: %s is a user nickname.
#. TRANS: Header for subscriptions overview for a user (first page).
#. TRANS: %s is a user nickname.
-#: actions/apiatomservice.php:104 actions/atompubsubscriptionfeed.php:148
-#: actions/subscriptions.php:51
#, php-format
msgid "%s subscriptions"
msgstr "اشتراكات %s"
@@ -443,58 +320,48 @@ msgstr "اشتراكات %s"
#. TRANS: Title for Atom feed with a user's favorite notices. %s is a user nickname.
#. TRANS: Title for Atom favorites feed.
#. TRANS: %s is a user nickname.
-#: actions/apiatomservice.php:115 actions/atompubfavoritefeed.php:142
-#, fuzzy, php-format
+#, php-format
msgid "%s favorites"
-msgstr "المفضلات"
+msgstr "مفضلات %s"
#. TRANS: Title for Atom feed with a user's memberships. %s is a user nickname.
-#: actions/apiatomservice.php:126
-#, fuzzy, php-format
+#, php-format
msgid "%s memberships"
-msgstr "أعضاء مجموعة %s"
+msgstr "عضويات %s"
#. TRANS: Client error displayed when users try to block themselves.
-#: actions/apiblockcreate.php:105
msgid "You cannot block yourself!"
msgstr "لا يمكنك منع نفسك!"
#. TRANS: Server error displayed when blocking a user has failed.
-#: actions/apiblockcreate.php:127
msgid "Block user failed."
msgstr "فشل منع المستخدم."
#. TRANS: Server error displayed when unblocking a user has failed.
-#: actions/apiblockdestroy.php:113
msgid "Unblock user failed."
msgstr "فشل إلغاء منع المستخدم."
#. TRANS: Title. %s is a user nickname.
-#: actions/apidirectmessage.php:88
#, php-format
msgid "Direct messages from %s"
msgstr "رسائل مباشرة من %s"
#. TRANS: Subtitle. %s is a user nickname.
-#: actions/apidirectmessage.php:93
#, php-format
msgid "All the direct messages sent from %s"
msgstr "جميع الرسائل المرسلة من %s"
#. TRANS: Title. %s is a user nickname.
-#: actions/apidirectmessage.php:102
#, php-format
msgid "Direct messages to %s"
msgstr "رسالة مباشرة %s"
#. TRANS: Subtitle. %s is a user nickname.
-#: actions/apidirectmessage.php:107
#, php-format
msgid "All the direct messages sent to %s"
msgstr "كل الرسائل المباشرة التي أرسلت إلى %s"
#. TRANS: Client error displayed when no message text was submitted (406).
-#: actions/apidirectmessagenew.php:117
msgid "No message text!"
msgstr "لا نص في الرسالة!"
@@ -502,7 +369,6 @@ msgstr "لا نص في الرسالة!"
#. TRANS: %d is the maximum number of characters for a message.
#. TRANS: Form validation error displayed when message content is too long.
#. TRANS: %d is the maximum number of characters for a message.
-#: actions/apidirectmessagenew.php:127 actions/newmessage.php:152
#, fuzzy, php-format
msgid "That's too long. Maximum message size is %d character."
msgid_plural "That's too long. Maximum message size is %d characters."
@@ -514,134 +380,111 @@ msgstr[4] "هذه طويلة جدًا. أطول حجم للإشعار %d حرف
msgstr[5] "هذه طويلة جدًا. أطول حجم للإشعار %d حرفًا."
#. TRANS: Client error displayed if a recipient user could not be found (403).
-#: actions/apidirectmessagenew.php:139
msgid "Recipient user not found."
msgstr "لم يُعثر على المستخدم المستلم."
#. TRANS: Client error displayed trying to direct message another user who's not a friend (403).
-#: actions/apidirectmessagenew.php:144
msgid "Cannot send direct messages to users who aren't your friend."
-msgstr ""
+msgstr "لا يمكن أن ترسل رسائل مباشرة إلى مستخدمين ليسوا أصدقاءك."
#. TRANS: Client error displayed trying to direct message self (403).
-#: actions/apidirectmessagenew.php:154
msgid ""
"Do not send a message to yourself; just say it to yourself quietly instead."
-msgstr ""
+msgstr "لا ترسل رسالة لنفسك؛ حسبك أن تقولها في سرك."
#. TRANS: Client error displayed when requesting a status with a non-existing ID.
#. TRANS: Client error displayed when trying to remove a favourite with an invalid ID.
#. TRANS: Client error displayed trying to delete a status with an invalid ID.
-#: actions/apifavoritecreate.php:108 actions/apifavoritedestroy.php:108
-#: actions/apistatusesdestroy.php:121
msgid "No status found with that ID."
msgstr "لا حالة وُجدت بهذا المُعرّف."
#. TRANS: Client error displayed when trying to mark a notice favourite that already is a favourite.
-#: actions/apifavoritecreate.php:120
msgid "This status is already a favorite."
msgstr "هذه الحالة مفضلة بالفعل."
#. TRANS: Client error displayed when marking a notice as favourite fails.
#. TRANS: Server error displayed when trying to mark a notice as favorite fails in the database.
#. TRANS: Error message text shown when a favorite could not be set.
-#: actions/apifavoritecreate.php:132 actions/favor.php:86 lib/command.php:294
msgid "Could not create favorite."
msgstr "تعذّر إنشاء مفضلة."
#. TRANS: Client error displayed when trying to remove a favourite that was not a favourite.
-#: actions/apifavoritedestroy.php:122
msgid "That status is not a favorite."
msgstr "تلك الحالة ليست مفضلة."
#. TRANS: Client error displayed when removing a favourite has failed.
#. TRANS: Server error displayed when removing a favorite from the database fails.
-#: actions/apifavoritedestroy.php:135 actions/disfavor.php:90
msgid "Could not delete favorite."
msgstr "تعذّر حذف المفضلة."
#. TRANS: Client error displayed when trying follow who's profile could not be found.
-#: actions/apifriendshipscreate.php:109
msgid "Could not follow user: profile not found."
msgstr "تعذر متابعة المستخدم: الحساب غير موجود."
#. TRANS: Client error displayed when trying to follow a user that's already being followed.
#. TRANS: %s is the nickname of the user that is already being followed.
-#: actions/apifriendshipscreate.php:120
#, php-format
msgid "Could not follow user: %s is already on your list."
msgstr "تعذر متابعة المستخدم: %s موجود في قائمتك مسبقًا."
#. TRANS: Client error displayed when trying to unfollow a user that cannot be found.
-#: actions/apifriendshipsdestroy.php:109
msgid "Could not unfollow user: User not found."
msgstr "تعذر إلغاء الاشتراك: المستخدم غير موجود."
#. TRANS: Client error displayed when trying to unfollow self.
-#: actions/apifriendshipsdestroy.php:121
msgid "You cannot unfollow yourself."
msgstr "لا يمكنك عدم متابعة نفسك."
#. TRANS: Client error displayed when supplying invalid parameters to an API call checking if a friendship exists.
-#: actions/apifriendshipsexists.php:88
msgid "Two valid IDs or nick names must be supplied."
msgstr ""
#. TRANS: Client error displayed when a source user could not be determined showing friendship.
-#: actions/apifriendshipsshow.php:131
msgid "Could not determine source user."
msgstr "تعذّر تحديد المستخدم المصدر."
#. TRANS: Client error displayed when a target user could not be determined showing friendship.
-#: actions/apifriendshipsshow.php:140
msgid "Could not find target user."
msgstr "تعذّر إيجاد المستخدم الهدف."
#. TRANS: Client error trying to create a group with a nickname this is already in use.
+#. TRANS: API validation exception thrown when nickname is already used.
#. TRANS: Group edit form validation error.
#. TRANS: Group create form validation error.
#. TRANS: Validation error in form for profile settings.
-#: actions/apigroupcreate.php:156 actions/editgroup.php:191
-#: actions/newgroup.php:137 actions/profilesettings.php:273
-#: actions/register.php:206
msgid "Nickname already in use. Try another one."
msgstr "الاسم المستعار مستخدم بالفعل. جرّب اسمًا آخرًا."
#. TRANS: Client error in form for group creation.
+#. TRANS: API validation exception thrown when nickname does not validate.
#. TRANS: Group edit form validation error.
#. TRANS: Group create form validation error.
#. TRANS: Validation error in form for profile settings.
-#: actions/apigroupcreate.php:164 actions/editgroup.php:195
-#: actions/newgroup.php:141 actions/profilesettings.php:243
-#: actions/register.php:208
msgid "Not a valid nickname."
msgstr "ليس اسمًا مستعارًا صحيحًا."
#. TRANS: Client error in form for group creation.
+#. TRANS: API validation exception thrown when homepage URL does not validate.
#. TRANS: Validation error shown when providing an invalid homepage URL in the "Edit application" form.
#. TRANS: Group edit form validation error.
#. TRANS: Validation error shown when providing an invalid homepage URL in the "New application" form.
#. TRANS: Group create form validation error.
#. TRANS: Validation error in form for profile settings.
-#: actions/apigroupcreate.php:181 actions/editapplication.php:235
-#: actions/editgroup.php:202 actions/newapplication.php:221
-#: actions/newgroup.php:148 actions/profilesettings.php:248
-#: actions/register.php:215
msgid "Homepage is not a valid URL."
msgstr "الصفحة الرئيسية ليست عنونًا صالحًا."
#. TRANS: Client error in form for group creation.
+#. TRANS: API validation exception thrown when full name does not validate.
#. TRANS: Group edit form validation error.
#. TRANS: Group create form validation error.
#. TRANS: Validation error in form for profile settings.
-#: actions/apigroupcreate.php:191 actions/editgroup.php:206
-#: actions/newgroup.php:152 actions/profilesettings.php:252
-#: actions/register.php:218
msgid "Full name is too long (maximum 255 characters)."
msgstr "الاسم الكامل طويل جدًا (الحد الأقصى 255 حرفًا)."
#. TRANS: Client error shown when providing too long a description during group creation.
#. TRANS: %d is the maximum number of allowed characters.
+#. TRANS: API validation exception thrown when description does not validate.
+#. TRANS: %d is the maximum description length and used for plural.
#. TRANS: Validation error shown when providing too long a description in the "Edit application" form.
#. TRANS: %d is the maximum number of allowed characters.
#. TRANS: Group edit form validation error.
@@ -649,9 +492,6 @@ msgstr "الاسم الكامل طويل جدًا (الحد الأقصى 255 ح
#. TRANS: %d is the maximum number of characters for the description.
#. TRANS: Group create form validation error.
#. TRANS: %d is the maximum number of allowed characters.
-#: actions/apigroupcreate.php:201 actions/editapplication.php:202
-#: actions/editgroup.php:211 actions/newapplication.php:182
-#: actions/newgroup.php:157
#, fuzzy, php-format
msgid "Description is too long (maximum %d character)."
msgid_plural "Description is too long (maximum %d characters)."
@@ -663,24 +503,21 @@ msgstr[4] "المنظمة طويلة جدا (الأقصى %d حرفا)."
msgstr[5] "المنظمة طويلة جدا (الأقصى %d حرفا)."
#. TRANS: Client error shown when providing too long a location during group creation.
+#. TRANS: API validation exception thrown when location does not validate.
#. TRANS: Group edit form validation error.
#. TRANS: Group create form validation error.
#. TRANS: Validation error in form for profile settings.
-#: actions/apigroupcreate.php:215 actions/editgroup.php:218
-#: actions/newgroup.php:164 actions/profilesettings.php:265
-#: actions/register.php:227
-#, fuzzy
msgid "Location is too long (maximum 255 characters)."
-msgstr "المنظمة طويلة جدا (الأقصى 255 حرفا)."
+msgstr "المنطقة طويلة جدا (الأقصى 255 حرفا)."
#. TRANS: Client error shown when providing too many aliases during group creation.
#. TRANS: %d is the maximum number of allowed aliases.
+#. TRANS: API validation exception thrown when aliases do not validate.
+#. TRANS: %d is the maximum number of aliases and used for plural.
#. TRANS: Group edit form validation error.
#. TRANS: %d is the maximum number of allowed aliases.
#. TRANS: Group create form validation error.
#. TRANS: %d is the maximum number of allowed aliases.
-#: actions/apigroupcreate.php:236 actions/editgroup.php:231
-#: actions/newgroup.php:177
#, fuzzy, php-format
msgid "Too many aliases! Maximum %d allowed."
msgid_plural "Too many aliases! Maximum %d allowed."
@@ -693,24 +530,24 @@ msgstr[5] "كنيات كيرة! العدد الأقصى هو %d."
#. TRANS: Client error shown when providing an invalid alias during group creation.
#. TRANS: %s is the invalid alias.
-#: actions/apigroupcreate.php:253
+#. TRANS: API validation exception thrown when aliases does not validate.
+#. TRANS: %s is the invalid alias.
#, php-format
msgid "Invalid alias: \"%s\"."
msgstr "كنية غير صالحة: \"%s\"."
#. TRANS: Client error displayed when trying to use an alias during group creation that is already in use.
#. TRANS: %s is the alias that is already in use.
+#. TRANS: API validation exception thrown when aliases is already used.
+#. TRANS: %s is the already used alias.
#. TRANS: Group edit form validation error.
#. TRANS: Group create form validation error. %s is the already used alias.
-#: actions/apigroupcreate.php:264 actions/editgroup.php:246
-#: actions/newgroup.php:193
#, fuzzy, php-format
msgid "Alias \"%s\" already in use. Try another one."
msgstr "هذا الاسم مستخدم بالفعل. جرّب اسمًا آخر."
#. TRANS: Client error displayed when trying to use an alias during group creation that is the same as the group's nickname.
#. TRANS: Group edit form validation error.
-#: actions/apigroupcreate.php:278 actions/editgroup.php:253
msgid "Alias can't be the same as nickname."
msgstr ""
@@ -718,18 +555,15 @@ msgstr ""
#. TRANS: Client error displayed when trying to join a group that does not exist.
#. TRANS: Client error displayed when trying to leave a group that does not exist.
#. TRANS: Client error displayed trying to show group membership on a non-existing group.
+#. TRANS: Client error displayed when not providing a group or an invalid group.
#. TRANS: Client error displayed when trying to show a group that could not be found.
#. TRANS: Client error displayed requesting most recent notices to a group for a non-existing group.
-#: actions/apigroupismember.php:95 actions/apigroupjoin.php:104
-#: actions/apigroupleave.php:104 actions/apigroupmembership.php:71
-#: actions/apigroupshow.php:81 actions/apitimelinegroup.php:89
msgid "Group not found."
msgstr "المجموعة غير موجودة."
#. TRANS: Server error displayed when trying to join a group the user is already a member of.
#. TRANS: Client error displayed when trying to join a group while already a member.
#. TRANS: Error text shown a user tries to join a group they already are a member of.
-#: actions/apigroupjoin.php:111 actions/joingroup.php:103 lib/command.php:333
#, fuzzy
msgid "You are already a member of that group."
msgstr "أنت بالفعل عضو في هذه المجموعة"
@@ -737,89 +571,99 @@ msgstr "أنت بالفعل عضو في هذه المجموعة"
#. TRANS: Server error displayed when trying to join a group the user is blocked from joining.
#. TRANS: Client error displayed when trying to join a group while being blocked form joining it.
#. TRANS: Error text shown when a user tries to join a group they are blocked from joining.
-#: actions/apigroupjoin.php:121 actions/joingroup.php:109 lib/command.php:338
#, fuzzy
msgid "You have been blocked from that group by the admin."
msgstr "لم تمنع هذا المستخدم."
-#. TRANS: Server error displayed when joining a group fails.
-#. TRANS: %1$s is a user nickname, $2$s is a group nickname.
#. TRANS: Server error displayed when joining a group failed in the database.
#. TRANS: %1$s is the joining user's nickname, $2$s is the group nickname for which the join failed.
#. TRANS: Message given having failed to add a user to a group.
#. TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
-#: actions/apigroupjoin.php:142 actions/joingroup.php:139 lib/command.php:350
#, php-format
msgid "Could not join user %1$s to group %2$s."
msgstr "لم يمكن ضم المستخدم %1$s إلى المجموعة %2$s."
#. TRANS: Server error displayed when trying to leave a group the user is not a member of.
-#: actions/apigroupleave.php:115
msgid "You are not a member of this group."
msgstr "لست عضوًا في هذه المجموعة"
-#. TRANS: Server error displayed when leaving a group fails.
-#. TRANS: %1$s is a user nickname, $2$s is a group nickname.
#. TRANS: Server error displayed when leaving a group failed in the database.
#. TRANS: %1$s is the leaving user's nickname, $2$s is the group nickname for which the leave failed.
#. TRANS: Message given having failed to remove a user from a group.
#. TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
-#: actions/apigroupleave.php:127 actions/leavegroup.php:133
-#: lib/command.php:398
#, php-format
msgid "Could not remove user %1$s from group %2$s."
msgstr "لم يمكن إزالة المستخدم %1$s من المجموعة %2$s."
#. TRANS: Used as title in check for group membership. %s is a user name.
-#: actions/apigrouplist.php:94
#, php-format
msgid "%s's groups"
msgstr "مجموعات %s"
-#. TRANS: Used as subtitle in check for group membership. %1$s is a user name, %2$s is the site name.
-#: actions/apigrouplist.php:104
+#. TRANS: Used as subtitle in check for group membership. %1$s is the site name, %2$s is a user name.
#, php-format
msgid "%1$s groups %2$s is a member of."
msgstr "مجموعات %1$s التي %2$s عضو فيها."
#. TRANS: Message is used as a title when listing the lastest 20 groups. %s is a site name.
#. TRANS: Message is used as a page title. %s is a nick name.
-#: actions/apigrouplistall.php:88 actions/usergroups.php:63
#, php-format
msgid "%s groups"
msgstr "مجموعات %s"
#. TRANS: Message is used as a subtitle when listing the lastest 20 groups. %s is a site name.
-#: actions/apigrouplistall.php:93
#, php-format
msgid "groups on %s"
msgstr "مجموعات %s"
-#. TRANS: Client error displayed when uploading a media file has failed.
-#: actions/apimediaupload.php:101
+#. TRANS: Client error displayed when trying to edit a group without being an admin.
+#. TRANS: Client error displayed trying to edit a group while not being a group admin.
+#. TRANS: Client error displayed trying to change group design settings without being a (group) admin.
+#. TRANS: Client error displayed when trying to change group logo settings while not being a group admin.
+msgid "You must be an admin to edit the group."
+msgstr "يجب أن تكون إداريا لتعدل المجموعة."
+
+#. TRANS: Server error displayed when group update fails.
+#. TRANS: Server error displayed when editing a group fails.
+msgid "Could not update group."
+msgstr "تعذر تحديث المجموعة."
+
+#. TRANS: Server error displayed when adding group aliases fails.
+#. TRANS: Server error displayed when group aliases could not be added.
+#. TRANS: Server exception thrown when creating group aliases failed.
+msgid "Could not create aliases."
+msgstr "تعذّر إنشاء الكنى."
+
+#. TRANS: API validation exception thrown when nickname does not validate.
+#. TRANS: Validation error in form for registration, profile and group settings, etc.
#, fuzzy
+msgid "Nickname must have only lowercase letters and numbers and no spaces."
+msgstr "1-64 حرفًا إنجليزيًا أو رقمًا بدون نقاط أو مسافات"
+
+#. TRANS: API validation exception thrown when alias is the same as nickname.
+#. TRANS: Group create form validation error.
+msgid "Alias cannot be the same as nickname."
+msgstr ""
+
+#. TRANS: Client error displayed when uploading a media file has failed.
msgid "Upload failed."
-msgstr "ارفع ملفًا"
+msgstr "فشل الرفع."
#. TRANS: Client error given from the OAuth API when the request token or verifier is invalid.
-#: actions/apioauthaccesstoken.php:102
#, fuzzy
msgid "Invalid request token or verifier."
msgstr "توكن دخول غير صحيح محدد."
#. TRANS: Client error given when no oauth_token was passed to the OAuth API.
-#: actions/apioauthauthorize.php:107
msgid "No oauth_token parameter provided."
msgstr ""
#. TRANS: Client error given when an invalid request token was passed to the OAuth API.
-#: actions/apioauthauthorize.php:115 actions/apioauthauthorize.php:129
#, fuzzy
msgid "Invalid request token."
msgstr "حجم غير صالح."
#. TRANS: Client error given when an invalid request token was passed to the OAuth API.
-#: actions/apioauthauthorize.php:121
#, fuzzy
msgid "Request token already authorized."
msgstr "لا تملك تصريحًا."
@@ -830,31 +674,14 @@ msgstr "لا تملك تصريحًا."
#. TRANS: Form validation error.
#. TRANS: Form validation error message.
#. TRANS: Client error displayed when the session token is not okay.
-#: actions/apioauthauthorize.php:147 actions/avatarsettings.php:280
-#: actions/deletenotice.php:177 actions/disfavor.php:75
-#: actions/emailsettings.php:291 actions/favor.php:75 actions/geocode.php:55
-#: actions/groupblock.php:65 actions/grouplogo.php:321
-#: actions/groupunblock.php:65 actions/imsettings.php:224
-#: actions/invite.php:60 actions/login.php:131 actions/makeadmin.php:66
-#: actions/newmessage.php:135 actions/newnotice.php:105 actions/nudge.php:80
-#: actions/oauthappssettings.php:165 actions/oauthconnectionssettings.php:136
-#: actions/othersettings.php:155 actions/passwordsettings.php:137
-#: actions/profilesettings.php:217 actions/recoverpassword.php:383
-#: actions/register.php:164 actions/remotesubscribe.php:76
-#: actions/repeat.php:82 actions/smssettings.php:250 actions/subedit.php:40
-#: actions/subscribe.php:87 actions/tagother.php:166
-#: actions/unsubscribe.php:69 actions/userauthorization.php:53
-#: lib/designsettings.php:310
msgid "There was a problem with your session token. Try again, please."
msgstr ""
#. TRANS: Form validation error given when an invalid username and/or password was passed to the OAuth API.
-#: actions/apioauthauthorize.php:168
msgid "Invalid nickname / password!"
msgstr "اسم/كلمة سر غير صحيحة!"
#. TRANS: Server error displayed when a database action fails.
-#: actions/apioauthauthorize.php:217
#, fuzzy
msgid "Database error inserting oauth_token_association."
msgstr "خطأ في قاعدة البيانات أثناء حذف مستخدم تطبيق OAuth."
@@ -871,28 +698,19 @@ msgstr "خطأ في قاعدة البيانات أثناء حذف مستخدم
#. TRANS: Client error displayed when unexpected data is posted in the password recovery form.
#. TRANS: Message given submitting a form with an unknown action in SMS settings.
#. TRANS: Unknown form validation error in design settings form.
-#: actions/apioauthauthorize.php:294 actions/avatarsettings.php:294
-#: actions/designadminpanel.php:100 actions/editapplication.php:144
-#: actions/emailsettings.php:310 actions/grouplogo.php:332
-#: actions/imsettings.php:239 actions/newapplication.php:124
-#: actions/oauthconnectionssettings.php:145 actions/recoverpassword.php:46
-#: actions/smssettings.php:271 lib/designsettings.php:321
msgid "Unexpected form submission."
msgstr ""
#. TRANS: Title for a page where a user can confirm/deny account access by an external application.
-#: actions/apioauthauthorize.php:387
msgid "An application would like to connect to your account"
msgstr ""
#. TRANS: Fieldset legend.
-#: actions/apioauthauthorize.php:404
msgid "Allow or deny access"
msgstr "اسمح أو امنع الوصول"
#. TRANS: User notification of external application requesting account access.
#. TRANS: %3$s is the access type requested (read-write or read-only), %4$s is the StatusNet sitename.
-#: actions/apioauthauthorize.php:425
#, php-format
msgid ""
"An application would like the ability to %3$s your %4$s "
@@ -903,7 +721,6 @@ msgstr ""
#. TRANS: User notification of external application requesting account access.
#. TRANS: %1$s is the application name requesting access, %2$s is the organisation behind the application,
#. TRANS: %3$s is the access type requested, %4$s is the StatusNet sitename.
-#: actions/apioauthauthorize.php:433
#, php-format
msgid ""
"The application %1$s by %2$s would like "
@@ -912,76 +729,55 @@ msgid ""
msgstr ""
#. TRANS: Fieldset legend.
-#: actions/apioauthauthorize.php:455
msgctxt "LEGEND"
msgid "Account"
msgstr "الحساب"
#. TRANS: Field label on OAuth API authorisation form.
+#. TRANS: Field label on login page.
#. TRANS: Field label in form for profile settings.
-#. TRANS: Label for group nickname (dt). Text hidden by default.
-#. TRANS: DT element on Authorise Subscription page.
-#. TRANS: DT for nick name in a profile.
-#: actions/apioauthauthorize.php:459 actions/login.php:242
-#: actions/profilesettings.php:106 actions/register.php:418
-#: actions/showgroup.php:243 actions/tagother.php:94
-#: actions/userauthorization.php:152 lib/groupeditform.php:145
-#: lib/userprofile.php:137
msgid "Nickname"
msgstr "الاسم المستعار"
#. TRANS: Field label on OAuth API authorisation form.
-#. TRANS: Link description in user account settings menu.
-#: actions/apioauthauthorize.php:463 actions/login.php:245
-#: actions/register.php:422 lib/accountsettingsaction.php:120
+#. TRANS: Field label on login page.
msgid "Password"
msgstr "كلمة السر"
#. TRANS: Button text that when clicked will cancel the process of allowing access to an account
#. TRANS: by an external application.
#. TRANS: Button label to cancel an e-mail address confirmation procedure.
-#. TRANS: Button label to cancel an Instant Messaging address confirmation procedure.
+#. TRANS: Button label to cancel an IM address confirmation procedure.
#. TRANS: Button label to cancel a SMS address confirmation procedure.
#. TRANS: Button label in the "Edit application" form.
-#: actions/apioauthauthorize.php:478 actions/emailsettings.php:124
-#: actions/imsettings.php:127 actions/smssettings.php:133
-#: lib/applicationeditform.php:351
msgctxt "BUTTON"
msgid "Cancel"
msgstr "ألغِ"
#. TRANS: Button text that when clicked will allow access to an account by an external application.
-#: actions/apioauthauthorize.php:485
msgctxt "BUTTON"
msgid "Allow"
msgstr "اسمح"
#. TRANS: Form instructions.
-#: actions/apioauthauthorize.php:502
msgid "Authorize access to your account information."
-msgstr ""
+msgstr "صرّح بالوصول إلى معلومات حسابك."
#. TRANS: Header for user notification after revoking OAuth access to an application.
-#: actions/apioauthauthorize.php:594
-#, fuzzy
msgid "Authorization canceled."
-msgstr "أُلغي تأكيد المراسلة الفورية."
+msgstr "ألغي التصريح."
#. TRANS: User notification after revoking OAuth access to an application.
#. TRANS: %s is an OAuth token.
-#: actions/apioauthauthorize.php:598
#, php-format
msgid "The request token %s has been revoked."
msgstr ""
#. TRANS: Title of the page notifying the user that an anonymous client application was successfully authorized to access the user's account with OAuth.
-#: actions/apioauthauthorize.php:621
-#, fuzzy
msgid "You have successfully authorized the application"
-msgstr "لا تملك تصريحًا."
+msgstr "صرّحت بنجاح للتطبيق"
#. TRANS: Message notifying the user that an anonymous client application was successfully authorized to access the user's account with OAuth.
-#: actions/apioauthauthorize.php:625
msgid ""
"Please return to the application and enter the following security code to "
"complete the process."
@@ -989,14 +785,12 @@ msgstr ""
#. TRANS: Title of the page notifying the user that the client application was successfully authorized to access the user's account with OAuth.
#. TRANS: %s is the authorised application name.
-#: actions/apioauthauthorize.php:632
-#, fuzzy, php-format
+#, php-format
msgid "You have successfully authorized %s"
-msgstr "لا تملك تصريحًا."
+msgstr "صرّحت بنجاح ل%s"
#. TRANS: Message notifying the user that the client application was successfully authorized to access the user's account with OAuth.
#. TRANS: %s is the authorised application name.
-#: actions/apioauthauthorize.php:639
#, php-format
msgid ""
"Please return to %s and enter the following security code to complete the "
@@ -1005,36 +799,28 @@ msgstr ""
#. TRANS: Client error displayed trying to delete a status not using POST or DELETE.
#. TRANS: POST and DELETE should not be translated.
-#: actions/apistatusesdestroy.php:111
#, fuzzy
msgid "This method requires a POST or DELETE."
msgstr "تتطلب هذه الطريقة POST."
#. TRANS: Client error displayed trying to delete a status of another user.
-#: actions/apistatusesdestroy.php:136
-#, fuzzy
msgid "You may not delete another user's status."
-msgstr "لا يمكنك حذف المستخدمين."
+msgstr "لا يسمح لك بحذف حالة مستخدم آخر."
#. TRANS: Client error displayed trying to repeat a non-existing notice through the API.
#. TRANS: Client error displayed trying to display redents of a non-exiting notice.
#. TRANS: Client exception thrown when referencing a non-existing notice.
#. TRANS: Error message displayed trying to delete a non-existing notice.
-#: actions/apistatusesretweet.php:74 actions/apistatusesretweets.php:70
-#: actions/atompubshowfavorite.php:82 actions/deletenotice.php:61
-#: actions/shownotice.php:92
msgid "No such notice."
msgstr "لا إشعار كهذا."
#. TRANS: Client error displayed trying to repeat an own notice through the API.
#. TRANS: Error text shown when trying to repeat an own notice.
-#: actions/apistatusesretweet.php:83 lib/command.php:537
msgid "Cannot repeat your own notice."
msgstr "لا يمكنك تكرار ملحوظتك الخاصة."
#. TRANS: Client error displayed trying to re-repeat a notice through the API.
#. TRANS: Error text shown when trying to repeat an notice that was already repeated by the user.
-#: actions/apistatusesretweet.php:92 lib/command.php:543
msgid "Already repeated that notice."
msgstr "كرر بالفعل هذه الملاحظة."
@@ -1044,59 +830,44 @@ msgstr "كرر بالفعل هذه الملاحظة."
#. TRANS: Client exception thrown when using an unsupported HTTP method.
#. TRANS: Client error shown when using a non-supported HTTP method.
#. TRANS: Client exception thrown when using an unsupported HTTP method.
-#: actions/apistatusesshow.php:118 actions/atompubfavoritefeed.php:103
-#: actions/atompubmembershipfeed.php:105 actions/atompubshowfavorite.php:117
-#: actions/atompubshowmembership.php:116
-#: actions/atompubshowsubscription.php:122
-#: actions/atompubsubscriptionfeed.php:109
#, fuzzy
msgid "HTTP method not supported."
msgstr "لم يتم العثور على وسيلة API."
#. TRANS: Exception thrown requesting an unsupported notice output format.
#. TRANS: %s is the requested output format.
-#: actions/apistatusesshow.php:144
-#, fuzzy, php-format
+#, php-format
msgid "Unsupported format: %s."
-msgstr "نسق غير مدعوم."
+msgstr "نسق غير مدعوم: %s."
#. TRANS: Client error displayed requesting a deleted status.
-#: actions/apistatusesshow.php:155
msgid "Status deleted."
msgstr "حُذِفت الحالة."
#. TRANS: Client error displayed requesting a status with an invalid ID.
-#: actions/apistatusesshow.php:162
msgid "No status with that ID found."
msgstr "لا حالة وُجدت بهذه الهوية."
#. TRANS: Client error displayed when trying to delete a notice not using the Atom format.
-#: actions/apistatusesshow.php:227
msgid "Can only delete using the Atom format."
msgstr ""
#. TRANS: Client error displayed when a user has no rights to delete notices of other users.
#. TRANS: Error message displayed trying to delete a notice that was not made by the current user.
-#: actions/apistatusesshow.php:235 actions/deletenotice.php:78
-#, fuzzy
msgid "Cannot delete this notice."
-msgstr "تعذّر حذف هذا الإشعار."
+msgstr "تعذر حذف هذا الإشعار."
#. TRANS: Confirmation of notice deletion in API. %d is the ID (number) of the deleted notice.
-#: actions/apistatusesshow.php:249
-#, fuzzy, php-format
+#, php-format
msgid "Deleted notice %d"
-msgstr "احذف الإشعار"
+msgstr "حُذِف الإشعار %d"
#. TRANS: Client error displayed when the parameter "status" is missing.
-#: actions/apistatusesupdate.php:221
msgid "Client must provide a 'status' parameter with a value."
msgstr ""
#. TRANS: Client error displayed when the parameter "status" is missing.
#. TRANS: %d is the maximum number of character for a notice.
-#: actions/apistatusesupdate.php:244 actions/newnotice.php:161
-#: lib/mailhandler.php:60
#, fuzzy, php-format
msgid "That's too long. Maximum notice size is %d character."
msgid_plural "That's too long. Maximum notice size is %d characters."
@@ -1108,14 +879,11 @@ msgstr[4] "هذه طويلة جدًا. أطول حجم للإشعار %d حرف
msgstr[5] "هذه طويلة جدًا. أطول حجم للإشعار %d حرفًا."
#. TRANS: Client error displayed when replying to a non-existing notice.
-#: actions/apistatusesupdate.php:284
-#, fuzzy
msgid "Parent notice not found."
-msgstr "لم يتم العثور على وسيلة API."
+msgstr "تعذر إيجاد الإشعار الوالد."
#. TRANS: Client error displayed exceeding the maximum notice length.
#. TRANS: %d is the maximum lenth for a notice.
-#: actions/apistatusesupdate.php:308 actions/newnotice.php:184
#, php-format
msgid "Maximum notice size is %d character, including attachment URL."
msgid_plural "Maximum notice size is %d characters, including attachment URL."
@@ -1128,257 +896,217 @@ msgstr[5] ""
#. TRANS: Client error displayed when requesting profiles of followers in an unsupported format.
#. TRANS: Client error displayed when requesting IDs of followers in an unsupported format.
-#: actions/apisubscriptions.php:228 actions/apisubscriptions.php:258
msgid "Unsupported format."
msgstr "نسق غير مدعوم."
#. TRANS: Title for timeline of most recent favourite notices by a user.
#. TRANS: %1$s is the StatusNet sitename, %2$s is a user nickname.
-#: actions/apitimelinefavorites.php:108
-#, fuzzy, php-format
+#, php-format
msgid "%1$s / Favorites from %2$s"
-msgstr "%1$s انضم للمجموعة %2$s"
+msgstr "%1$s / مفضلات %2$s"
#. TRANS: Subtitle for timeline of most recent favourite notices by a user.
#. TRANS: %1$s is the StatusNet sitename, %2$s is a user's full name,
#. TRANS: %3$s is a user nickname.
-#: actions/apitimelinefavorites.php:120
#, fuzzy, php-format
msgid "%1$s updates favorited by %2$s / %3$s."
msgstr "الإشعارات التي فضلها %1$s في %2$s!"
-#. TRANS: Server error displayed when generating an Atom feed fails.
-#. TRANS: %s is the error.
-#: actions/apitimelinegroup.php:134
-#, fuzzy, php-format
-msgid "Could not generate feed for group - %s"
-msgstr "تعذر تحديث المجموعة."
-
#. TRANS: Title for timeline of most recent mentions of a user.
#. TRANS: %1$s is the StatusNet sitename, %2$s is a user nickname.
-#: actions/apitimelinementions.php:115
-#, fuzzy, php-format
+#, php-format
msgid "%1$s / Updates mentioning %2$s"
-msgstr "حالة %1$s في يوم %2$s"
+msgstr "%1$s / التحديثات التي تذكر %2$s"
#. TRANS: Subtitle for timeline of most recent mentions of a user.
#. TRANS: %1$s is the StatusNet sitename, %2$s is a user nickname,
#. TRANS: %3$s is a user's full name.
-#: actions/apitimelinementions.php:131
#, php-format
msgid "%1$s updates that reply to updates from %2$s / %3$s."
msgstr ""
#. TRANS: Title for site timeline. %s is the StatusNet sitename.
#. TRANS: Public RSS feed title. %s is the StatusNet site name.
-#: actions/apitimelinepublic.php:193 actions/publicrss.php:103
#, php-format
msgid "%s public timeline"
msgstr "مسار %s الزمني العام"
#. TRANS: Subtitle for site timeline. %s is the StatusNet sitename.
-#: actions/apitimelinepublic.php:199
#, php-format
msgid "%s updates from everyone!"
msgstr ""
#. TRANS: Server error displayed calling unimplemented API method for 'retweeted by me'.
-#: actions/apitimelineretweetedbyme.php:71
#, fuzzy
msgid "Unimplemented."
msgstr "الأمر لم يُجهزّ بعد."
#. TRANS: Title for Atom feed "repeated to me". %s is the user nickname.
-#: actions/apitimelineretweetedtome.php:108
#, php-format
msgid "Repeated to %s"
msgstr "كرر إلى %s"
+#, fuzzy, php-format
+msgid "%1$s notices that were to repeated to %2$s / %3$s."
+msgstr "الإشعارات التي فضلها %1$s في %2$s!"
+
#. TRANS: Title of list of repeated notices of the logged in user.
#. TRANS: %s is the nickname of the logged in user.
-#: actions/apitimelineretweetsofme.php:112
#, php-format
msgid "Repeats of %s"
msgstr "تكرارات %s"
+#, fuzzy, php-format
+msgid "%1$s notices that %2$s / %3$s has repeated."
+msgstr "لقد أضاف %s (@%s) إشعارك إلى مفضلاته"
+
#. TRANS: Title for timeline with lastest notices with a given tag.
#. TRANS: %s is the tag.
-#: actions/apitimelinetag.php:101 actions/tag.php:67
+#. TRANS: Title for first page of notices with tags.
+#. TRANS: %s is the tag.
#, php-format
msgid "Notices tagged with %s"
msgstr "الإشعارات الموسومة ب%s"
#. TRANS: Subtitle for timeline with lastest notices with a given tag.
#. TRANS: %1$s is the tag, $2$s is the StatusNet sitename.
-#: actions/apitimelinetag.php:105 actions/tagrss.php:65
#, fuzzy, php-format
msgid "Updates tagged with %1$s on %2$s!"
msgstr "الإشعارات التي فضلها %1$s في %2$s!"
#. TRANS: Client error displayed trying to add a notice to another user's timeline.
-#: actions/apitimelineuser.php:297
#, fuzzy
msgid "Only the user can add to their own timeline."
msgstr "يستطيع المستخدمون الوالجون وحدهم تكرار الإشعارات."
#. TRANS: Client error displayed when using another format than AtomPub.
-#: actions/apitimelineuser.php:304
msgid "Only accept AtomPub for Atom feeds."
msgstr ""
#. TRANS: Client error displayed attempting to post an empty API notice.
-#: actions/apitimelineuser.php:311
msgid "Atom post must not be empty."
msgstr ""
#. TRANS: Client error displayed attempting to post an API that is not well-formed XML.
-#: actions/apitimelineuser.php:317
msgid "Atom post must be well-formed XML."
msgstr ""
#. TRANS: Client error displayed when not using an Atom entry.
-#: actions/apitimelineuser.php:323 actions/atompubfavoritefeed.php:228
-#: actions/atompubmembershipfeed.php:230
-#: actions/atompubsubscriptionfeed.php:236
msgid "Atom post must be an Atom entry."
msgstr ""
#. TRANS: Client error displayed when not using the POST verb. Do not translate POST.
-#: actions/apitimelineuser.php:334
msgid "Can only handle POST activities."
msgstr ""
#. TRANS: Client error displayed when using an unsupported activity object type.
#. TRANS: %s is the unsupported activity object type.
-#: actions/apitimelineuser.php:345
#, php-format
msgid "Cannot handle activity object type \"%s\"."
msgstr ""
#. TRANS: Client error displayed when posting a notice without content through the API.
#. TRANS: %d is the notice ID (number).
-#: actions/apitimelineuser.php:379
#, fuzzy, php-format
msgid "No content for notice %d."
msgstr "ابحث عن محتويات في الإشعارات"
#. TRANS: Client error displayed when using another format than AtomPub.
#. TRANS: %s is the notice URI.
-#: actions/apitimelineuser.php:408
#, fuzzy, php-format
msgid "Notice with URI \"%s\" already exists."
msgstr "لا ملف بهذه الهوية."
#. TRANS: Server error for unfinished API method showTrends.
-#: actions/apitrends.php:85
#, fuzzy
msgid "API method under construction."
msgstr "لم يتم العثور على وسيلة API."
#. TRANS: Client error displayed when requesting user information for a non-existing user.
-#: actions/apiuserprofileimage.php:80 actions/apiusershow.php:94
msgid "User not found."
msgstr "لم يُعثرعلى المستخدم."
#. TRANS: Client exception thrown when requesting a favorite feed for a non-existing profile.
#. TRANS: Client exception.
#. TRANS: Client error displayed trying to subscribe to a non-existing profile.
-#: actions/atompubfavoritefeed.php:69 actions/atompubmembershipfeed.php:71
-#: actions/atompubshowfavorite.php:75 actions/atompubshowmembership.php:72
-#: actions/subscribe.php:110
msgid "No such profile."
msgstr "لا ملف كهذا."
#. TRANS: Subtitle for Atom favorites feed.
#. TRANS: %1$s is a user nickname, %2$s is the StatusNet sitename.
-#: actions/atompubfavoritefeed.php:147
#, fuzzy, php-format
msgid "Notices %1$s has favorited on %2$s"
msgstr "الإشعارات التي فضلها %1$s في %2$s!"
#. TRANS: Client exception thrown when trying to set a favorite for another user.
#. TRANS: Client exception thrown when trying to subscribe another user.
-#: actions/atompubfavoritefeed.php:217 actions/atompubsubscriptionfeed.php:225
#, fuzzy
msgid "Cannot add someone else's subscription."
msgstr "تعذّر إدراج اشتراك جديد."
#. TRANS: Client exception thrown when trying use an incorrect activity verb for the Atom pub method.
-#: actions/atompubfavoritefeed.php:240
#, fuzzy
msgid "Can only handle favorite activities."
msgstr "ابحث عن محتويات في الإشعارات"
#. TRANS: Client exception thrown when trying favorite an object that is not a notice.
-#: actions/atompubfavoritefeed.php:250 actions/atompubmembershipfeed.php:250
#, fuzzy
msgid "Can only fave notices."
msgstr "ابحث عن محتويات في الإشعارات"
#. TRANS: Client exception thrown when trying favorite a notice without content.
-#: actions/atompubfavoritefeed.php:259
#, fuzzy
-msgid "Unknown note."
+msgid "Unknown notice."
msgstr "غير معروفة"
#. TRANS: Client exception thrown when trying favorite an already favorited notice.
-#: actions/atompubfavoritefeed.php:267
#, fuzzy
msgid "Already a favorite."
msgstr "أضف إلى المفضلات"
#. TRANS: Title for group membership feed.
#. TRANS: %s is a username.
-#: actions/atompubmembershipfeed.php:144
#, fuzzy, php-format
msgid "%s group memberships"
msgstr "أعضاء مجموعة %s"
#. TRANS: Subtitle for group membership feed.
#. TRANS: %1$s is a username, %2$s is the StatusNet sitename.
-#: actions/atompubmembershipfeed.php:149
#, fuzzy, php-format
msgid "Groups %1$s is a member of on %2$s"
msgstr "المجموعات التي %s عضو فيها"
#. TRANS: Client exception thrown when trying subscribe someone else to a group.
-#: actions/atompubmembershipfeed.php:219
#, fuzzy
msgid "Cannot add someone else's membership."
msgstr "تعذّر إدراج اشتراك جديد."
#. TRANS: Client error displayed when not using the POST verb.
#. TRANS: Do not translate POST.
-#: actions/atompubmembershipfeed.php:242
#, fuzzy
msgid "Can only handle join activities."
msgstr "ابحث عن محتويات في الإشعارات"
#. TRANS: Client exception thrown when trying to subscribe to a non-existing group.
-#: actions/atompubmembershipfeed.php:259
#, fuzzy
msgid "Unknown group."
msgstr "غير معروفة"
#. TRANS: Client exception thrown when trying to subscribe to an already subscribed group.
-#: actions/atompubmembershipfeed.php:267
#, fuzzy
msgid "Already a member."
msgstr "جميع الأعضاء"
#. TRANS: Client exception thrown when trying to subscribe to group while blocked from that group.
-#: actions/atompubmembershipfeed.php:275
msgid "Blocked by admin."
msgstr "أنت ممنوع من قِبل المدير."
#. TRANS: Client exception thrown when referencing a non-existing favorite.
-#: actions/atompubshowfavorite.php:90
#, fuzzy
msgid "No such favorite."
msgstr "لا ملف كهذا."
#. TRANS: Client exception thrown when trying to remove a favorite notice of another user.
-#: actions/atompubshowfavorite.php:151
#, fuzzy
msgid "Cannot delete someone else's favorite."
msgstr "تعذّر حذف المفضلة."
@@ -1405,93 +1133,67 @@ msgstr "تعذّر حذف المفضلة."
#. TRANS: Client error displayed when trying to join a non-existing group.
#. TRANS: Client error displayed when trying to leave a non-local group.
#. TRANS: Client error displayed when trying to leave a non-existing group.
+#. TRANS: Client error displayed when providing an invalid group ID on the Make Admin page.
#. TRANS: Client error displayed if no remote group with a given name was found requesting group page.
#. TRANS: Client error displayed if no local group with a given name was found requesting group page.
#. TRANS: Command exception text shown when a group is requested that does not exist.
#. TRANS: Error text shown when trying to leave a group that does not exist.
-#: actions/atompubshowmembership.php:81 actions/blockedfromgroup.php:81
-#: actions/blockedfromgroup.php:89 actions/deletegroup.php:87
-#: actions/deletegroup.php:100 actions/editgroup.php:102
-#: actions/foafgroup.php:46 actions/foafgroup.php:65 actions/foafgroup.php:73
-#: actions/groupblock.php:89 actions/groupbyid.php:83
-#: actions/groupdesignsettings.php:101 actions/grouplogo.php:103
-#: actions/groupmembers.php:84 actions/groupmembers.php:92
-#: actions/grouprss.php:97 actions/grouprss.php:105
-#: actions/groupunblock.php:89 actions/joingroup.php:82
-#: actions/joingroup.php:95 actions/leavegroup.php:82
-#: actions/leavegroup.php:95 actions/makeadmin.php:86
-#: actions/showgroup.php:134 actions/showgroup.php:143 lib/command.php:168
-#: lib/command.php:380
msgid "No such group."
msgstr "لا مجموعة كهذه."
#. TRANS: Client exception thrown when trying to show membership of a non-subscribed group
-#: actions/atompubshowmembership.php:91
#, fuzzy
msgid "Not a member."
msgstr "جميع الأعضاء"
#. TRANS: Client exception thrown when deleting someone else's membership.
-#: actions/atompubshowmembership.php:150
#, fuzzy
msgid "Cannot delete someone else's membership."
msgstr "تعذّر حفظ الاشتراك."
#. TRANS: Client exception thrown when trying to display a subscription for a non-existing profile ID.
#. TRANS: %d is the non-existing profile ID number.
-#: actions/atompubshowsubscription.php:72
-#: actions/atompubshowsubscription.php:83
-#: actions/atompubsubscriptionfeed.php:74
#, fuzzy, php-format
msgid "No such profile id: %d."
msgstr "لا ملف كهذا."
#. TRANS: Client exception thrown when trying to display a subscription for a non-subscribed profile ID.
#. TRANS: %1$d is the non-existing subscriber ID number, $2$d is the ID of the profile that was not subscribed to.
-#: actions/atompubshowsubscription.php:94
#, fuzzy, php-format
msgid "Profile %1$d not subscribed to profile %2$d."
msgstr "لست مُشتركًا بأي أحد."
#. TRANS: Client exception thrown when trying to delete a subscription of another user.
-#: actions/atompubshowsubscription.php:157
-#, fuzzy
msgid "Cannot delete someone else's subscription."
-msgstr "تعذّر حفظ الاشتراك."
+msgstr "لا يمكن أن تحذف اشتراك شخص آخر."
#. TRANS: Subtitle for Atom subscription feed.
#. TRANS: %1$s is a user nickname, %s$s is the StatusNet sitename.
-#: actions/atompubsubscriptionfeed.php:153
#, fuzzy, php-format
msgid "People %1$s has subscribed to on %2$s"
msgstr "الأشخاص المشتركون ب%s"
#. TRANS: Client error displayed when not using the follow verb.
-#: actions/atompubsubscriptionfeed.php:248
msgid "Can only handle Follow activities."
msgstr ""
#. TRANS: Client exception thrown when subscribing to an object that is not a person.
-#: actions/atompubsubscriptionfeed.php:256
msgid "Can only follow people."
msgstr ""
#. TRANS: Client exception thrown when subscribing to a non-existing profile.
#. TRANS: %s is the unknown profile ID.
-#: actions/atompubsubscriptionfeed.php:267
#, fuzzy, php-format
msgid "Unknown profile %s."
msgstr "نوع ملف غير معروف"
#. TRANS: Client error displayed trying to subscribe to an already subscribed profile.
#. TRANS: %s is the profile the user already has a subscription on.
-#: actions/atompubsubscriptionfeed.php:275
#, fuzzy, php-format
msgid "Already subscribed to %s."
msgstr "مُشترك أصلا!"
#. TRANS: Client error displayed trying to get a non-existing attachment.
-#: actions/attachment.php:73
msgid "No such attachment."
msgstr "لا مرفق كهذا."
@@ -1503,34 +1205,23 @@ msgstr "لا مرفق كهذا."
#. TRANS: Client error displayed when trying to view group members without providing a group nickname.
#. TRANS: Client error displayed when requesting a group RSS feed without providing a group nickname.
#. TRANS: Client error displayed if no nickname argument was given requesting a group page.
-#: actions/avatarbynickname.php:60 actions/blockedfromgroup.php:73
-#: actions/editgroup.php:85 actions/groupdesignsettings.php:84
-#: actions/grouplogo.php:86 actions/groupmembers.php:76
-#: actions/grouprss.php:89 actions/showgroup.php:116
msgid "No nickname."
msgstr "لا اسم مستعار."
#. TRANS: Client error displayed trying to get an avatar without providing an avatar size.
-#: actions/avatarbynickname.php:66
msgid "No size."
msgstr "لا حجم."
#. TRANS: Client error displayed trying to get an avatar providing an invalid avatar size.
-#: actions/avatarbynickname.php:72
msgid "Invalid size."
msgstr "حجم غير صالح."
#. TRANS: Title for avatar upload page.
-#. TRANS: Label for group avatar (dt). Text hidden by default.
-#. TRANS: Link description in user account settings menu.
-#: actions/avatarsettings.php:66 actions/showgroup.php:227
-#: lib/accountsettingsaction.php:113
msgid "Avatar"
msgstr "أفتار"
#. TRANS: Instruction for avatar upload page.
#. TRANS: %s is the maximum file size, for example "500b", "10kB" or "2MB".
-#: actions/avatarsettings.php:78
#, php-format
msgid "You can upload your personal avatar. The maximum file size is %s."
msgstr "بإمكانك رفع أفتارك الشخصي. أقصى حجم للملف هو %s."
@@ -1539,17 +1230,12 @@ msgstr "بإمكانك رفع أفتارك الشخصي. أقصى حجم للم
#. TRANS: Server error displayed coming across a request from a user without a profile.
#. TRANS: Server error displayed when trying to authorise a remote subscription request
#. TRANS: while the user has no profile.
-#: actions/avatarsettings.php:108 actions/avatarsettings.php:192
-#: actions/grouplogo.php:184 actions/remotesubscribe.php:190
-#: actions/userauthorization.php:75 actions/userrss.php:108
msgid "User without matching profile."
msgstr "المستخدم بدون ملف مطابق."
#. TRANS: Avatar upload page form legend.
#. TRANS: Avatar upload page crop form legend.
#. TRANS: Legend for group logo settings fieldset.
-#: actions/avatarsettings.php:122 actions/avatarsettings.php:205
-#: actions/grouplogo.php:261
msgid "Avatar settings"
msgstr "إعدادات الأفتار"
@@ -1557,8 +1243,6 @@ msgstr "إعدادات الأفتار"
#. TRANS: Header on avatar upload crop form for thumbnail of originally uploaded avatar (h2).
#. TRANS: Uploaded original file in group logo form.
#. TRANS: Header for originally uploaded file before a crop on the group logo page.
-#: actions/avatarsettings.php:131 actions/avatarsettings.php:214
-#: actions/grouplogo.php:207 actions/grouplogo.php:270
msgid "Original"
msgstr "الأصل"
@@ -1566,85 +1250,67 @@ msgstr "الأصل"
#. TRANS: Header on avatar upload crop form for thumbnail of to be used rendition of uploaded avatar (h2).
#. TRANS: Header for preview of to be displayed group logo.
#. TRANS: Header for the cropped group logo on the group logo page.
-#: actions/avatarsettings.php:147 actions/avatarsettings.php:227
-#: actions/grouplogo.php:219 actions/grouplogo.php:283
msgid "Preview"
msgstr "معاينة"
#. TRANS: Button on avatar upload page to delete current avatar.
#. TRANS: Button text for user account deletion.
-#: actions/avatarsettings.php:155 actions/deleteaccount.php:319
-#, fuzzy
msgctxt "BUTTON"
msgid "Delete"
msgstr "احذف"
#. TRANS: Button on avatar upload page to upload an avatar.
#. TRANS: Submit button to confirm upload of a user backup file for account restore.
-#: actions/avatarsettings.php:173 actions/restoreaccount.php:369
-#, fuzzy
msgctxt "BUTTON"
msgid "Upload"
msgstr "ارفع"
#. TRANS: Button on avatar upload crop form to confirm a selected crop as avatar.
-#: actions/avatarsettings.php:243
#, fuzzy
msgctxt "BUTTON"
msgid "Crop"
msgstr "مجموعات"
#. TRANS: Validation error on avatar upload form when no file was uploaded.
-#: actions/avatarsettings.php:318
msgid "No file uploaded."
msgstr "لم يُرفع ملف."
#. TRANS: Avatar upload form instruction after uploading a file.
-#: actions/avatarsettings.php:345
-#, fuzzy
msgid "Pick a square area of the image to be your avatar."
-msgstr "اختر منطقة مربعة من الصورة لتكون صورة أفتارك."
+msgstr "اختر منطقة مربعة من الصورة لتكون أفتارك."
#. TRANS: Server error displayed if an avatar upload went wrong somehow server side.
#. TRANS: Server error displayed trying to crop an uploaded group logo that is no longer present.
-#: actions/avatarsettings.php:360 actions/grouplogo.php:391
msgid "Lost our file data."
-msgstr ""
+msgstr "ضاع ملف البيانات."
#. TRANS: Success message for having updated a user avatar.
-#: actions/avatarsettings.php:384
msgid "Avatar updated."
msgstr "رُفع الأفتار."
#. TRANS: Error displayed on the avatar upload page if the avatar could not be updated for an unknown reason.
-#: actions/avatarsettings.php:388
msgid "Failed updating avatar."
msgstr "فشل تحديث الأفتار."
#. TRANS: Success message for deleting a user avatar.
-#: actions/avatarsettings.php:412
msgid "Avatar deleted."
msgstr "حُذف الأفتار."
#. TRANS: Title for backup account page.
#. TRANS: Option in profile settings to create a backup of the account of the currently logged in user.
-#: actions/backupaccount.php:61 actions/profilesettings.php:467
msgid "Backup account"
-msgstr ""
+msgstr "انسخ الحساب احتياطيا"
#. TRANS: Client exception thrown when trying to backup an account while not logged in.
-#: actions/backupaccount.php:79
#, fuzzy
msgid "Only logged-in users can backup their account."
msgstr "يستطيع المستخدمون الوالجون وحدهم تكرار الإشعارات."
#. TRANS: Client exception thrown when trying to backup an account without having backup rights.
-#: actions/backupaccount.php:84
msgid "You may not backup your account."
-msgstr ""
+msgstr "لا يسمح لك بنسخ حسابك احتياطيًا."
#. TRANS: Information displayed on the backup account page.
-#: actions/backupaccount.php:225
msgid ""
"You can backup your account data in Activity Streams format. This is an experimental feature and provides "
@@ -1652,33 +1318,31 @@ msgid ""
"addresses is not backed up. Additionally, uploaded files and direct messages "
"are not backed up."
msgstr ""
+"يمكنك نسخ بيانات حسابك بنسق Activity "
+"Streams. هذه الميزة تجريبية وتوفر نسخة احتياطية غير مكتملة. معلومات "
+"الحساب الخاصة كالبريد الإلكتروني وعناوين المحادثة الفورية لن تنسخ. الملفات "
+"المرفوعة والرسائل المباشرة أيضًا لن تنسخ."
#. TRANS: Submit button to backup an account on the backup account page.
-#: actions/backupaccount.php:248
-#, fuzzy
msgctxt "BUTTON"
msgid "Backup"
-msgstr "الخلفية"
+msgstr "انسخ احتياطيًا"
#. TRANS: Title for submit button to backup an account on the backup account page.
-#: actions/backupaccount.php:252
msgid "Backup your account."
-msgstr ""
+msgstr "انسخ حسابك احتياطيًا."
#. TRANS: Client error displayed when blocking a user that has already been blocked.
-#: actions/block.php:68
msgid "You already blocked that user."
msgstr "لقد منعتَ هذا المستخدم مسبقًا."
#. TRANS: Title for block user page.
#. TRANS: Legend for block user form.
#. TRANS: Fieldset legend for block user from group form.
-#: actions/block.php:106 actions/block.php:136 actions/groupblock.php:165
msgid "Block user"
msgstr "امنع المستخدم"
#. TRANS: Explanation of consequences when blocking a user on the block user page.
-#: actions/block.php:139
msgid ""
"Are you sure you want to block this user? Afterwards, they will be "
"unsubscribed from you, unable to subscribe to you in the future, and you "
@@ -1693,18 +1357,13 @@ msgstr ""
#. TRANS: Button label on the delete notice form.
#. TRANS: Button label on the delete user form.
#. TRANS: Button label on the form to block a user from a group.
-#: actions/block.php:154 actions/deleteapplication.php:157
-#: actions/deletegroup.php:220 actions/deletenotice.php:155
-#: actions/deleteuser.php:154 actions/groupblock.php:187
msgctxt "BUTTON"
msgid "No"
msgstr "لا"
#. TRANS: Submit button title for 'No' when blocking a user.
-#: actions/block.php:158
-#, fuzzy
msgid "Do not block this user."
-msgstr "لا تمنع هذا المستخدم"
+msgstr "لا تمنع هذا المستخدم."
#. TRANS: Button label on the user block form.
#. TRANS: Button label on the delete application form.
@@ -1712,293 +1371,244 @@ msgstr "لا تمنع هذا المستخدم"
#. TRANS: Button label on the delete notice form.
#. TRANS: Button label on the delete user form.
#. TRANS: Button label on the form to block a user from a group.
-#: actions/block.php:161 actions/deleteapplication.php:164
-#: actions/deletegroup.php:227 actions/deletenotice.php:162
-#: actions/deleteuser.php:161 actions/groupblock.php:194
msgctxt "BUTTON"
msgid "Yes"
msgstr "نعم"
#. TRANS: Submit button title for 'Yes' when blocking a user.
-#: actions/block.php:165
-#, fuzzy
msgid "Block this user."
-msgstr "امنع هذا المستخدم"
+msgstr "امنع هذا المستخدم."
#. TRANS: Server error displayed when blocking a user fails.
-#: actions/block.php:189
msgid "Failed to save block information."
msgstr "فشل حفظ معلومات المنع."
#. TRANS: Title for first page with list of users blocked from a group.
#. TRANS: %s is a group nickname.
-#: actions/blockedfromgroup.php:101
-#, fuzzy, php-format
+#, php-format
msgid "%s blocked profiles"
-msgstr "%1$s ملفات ممنوعة, الصفحة %2$d"
+msgstr "الملفات الممنوعة من %s"
#. TRANS: Title for any but the first page with list of users blocked from a group.
#. TRANS: %1$s is a group nickname, %2$d is a page number.
-#: actions/blockedfromgroup.php:106
#, php-format
msgid "%1$s blocked profiles, page %2$d"
msgstr "%1$s ملفات ممنوعة, الصفحة %2$d"
#. TRANS: Instructions for list of users blocked from a group.
-#: actions/blockedfromgroup.php:122
-#, fuzzy
msgid "A list of the users blocked from joining this group."
-msgstr "قائمة بمستخدمي هذه المجموعة."
+msgstr "قائمة بالمستخدمين الممنوعين من الانضمام إلى هذه المجموعة."
#. TRANS: Form legend for unblocking a user from a group.
-#: actions/blockedfromgroup.php:291
msgid "Unblock user from group"
msgstr "ألغ منع المستخدم من المجموعة"
#. TRANS: Button text for unblocking a user from a group.
-#: actions/blockedfromgroup.php:323
msgctxt "BUTTON"
msgid "Unblock"
msgstr "ألغِ المنع"
#. TRANS: Tooltip for button for unblocking a user from a group.
#. TRANS: Description of the form to unblock a user.
-#: actions/blockedfromgroup.php:327 lib/unblockform.php:78
msgid "Unblock this user"
msgstr "ألغِ منع هذا المستخدم"
#. TRANS: Title for mini-posting window loaded from bookmarklet.
#. TRANS: %s is the StatusNet site name.
-#: actions/bookmarklet.php:51
#, php-format
msgid "Post to %s"
msgstr "أرسل إلى %s"
#. TRANS: Client error displayed when not providing a confirmation code in the contact address confirmation action.
-#: actions/confirmaddress.php:74
msgid "No confirmation code."
msgstr "لا رمز تأكيد."
#. TRANS: Client error displayed when providing a non-existing confirmation code in the contact address confirmation action.
-#: actions/confirmaddress.php:80
msgid "Confirmation code not found."
msgstr "لم يوجد رمز التأكيد."
#. TRANS: Client error displayed when not providing a confirmation code for another user in the contact address confirmation action.
-#: actions/confirmaddress.php:86
msgid "That confirmation code is not for you!"
msgstr "رمز التأكيد ليس لك!"
-#. TRANS: Server error for a unknow address type %s, which can be 'email', 'jabber', or 'sms'.
-#: actions/confirmaddress.php:92
-#, php-format
-msgid "Unrecognized address type %s."
-msgstr ""
+#. TRANS: Server error for an unknown address type, which can be 'email', 'sms', or the name of an IM network (such as 'xmpp' or 'aim')
+#, fuzzy, php-format
+msgid "Unrecognized address type %s"
+msgstr "نوع رسالة غير مدعوم: %s"
#. TRANS: Client error for an already confirmed email/jabber/sms address.
-#: actions/confirmaddress.php:97
#, fuzzy
msgid "That address has already been confirmed."
msgstr "هذا البريد الإلكتروني ملك مستخدم آخر بالفعل."
+msgid "Couldn't update user."
+msgstr "تعذّر تحديث المستخدم."
+
+#, fuzzy
+msgid "Couldn't update user im preferences."
+msgstr "تعذّر تحديث سجل المستخدم."
+
+#, fuzzy
+msgid "Couldn't insert user im preferences."
+msgstr "تعذّر إدراج اشتراك جديد."
+
#. TRANS: Server error displayed when an address confirmation code deletion from the
#. TRANS: database fails in the contact address confirmation action.
-#: actions/confirmaddress.php:132
#, fuzzy
msgid "Could not delete address confirmation."
msgstr "تعذّر حذف تأكيد البريد المراسلة الفورية."
#. TRANS: Title for the contact address confirmation action.
-#: actions/confirmaddress.php:150
msgid "Confirm address"
msgstr "أكد العنوان"
#. TRANS: Success message for the contact address confirmation action.
#. TRANS: %s can be 'email', 'jabber', or 'sms'.
-#: actions/confirmaddress.php:166
#, php-format
msgid "The address \"%s\" has been confirmed for your account."
msgstr ""
#. TRANS: Title for page with a conversion (multiple notices in context).
-#: actions/conversation.php:96
msgid "Conversation"
msgstr "محادثة"
#. TRANS: Header on conversation page. Hidden by default (h2).
#. TRANS: Label for user statistics.
-#: actions/conversation.php:149 lib/noticelist.php:87
-#: lib/profileaction.php:246 lib/searchgroupnav.php:82
msgid "Notices"
msgstr "الإشعارات"
#. TRANS: Client exception displayed trying to delete a user account while not logged in.
-#: actions/deleteaccount.php:71
#, fuzzy
msgid "Only logged-in users can delete their account."
msgstr "يستطيع المستخدمون الوالجون وحدهم تكرار الإشعارات."
#. TRANS: Client exception displayed trying to delete a user account without have the rights to do that.
-#: actions/deleteaccount.php:77
#, fuzzy
msgid "You cannot delete your account."
msgstr "لا يمكنك حذف المستخدمين."
#. TRANS: Confirmation text for user deletion. The user has to type this exactly the same, including punctuation.
-#: actions/deleteaccount.php:160 actions/deleteaccount.php:297
msgid "I am sure."
msgstr "أنا متأكد."
#. TRANS: Notification for user about the text that must be input to be able to delete a user account.
#. TRANS: %s is the text that needs to be input.
-#: actions/deleteaccount.php:164
#, php-format
msgid "You must write \"%s\" exactly in the box."
msgstr "يجب أن تكتب \"%s\" كما هي في الصندوق."
#. TRANS: Confirmation that a user account has been deleted.
-#: actions/deleteaccount.php:206
#, fuzzy
msgid "Account deleted."
msgstr "حُذف الأفتار."
#. TRANS: Page title for page on which a user account can be deleted.
#. TRANS: Option in profile settings to delete the account of the currently logged in user.
-#: actions/deleteaccount.php:228 actions/profilesettings.php:475
-#, fuzzy
msgid "Delete account"
-msgstr "أنشئ حسابًا"
+msgstr "حذف الحساب"
#. TRANS: Form text for user deletion form.
-#: actions/deleteaccount.php:279
msgid ""
"This will permanently delete your account data from this "
"server."
-msgstr ""
+msgstr "سوف هذا الخيار بيانات حسابك من هذا الخادوم إلى الأبد."
#. TRANS: Additional form text for user deletion form shown if a user has account backup rights.
#. TRANS: %s is a URL to the backup page.
-#: actions/deleteaccount.php:285
#, php-format
msgid ""
"You are strongly advised to back up your data before "
"deletion."
-msgstr ""
+msgstr "يوصى بشدة أن تنسخ بيانتك احتياطيًا قبل الحذف."
#. TRANS: Field label for delete account confirmation entry.
#. TRANS: Field label for password reset form where the password has to be typed again.
-#: actions/deleteaccount.php:300 actions/passwordsettings.php:112
-#: actions/recoverpassword.php:262 actions/register.php:426
msgid "Confirm"
msgstr "أكّد"
#. TRANS: Input title for the delete account field.
#. TRANS: %s is the text that needs to be input.
-#: actions/deleteaccount.php:304
-#, fuzzy, php-format
+#, php-format
msgid "Enter \"%s\" to confirm that you want to delete your account."
-msgstr "لا يمكنك حذف المستخدمين."
+msgstr "أدخل \"%s\" لتأكيد رغبتك في حذف حسابك."
#. TRANS: Button title for user account deletion.
-#: actions/deleteaccount.php:323
-#, fuzzy
msgid "Permanently delete your account"
-msgstr "لا يمكنك حذف المستخدمين."
+msgstr "احذف حسابك إلى الأبد"
#. TRANS: Client error displayed trying to delete an application while not logged in.
-#: actions/deleteapplication.php:62
msgid "You must be logged in to delete an application."
msgstr "يجب أن تسجل الدخول لتحذف تطبيقا."
#. TRANS: Client error displayed trying to delete an application that does not exist.
-#: actions/deleteapplication.php:71
msgid "Application not found."
msgstr "لم يوجد التطبيق."
#. TRANS: Client error displayed trying to delete an application the current user does not own.
#. TRANS: Client error displayed trying to edit an application while not being its owner.
-#: actions/deleteapplication.php:79 actions/editapplication.php:78
-#: actions/showapplication.php:90
msgid "You are not the owner of this application."
msgstr "أنت لست مالك هذا التطبيق."
#. TRANS: Client error text when there is a problem with the session token.
-#: actions/deleteapplication.php:102 actions/editapplication.php:131
-#: actions/newapplication.php:112 actions/showapplication.php:113
-#: lib/action.php:1422
msgid "There was a problem with your session token."
msgstr ""
#. TRANS: Title for delete application page.
#. TRANS: Fieldset legend on delete application page.
-#: actions/deleteapplication.php:124 actions/deleteapplication.php:149
msgid "Delete application"
msgstr "احذف هذا التطبيق"
#. TRANS: Confirmation text on delete application page.
-#: actions/deleteapplication.php:152
msgid ""
"Are you sure you want to delete this application? This will clear all data "
"about the application from the database, including all existing user "
"connections."
msgstr ""
+"أمتأكد أنك ترغب في حذف هذا التطبيق؟ سوف يمسح هذا الخيار جميع بيانات التطبيق "
+"من قاعدة البيانات بما فيها جميع اتصالات المستخدمين."
#. TRANS: Submit button title for 'No' when deleting an application.
-#: actions/deleteapplication.php:161
-#, fuzzy
msgid "Do not delete this application."
-msgstr "لا تحذف هذا التطبيق"
+msgstr "لا تحذف هذا التطبيق."
#. TRANS: Submit button title for 'Yes' when deleting an application.
-#: actions/deleteapplication.php:167
-#, fuzzy
msgid "Delete this application."
-msgstr "احذف هذا التطبيق"
+msgstr "احذف هذا التطبيق."
#. TRANS: Client error when trying to delete group while not logged in.
-#: actions/deletegroup.php:64
-#, fuzzy
msgid "You must be logged in to delete a group."
-msgstr "يجب أن تلج لتغادر مجموعة."
+msgstr "يجب أن تدخل لتحذف مجموعة."
#. TRANS: Client error when trying to delete a group without providing a nickname or ID for the group.
#. TRANS: Client error displayed when trying to join a group without providing a group name or group ID.
#. TRANS: Client error displayed when trying to leave a group without providing a group name or group ID.
-#: actions/deletegroup.php:94 actions/joingroup.php:89
-#: actions/leavegroup.php:89
#, fuzzy
msgid "No nickname or ID."
msgstr "لا اسم مستعار."
#. TRANS: Client error when trying to delete a group without having the rights to delete it.
-#: actions/deletegroup.php:107
-#, fuzzy
msgid "You are not allowed to delete this group."
-msgstr "لست عضوًا في هذه المجموعة"
+msgstr "لا يسمح لك بحذف هذه المجموعة."
#. TRANS: Server error displayed if a group could not be deleted.
#. TRANS: %s is the name of the group that could not be deleted.
-#: actions/deletegroup.php:150
#, fuzzy, php-format
msgid "Could not delete group %s."
msgstr "تعذر تحديث المجموعة."
#. TRANS: Message given after deleting a group.
#. TRANS: %s is the deleted group's name.
-#: actions/deletegroup.php:159
#, fuzzy, php-format
msgid "Deleted group %s"
msgstr "%1$s ترك المجموعة %2$s"
#. TRANS: Title of delete group page.
#. TRANS: Form legend for deleting a group.
-#: actions/deletegroup.php:176 actions/deletegroup.php:202
#, fuzzy
msgid "Delete group"
msgstr "احذف المستخدم"
#. TRANS: Warning in form for deleleting a group.
-#: actions/deletegroup.php:206
msgid ""
"Are you sure you want to delete this group? This will clear all data about "
"the group from the database, without a backup. Public posts to this group "
@@ -2006,13 +1616,11 @@ msgid ""
msgstr ""
#. TRANS: Submit button title for 'No' when deleting a group.
-#: actions/deletegroup.php:224
#, fuzzy
msgid "Do not delete this group."
msgstr "لا تحذف هذا الإشعار"
#. TRANS: Submit button title for 'Yes' when deleting a group.
-#: actions/deletegroup.php:231
#, fuzzy
msgid "Delete this group."
msgstr "احذف هذا المستخدم"
@@ -2023,22 +1631,19 @@ msgstr "احذف هذا المستخدم"
#. TRANS: Client error displayed trying to block a user from a group while not logged in.
#. TRANS: Client error displayed when trying to unblock a user from a group while not logged in.
#. TRANS: Client error displayed trying to log out when not logged in.
+#. TRANS: Client error displayed when trying to access the "make admin" page while not logged in.
+#. TRANS: Client error displayed trying to create a new direct message while not logged in.
+#. TRANS: Client error displayed trying to send a notice while not logged in.
+#. TRANS: Client error displayed trying to nudge a user without being logged in.
+#. TRANS: Client error displayed when trying to enable or disable a plugin while not logged in.
#. TRANS: Client error displayed trying a change a subscription while not logged in.
#. TRANS: Client error displayed trying to subscribe when not logged in.
#. TRANS: Client error message thrown when trying to access the admin panel while not logged in.
#. TRANS: Client error displayed when trying to change user options while not logged in.
-#: actions/deletenotice.php:52 actions/disfavor.php:61 actions/favor.php:62
-#: actions/groupblock.php:60 actions/groupunblock.php:60 actions/logout.php:70
-#: actions/makeadmin.php:61 actions/newmessage.php:87 actions/newnotice.php:88
-#: actions/nudge.php:63 actions/subedit.php:33 actions/subscribe.php:98
-#: actions/tagother.php:33 actions/unsubscribe.php:52
-#: lib/adminpanelaction.php:71 lib/profileformaction.php:63
-#: lib/settingsaction.php:72
msgid "Not logged in."
msgstr "لست والجًا."
#. TRANS: Instructions for deleting a notice.
-#: actions/deletenotice.php:110
msgid ""
"You are about to permanently delete a notice. Once this is done, it cannot "
"be undone."
@@ -2046,154 +1651,124 @@ msgstr ""
#. TRANS: Page title when deleting a notice.
#. TRANS: Fieldset legend for the delete notice form.
-#: actions/deletenotice.php:117 actions/deletenotice.php:148
msgid "Delete notice"
msgstr "احذف الإشعار"
#. TRANS: Message for the delete notice form.
-#: actions/deletenotice.php:152
msgid "Are you sure you want to delete this notice?"
msgstr "أمتأكد من أنك تريد حذف هذا الإشعار؟"
#. TRANS: Submit button title for 'No' when deleting a notice.
-#: actions/deletenotice.php:159
#, fuzzy
msgid "Do not delete this notice."
msgstr "لا تحذف هذا الإشعار"
#. TRANS: Submit button title for 'Yes' when deleting a notice.
-#: actions/deletenotice.php:166
#, fuzzy
msgid "Delete this notice."
msgstr "احذف هذا الإشعار"
#. TRANS: Client error displayed when trying to delete a user without having the right to delete users.
-#: actions/deleteuser.php:66
msgid "You cannot delete users."
msgstr "لا يمكنك حذف المستخدمين."
#. TRANS: Client error displayed when trying to delete a non-local user.
-#: actions/deleteuser.php:74
msgid "You can only delete local users."
msgstr "يمكنك حذف المستخدمين المحليين فقط."
#. TRANS: Title of delete user page.
-#: actions/deleteuser.php:110
#, fuzzy
msgctxt "TITLE"
msgid "Delete user"
msgstr "احذف المستخدم"
#. TRANS: Fieldset legend on delete user page.
-#: actions/deleteuser.php:134
msgid "Delete user"
msgstr "احذف المستخدم"
#. TRANS: Information text to request if a user is certain that the described action has to be performed.
-#: actions/deleteuser.php:138
msgid ""
"Are you sure you want to delete this user? This will clear all data about "
"the user from the database, without a backup."
msgstr ""
+"أمتأكد أنك ترغب في حذف هذا المستخدم؟ سوف يؤدي ذلك إلى مسح كافة البيانات "
+"المتعلقة بالمستخدم من قاعدة البيانات دون نسخ احتياطي."
#. TRANS: Submit button title for 'No' when deleting a user.
-#: actions/deleteuser.php:158
-#, fuzzy
msgid "Do not delete this user."
-msgstr "لا تحذف هذا الإشعار"
+msgstr "لا تحذف هذا المستخدم."
#. TRANS: Submit button title for 'Yes' when deleting a user.
-#: actions/deleteuser.php:165
-#, fuzzy
msgid "Delete this user."
-msgstr "احذف هذا المستخدم"
+msgstr "احذف هذا المستخدم."
#. TRANS: Message used as title for design settings for the site.
-#. TRANS: Link description in user account settings menu.
-#: actions/designadminpanel.php:60 lib/accountsettingsaction.php:134
msgid "Design"
msgstr "التصميم"
#. TRANS: Instructions for design adminsitration panel.
-#: actions/designadminpanel.php:71
msgid "Design settings for this StatusNet site"
-msgstr ""
+msgstr "إعدادات تصميم موقع ستاسنت هذا"
#. TRANS: Client error displayed when a logo URL does is not valid.
-#: actions/designadminpanel.php:327
msgid "Invalid logo URL."
msgstr "مسار شعار غير صالح."
#. TRANS: Client error displayed when an SSL logo URL is invalid.
-#: actions/designadminpanel.php:333
#, fuzzy
msgid "Invalid SSL logo URL."
msgstr "مسار شعار غير صالح."
#. TRANS: Client error displayed when a theme is submitted through the form that is not in the theme list.
#. TRANS: %s is the chosen unavailable theme.
-#: actions/designadminpanel.php:339
#, php-format
msgid "Theme not available: %s."
msgstr "السمة غير متوفرة: %s"
#. TRANS: Fieldset legend for form to change logo.
-#: actions/designadminpanel.php:437
msgid "Change logo"
msgstr "غيّر الشعار"
#. TRANS: Field label for StatusNet site logo.
-#: actions/designadminpanel.php:444
msgid "Site logo"
msgstr "شعار الموقع"
#. TRANS: Field label for SSL StatusNet site logo.
-#: actions/designadminpanel.php:452
#, fuzzy
msgid "SSL logo"
msgstr "شعار الموقع"
#. TRANS: Fieldset legend for form change StatusNet site's theme.
-#: actions/designadminpanel.php:467
msgid "Change theme"
msgstr "غيّر السمة"
#. TRANS: Field label for dropdown to choose site theme.
-#: actions/designadminpanel.php:485
msgid "Site theme"
msgstr "سمة الموقع"
#. TRANS: Title for field label for dropdown to choose site theme.
-#: actions/designadminpanel.php:487
msgid "Theme for the site."
msgstr "سمة الموقع."
#. TRANS: Field label for uploading a cutom theme.
-#: actions/designadminpanel.php:494
msgid "Custom theme"
msgstr "سمة مخصصة"
#. TRANS: Form instructions for uploading a cutom StatusNet theme.
-#: actions/designadminpanel.php:499
msgid "You can upload a custom StatusNet theme as a .ZIP archive."
msgstr ""
#. TRANS: Fieldset legend for theme background image.
-#. TRANS: Fieldset legend on profile design page.
-#: actions/designadminpanel.php:515 lib/designsettings.php:98
msgid "Change background image"
msgstr "تغيير صورة الخلفية"
#. TRANS: Field label for background image on theme designer page.
#. TRANS: Field label for background color selector.
#. TRANS: Label on profile design page for setting a profile page background colour.
-#: actions/designadminpanel.php:525 actions/designadminpanel.php:609
-#: lib/designsettings.php:183
msgid "Background"
msgstr "الخلفية"
#. TRANS: Form guide for background image upload form on theme designer page.
-#: actions/designadminpanel.php:531
#, php-format
msgid ""
"You can upload a background image for the site. The maximum file size is %1"
@@ -2201,208 +1776,166 @@ msgid ""
msgstr "بإمكانك رفع صورة خلفية للموقع. أقصى حجم للملف هو %1$s."
#. TRANS: Used as radio button label to add a background image.
-#: actions/designadminpanel.php:558
msgid "On"
msgstr "مكّن"
#. TRANS: Used as radio button label to not add a background image.
-#: actions/designadminpanel.php:575
msgid "Off"
msgstr "عطّل"
#. TRANS: Form guide for turning background image on or off on theme designer page.
#. TRANS: Form guide for a set of radio buttons on the profile design page that will enable or disable
#. TRANS: use of the uploaded profile image.
-#: actions/designadminpanel.php:577 lib/designsettings.php:159
msgid "Turn background image on or off."
msgstr "مكّن صورة الخلفية أو عطّلها."
#. TRANS: Checkbox label to title background image on theme designer page.
#. TRANS: Checkbox label on profile design page that will cause the profile image to be tiled.
-#: actions/designadminpanel.php:583 lib/designsettings.php:165
-#, fuzzy
msgid "Tile background image"
-msgstr "تغيير صورة الخلفية"
+msgstr "كرّر صورة الخلفية"
#. TRANS: Fieldset legend for theme colors.
-#: actions/designadminpanel.php:598
-#, fuzzy
msgid "Change colors"
-msgstr "تغيير الألوان"
+msgstr "غيّر الألوان"
#. TRANS: Field label for content color selector.
#. TRANS: Label on profile design page for setting a profile page content colour.
-#: actions/designadminpanel.php:623 lib/designsettings.php:197
msgid "Content"
msgstr "المحتوى"
#. TRANS: Field label for sidebar color selector.
#. TRANS: Label on profile design page for setting a profile page sidebar colour.
-#: actions/designadminpanel.php:637 lib/designsettings.php:211
msgid "Sidebar"
msgstr "الشريط الجانبي"
#. TRANS: Field label for text color selector.
#. TRANS: Label on profile design page for setting a profile page text colour.
-#: actions/designadminpanel.php:651 lib/designsettings.php:225
msgid "Text"
msgstr "النص"
#. TRANS: Field label for link color selector.
#. TRANS: Label on profile design page for setting a profile page links colour.
-#: actions/designadminpanel.php:665 lib/designsettings.php:239
msgid "Links"
msgstr "وصلات"
#. TRANS: Fieldset legend for advanced theme design settings.
-#: actions/designadminpanel.php:691
msgid "Advanced"
msgstr "متقدم"
#. TRANS: Field label for custom CSS.
-#: actions/designadminpanel.php:696
msgid "Custom CSS"
msgstr "CSS مخصصة"
#. TRANS: Button text for resetting theme settings.
-#: actions/designadminpanel.php:718
-#, fuzzy
msgctxt "BUTTON"
msgid "Use defaults"
-msgstr "استخدم المبدئيات"
+msgstr "استخدم المبدئية"
#. TRANS: Title for button for resetting theme settings.
-#: actions/designadminpanel.php:720
-#, fuzzy
msgid "Restore default designs."
-msgstr "استعد التصميمات المبدئية"
+msgstr "استعد التصاميم المبدئية."
#. TRANS: Title for button for resetting theme settings.
-#: actions/designadminpanel.php:728
#, fuzzy
msgid "Reset back to default."
msgstr "ارجع إلى المبدئي"
#. TRANS: Title for button for saving theme settings.
-#: actions/designadminpanel.php:736
-#, fuzzy
msgid "Save design."
-msgstr "احفظ التصميم"
+msgstr "احفظ التصميم."
#. TRANS: Client error displayed when trying to remove favorite status for a notice that is not a favorite.
-#: actions/disfavor.php:83
msgid "This notice is not a favorite!"
msgstr "هذا الشعار ليس مفضلًا!"
#. TRANS: Title for page on which favorites can be added.
-#: actions/disfavor.php:98
msgid "Add to favorites"
msgstr "أضف إلى المفضلات"
#. TRANS: Client exception thrown when requesting a document from the documentation that does not exist.
#. TRANS: %s is the non-existing document.
-#: actions/doc.php:155
-#, fuzzy, php-format
+#, php-format
msgid "No such document \"%s\"."
-msgstr "لا مستند باسم \"%s\""
+msgstr "لا مستند باسم \"%s\"."
#. TRANS: Title for "Edit application" form.
#. TRANS: Form legend.
-#: actions/editapplication.php:54 lib/applicationeditform.php:129
msgid "Edit application"
msgstr "عدّل التطبيق"
#. TRANS: Client error displayed trying to edit an application while not logged in.
-#: actions/editapplication.php:66
msgid "You must be logged in to edit an application."
msgstr "يجب أن تكون مسجل الدخول لتعدل تطبيقا."
#. TRANS: Client error displayed trying to edit an application that does not exist.
-#: actions/editapplication.php:83 actions/showapplication.php:83
msgid "No such application."
msgstr "لا تطبيق كهذا."
#. TRANS: Instructions for "Edit application" form.
-#: actions/editapplication.php:167
msgid "Use this form to edit your application."
msgstr "استخدم هذا النموذج لتعدل تطبيقك."
#. TRANS: Validation error shown when not providing a name in the "Edit application" form.
#. TRANS: Validation error shown when not providing a name in the "New application" form.
-#: actions/editapplication.php:184 actions/newapplication.php:164
msgid "Name is required."
msgstr "الاسم مطلوب."
#. TRANS: Validation error shown when providing too long a name in the "Edit application" form.
#. TRANS: Validation error shown when providing too long a name in the "New application" form.
-#: actions/editapplication.php:188 actions/newapplication.php:172
-#, fuzzy
msgid "Name is too long (maximum 255 characters)."
-msgstr "الاسم طويل جدا (الأقصى 255 حرفا)."
+msgstr "الاسم طويل جدا (الحد الأقصى 255 حرفا)."
#. TRANS: Validation error shown when providing a name for an application that already exists in the "Edit application" form.
#. TRANS: Validation error shown when providing a name for an application that already exists in the "New application" form.
-#: actions/editapplication.php:192 actions/newapplication.php:168
#, fuzzy
msgid "Name already in use. Try another one."
msgstr "الاسم المستعار مستخدم بالفعل. جرّب اسمًا آخرًا."
#. TRANS: Validation error shown when not providing a description in the "Edit application" form.
#. TRANS: Validation error shown when not providing a description in the "New application" form.
-#: actions/editapplication.php:196 actions/newapplication.php:176
msgid "Description is required."
msgstr "الوصف مطلوب."
#. TRANS: Validation error shown when providing too long a source URL in the "Edit application" form.
-#: actions/editapplication.php:209
msgid "Source URL is too long."
msgstr "المسار المصدر طويل جدًا."
#. TRANS: Validation error shown when providing an invalid source URL in the "Edit application" form.
#. TRANS: Validation error shown when providing an invalid source URL in the "New application" form.
-#: actions/editapplication.php:216 actions/newapplication.php:199
msgid "Source URL is not valid."
msgstr "مسار المصدر ليس صحيحا."
#. TRANS: Validation error shown when not providing an organisation in the "Edit application" form.
#. TRANS: Validation error shown when not providing an organisation in the "New application" form.
-#: actions/editapplication.php:220 actions/newapplication.php:203
msgid "Organization is required."
msgstr "المنظمة مطلوبة."
#. TRANS: Validation error shown when providing too long an arganisation name in the "Edit application" form.
-#: actions/editapplication.php:224 actions/newapplication.php:207
-#, fuzzy
msgid "Organization is too long (maximum 255 characters)."
-msgstr "المنظمة طويلة جدا (الأقصى 255 حرفا)."
+msgstr "المنظمة طويلة جدا (الحد الأقصى 255 حرفا)."
#. TRANS: Form validation error show when an organisation name has not been provided in the edit application form.
#. TRANS: Form validation error show when an organisation name has not been provided in the new application form.
-#: actions/editapplication.php:228 actions/newapplication.php:211
msgid "Organization homepage is required."
msgstr "صفحة المنظمة الرئيسية مطلوبة."
#. TRANS: Validation error shown when providing too long a callback URL in the "Edit application" form.
#. TRANS: Validation error shown when providing too long a callback URL in the "New application" form.
-#: actions/editapplication.php:239 actions/newapplication.php:225
#, fuzzy
msgid "Callback is too long."
msgstr "المسار المصدر طويل جدًا."
#. TRANS: Validation error shown when providing an invalid callback URL in the "Edit application" form.
#. TRANS: Validation error shown when providing an invalid callback URL in the "New application" form.
-#: actions/editapplication.php:247 actions/newapplication.php:235
#, fuzzy
msgid "Callback URL is not valid."
msgstr "مسار المصدر ليس صحيحا."
#. TRANS: Server error occuring when an application could not be updated from the "Edit application" form.
-#: actions/editapplication.php:284
msgid "Could not update application."
msgstr "لم يمكن تحديث التطبيق."
#. TRANS: Title for form to edit a group. %s is a group nickname.
-#: actions/editgroup.php:55
#, php-format
msgid "Edit %s group"
msgstr "عدّل مجموعة %s"
@@ -2410,222 +1943,168 @@ msgstr "عدّل مجموعة %s"
#. TRANS: Client error displayed trying to edit a group while not logged in.
#. TRANS: Client error displayed when trying to create a group while not logged in.
#. TRANS: Client error displayed trying to create a group while not logged in.
-#: actions/editgroup.php:68 actions/grouplogo.php:69 actions/newgroup.php:65
msgid "You must be logged in to create a group."
msgstr "يجب أن تكون والجًا لتنشئ مجموعة."
-#. TRANS: Client error displayed trying to edit a group while not being a group admin.
-#. TRANS: Client error displayed trying to change group design settings without being a (group) admin.
-#. TRANS: Client error displayed when trying to change group logo settings while not being a group admin.
-#: actions/editgroup.php:110 actions/editgroup.php:176
-#: actions/groupdesignsettings.php:109 actions/grouplogo.php:111
-msgid "You must be an admin to edit the group."
-msgstr "يجب أن تكون إداريا لتعدل المجموعة."
-
#. TRANS: Form instructions for group edit form.
-#: actions/editgroup.php:161
msgid "Use this form to edit the group."
msgstr "استخدم هذا النموذج لتعديل المجموعة."
#. TRANS: Group edit form validation error.
#. TRANS: Group create form validation error.
#. TRANS: %s is the invalid alias.
-#: actions/editgroup.php:241 actions/newgroup.php:188
#, php-format
msgid "Invalid alias: \"%s\""
msgstr "كنية غير صالحة: \"%s\""
-#. TRANS: Server error displayed when editing a group fails.
-#: actions/editgroup.php:274
-msgid "Could not update group."
-msgstr "تعذر تحديث المجموعة."
-
-#. TRANS: Server error displayed when group aliases could not be added.
-#. TRANS: Server exception thrown when creating group aliases failed.
-#: actions/editgroup.php:281 classes/User_group.php:540
-msgid "Could not create aliases."
-msgstr "تعذّر إنشاء الكنى."
-
#. TRANS: Group edit form success message.
-#: actions/editgroup.php:301
msgid "Options saved."
msgstr "حُفظت الخيارات."
#. TRANS: Title for e-mail settings.
-#: actions/emailsettings.php:59
msgid "Email settings"
msgstr "إعدادات البريد الإلكتروني"
#. TRANS: E-mail settings page instructions.
#. TRANS: %%site.name%% is the name of the site.
-#: actions/emailsettings.php:73
#, php-format
msgid "Manage how you get email from %%site.name%%."
msgstr "أدر كيف تستلم البريد الإلكتروني من %%site.name%%."
#. TRANS: Form legend for e-mail settings form.
#. TRANS: Field label for e-mail address input in e-mail settings form.
-#: actions/emailsettings.php:103 actions/emailsettings.php:129
msgid "Email address"
msgstr "عنوان البريد الإلكتروني"
#. TRANS: Form note in e-mail settings form.
-#: actions/emailsettings.php:109
msgid "Current confirmed email address."
msgstr "عنوان البريد الإلكتروني المُؤكد الحالي."
#. TRANS: Button label to remove a confirmed e-mail address.
#. TRANS: Button label for removing a set sender e-mail address to post notices from.
-#. TRANS: Button label to remove a confirmed Instant Messaging address.
+#. TRANS: Button label to remove a confirmed IM address.
#. TRANS: Button label to remove a confirmed SMS address.
#. TRANS: Button label for removing a set sender SMS e-mail address to post notices from.
-#: actions/emailsettings.php:112 actions/emailsettings.php:179
-#: actions/imsettings.php:112 actions/smssettings.php:120
-#: actions/smssettings.php:176
msgctxt "BUTTON"
msgid "Remove"
msgstr "أزل"
#. TRANS: Form note in e-mail settings form.
-#: actions/emailsettings.php:119
msgid ""
"Awaiting confirmation on this address. Check your inbox (and spam box!) for "
"a message with further instructions."
msgstr ""
+"في انتظار تأكيد هذا العنوان. التمس رسالة تحوي مزيدًا من التعليمات في صندوق "
+"الوارد (وصندوق الرسائل المزعجة!)."
#. TRANS: Instructions for e-mail address input form. Do not translate
#. TRANS: "example.org". It is one of the domain names reserved for
#. TRANS: use in examples by http://www.rfc-editor.org/rfc/rfc2606.txt.
#. TRANS: Any other domain may be owned by a legitimate person or
#. TRANS: organization.
-#: actions/emailsettings.php:136
msgid "Email address, like \"UserName@example.org\""
msgstr "عنوان البريد الإلكتروني، مثل \"UserName@example.org\""
#. TRANS: Button label for adding an e-mail address in e-mail settings form.
-#. TRANS: Button label for adding an Instant Messaging address in Instant Messaging settings form.
+#. TRANS: Button label for adding an IM address in IM settings form.
#. TRANS: Button label for adding a SMS phone number in SMS settings form.
-#: actions/emailsettings.php:140 actions/imsettings.php:147
-#: actions/smssettings.php:158
msgctxt "BUTTON"
msgid "Add"
msgstr "أضف"
#. TRANS: Form legend for incoming e-mail settings form.
#. TRANS: Form legend for incoming SMS settings form.
-#: actions/emailsettings.php:148 actions/smssettings.php:167
msgid "Incoming email"
msgstr "البريد الإلكتروني الوارد"
#. TRANS: Checkbox label in e-mail preferences form.
-#: actions/emailsettings.php:154
msgid "I want to post notices by email."
msgstr "أريد أن أرسل الملاحظات عبر البريد الإلكتروني."
#. TRANS: Form instructions for incoming e-mail form in e-mail settings.
#. TRANS: Form instructions for incoming SMS e-mail address form in SMS settings.
-#: actions/emailsettings.php:176 actions/smssettings.php:174
msgid "Send email to this address to post new notices."
msgstr "أرسل بريدًا إلكترونيًا إلى هذا العنوان لترسل إشعارات جديدة."
#. TRANS: Instructions for incoming e-mail address input form, when an address has already been assigned.
#. TRANS: Instructions for incoming SMS e-mail address input form.
-#: actions/emailsettings.php:185 actions/smssettings.php:182
msgid "Make a new email address for posting to; cancels the old one."
msgstr "أنشئ عنوان بريد إلكتروني لترسل إليه؛ ألغِ القديم."
#. TRANS: Instructions for incoming e-mail address input form.
-#: actions/emailsettings.php:189
msgid ""
"To send notices via email, we need to create a unique email address for you "
"on this server:"
msgstr ""
+"لإرسال الإشعارات بالبريد الإلكتروني، يجب أن ننشئ بريدًا فريدًا لك على هذا "
+"الخادوم:"
#. TRANS: Button label for adding an e-mail address to send notices from.
#. TRANS: Button label for adding an SMS e-mail address to send notices from.
-#: actions/emailsettings.php:195 actions/smssettings.php:185
msgctxt "BUTTON"
msgid "New"
msgstr "جديد"
#. TRANS: Form legend for e-mail preferences form.
-#: actions/emailsettings.php:204
msgid "Email preferences"
msgstr "تفضيلات البريد الإلكتروني"
#. TRANS: Checkbox label in e-mail preferences form.
-#: actions/emailsettings.php:212
msgid "Send me notices of new subscriptions through email."
msgstr "أرسل لي إشعارات بالاشتراكات الجديدة عبر البريد الإلكتروني."
#. TRANS: Checkbox label in e-mail preferences form.
-#: actions/emailsettings.php:218
msgid "Send me email when someone adds my notice as a favorite."
msgstr "أرسل لي بريدًا إلكرتونيًا عندما يضيف أحدهم إشعاري مفضلة."
#. TRANS: Checkbox label in e-mail preferences form.
-#: actions/emailsettings.php:225
msgid "Send me email when someone sends me a private message."
msgstr "أرسل لي بريدًا إلكترونيًا عندما يرسل لي أحد رسالة خاصة."
#. TRANS: Checkbox label in e-mail preferences form.
-#: actions/emailsettings.php:231
msgid "Send me email when someone sends me an \"@-reply\"."
msgstr "أرسل لي بريدًا إلكترونيًا عندما يرسل لي أحد \"@-رد\"."
#. TRANS: Checkbox label in e-mail preferences form.
-#: actions/emailsettings.php:237
msgid "Allow friends to nudge me and send me an email."
msgstr "اسمح لأصدقائي بتنبيهي ومراسلتي عبر البريد الإلكتروني."
#. TRANS: Checkbox label in e-mail preferences form.
-#: actions/emailsettings.php:243
msgid "Publish a MicroID for my email address."
msgstr "انشر هوية مصغّرة لعنوان بريدي الإلكتروني."
#. TRANS: Confirmation message for successful e-mail preferences save.
-#: actions/emailsettings.php:361
msgid "Email preferences saved."
msgstr "حُفظت تفضيلات البريد الإلكرتوني."
#. TRANS: Message given saving e-mail address without having provided one.
-#: actions/emailsettings.php:380
msgid "No email address."
msgstr "لا عنوان بريد إلكتروني."
#. TRANS: Message given saving e-mail address that cannot be normalised.
-#: actions/emailsettings.php:388
#, fuzzy
msgid "Cannot normalize that email address."
msgstr "عنوان البريد الإلكتروني المُؤكد الحالي."
#. TRANS: Message given saving e-mail address that not valid.
-#: actions/emailsettings.php:393 actions/register.php:204
-#: actions/siteadminpanel.php:144
msgid "Not a valid email address."
msgstr "ليس عنوان بريد صالح."
#. TRANS: Message given saving e-mail address that is already set.
-#: actions/emailsettings.php:397
msgid "That is already your email address."
msgstr "هذا هو عنوان بريدك الإكتروني سابقًا."
#. TRANS: Message given saving e-mail address that is already set for another user.
-#: actions/emailsettings.php:401
msgid "That email address already belongs to another user."
msgstr "هذا البريد الإلكتروني ملك مستخدم آخر بالفعل."
#. TRANS: Server error thrown on database error adding e-mail confirmation code.
#. TRANS: Server error thrown on database error adding Instant Messaging confirmation code.
#. TRANS: Server error thrown on database error adding SMS confirmation code.
-#: actions/emailsettings.php:418 actions/imsettings.php:343
-#: actions/smssettings.php:365
-#, fuzzy
msgid "Could not insert confirmation code."
msgstr "تعذّر إدراج رمز التأكيد."
#. TRANS: Message given saving valid e-mail address that is to be confirmed.
-#: actions/emailsettings.php:425
msgid ""
"A confirmation code was sent to the email address you added. Check your "
"inbox (and spam box!) for the code and instructions on how to use it."
@@ -2634,105 +2113,85 @@ msgstr ""
#. TRANS: Message given canceling e-mail address confirmation that is not pending.
#. TRANS: Message given canceling Instant Messaging address confirmation that is not pending.
#. TRANS: Message given canceling SMS phone number confirmation that is not pending.
-#: actions/emailsettings.php:445 actions/imsettings.php:377
-#: actions/smssettings.php:399
#, fuzzy
msgid "No pending confirmation to cancel."
msgstr "أُلغي تأكيد المراسلة الفورية."
#. TRANS: Message given canceling e-mail address confirmation for the wrong e-mail address.
-#: actions/emailsettings.php:450
msgid "That is the wrong email address."
msgstr "هذا عنوان بريد إلكتروني خطأ."
#. TRANS: Server error thrown on database error canceling e-mail address confirmation.
#. TRANS: Server error thrown on database error canceling SMS phone number confirmation.
-#: actions/emailsettings.php:459 actions/smssettings.php:413
#, fuzzy
msgid "Could not delete email confirmation."
msgstr "تعذّر حذف تأكيد البريد الإلكتروني."
#. TRANS: Message given after successfully canceling e-mail address confirmation.
-#: actions/emailsettings.php:464
msgid "Email confirmation cancelled."
msgstr "أُلغي تأكيد البريد الإلكتروني."
#. TRANS: Message given trying to remove an e-mail address that is not
#. TRANS: registered for the active user.
-#: actions/emailsettings.php:483
msgid "That is not your email address."
msgstr "هذا ليس عنوان بريدك الإلكتروني."
#. TRANS: Message given after successfully removing a registered e-mail address.
-#: actions/emailsettings.php:504
msgid "The email address was removed."
msgstr "أزيل عنوان البريد الإلكتروني."
#. TRANS: Form validation error displayed when trying to remove an incoming e-mail address while no address has been set.
-#: actions/emailsettings.php:518 actions/smssettings.php:555
msgid "No incoming email address."
msgstr "لا عنوان بريد إلكتروني وارد."
#. TRANS: Server error thrown on database error removing incoming e-mail address.
#. TRANS: Server error thrown on database error adding incoming e-mail address.
#. TRANS: Server error displayed when the user could not be updated in SMS settings.
-#: actions/emailsettings.php:530 actions/emailsettings.php:554
-#: actions/smssettings.php:566 actions/smssettings.php:591
#, fuzzy
msgid "Could not update user record."
msgstr "تعذّر تحديث سجل المستخدم."
#. TRANS: Message given after successfully removing an incoming e-mail address.
#. TRANS: Confirmation text after updating SMS settings.
-#: actions/emailsettings.php:534 actions/smssettings.php:570
#, fuzzy
msgid "Incoming email address removed."
msgstr "لا عنوان بريد إلكتروني وارد."
#. TRANS: Message given after successfully adding an incoming e-mail address.
#. TRANS: Confirmation text after updating SMS settings.
-#: actions/emailsettings.php:558 actions/smssettings.php:595
#, fuzzy
msgid "New incoming email address added."
msgstr "لا عنوان بريد إلكتروني وارد."
#. TRANS: Client error displayed when trying to mark a notice as favorite that already is a favorite.
-#: actions/favor.php:80
msgid "This notice is already a favorite!"
msgstr "هذا الإشعار مفضلة مسبقًا!"
#. TRANS: Page title for page on which favorite notices can be unfavourited.
-#: actions/favor.php:95
#, fuzzy
msgid "Disfavor favorite."
msgstr "ألغِ تفضيل المفضلة"
#. TRANS: Page title for first page of favorited notices.
#. TRANS: Title for favourited notices section.
-#: actions/favorited.php:65 lib/popularnoticesection.php:62
-#: lib/publicgroupnav.php:93
msgid "Popular notices"
msgstr "إشعارات محبوبة"
#. TRANS: Page title for all but first page of favorited notices.
#. TRANS: %d is the page number being displayed.
-#: actions/favorited.php:69
#, php-format
msgid "Popular notices, page %d"
msgstr "إشعارات محبوبة، الصفحة %d"
#. TRANS: Description on page displaying favorited notices.
-#: actions/favorited.php:81
msgid "The most popular notices on the site right now."
msgstr "أشهر الإشعارات على الموقع حاليًا."
#. TRANS: Text displayed instead of a list when a site does not yet have any favourited notices.
-#: actions/favorited.php:149
msgid "Favorite notices appear on this page but no one has favorited one yet."
msgstr ""
#. TRANS: Additional text displayed instead of a list when a site does not yet have any favourited notices for logged in users.
-#: actions/favorited.php:153
msgid ""
"Be the first to add a notice to your favorites by clicking the fave button "
"next to any notice you like."
@@ -2740,7 +2199,6 @@ msgstr ""
#. TRANS: Additional text displayed instead of a list when a site does not yet have any favourited notices for not logged in users.
#. TRANS: %%action.register%% is a registration link. "[link text](link)" is Mark Down. Do not change the formatting.
-#: actions/favorited.php:158
#, php-format
msgid ""
"Why not [register an account](%%action.register%%) and be the first to add a "
@@ -2751,185 +2209,149 @@ msgstr ""
#. TRANS: %s is a user's nickname.
#. TRANS: Title for first page of favourite notices of a user.
#. TRANS: %s is the user for whom the favourite notices are displayed.
-#. TRANS: Tooltip for personal group navigation menu option when logged in for viewing own favourited notices.
-#: actions/favoritesrss.php:111 actions/showfavorites.php:76
-#: lib/personalgroupnav.php:122
#, php-format
msgid "%s's favorite notices"
msgstr "إشعارات %s المُفضلة"
#. TRANS: Desciption of RSS feed with favourite notices of a user.
#. TRANS: %1$s is a user's nickname, %2$s is the name of the StatusNet site.
-#: actions/favoritesrss.php:117
#, php-format
msgid "Updates favored by %1$s on %2$s!"
msgstr "الإشعارات التي فضلها %1$s في %2$s!"
#. TRANS: Page title for first page of featured users.
#. TRANS: Title for featured users section.
-#: actions/featured.php:69 lib/featureduserssection.php:87
-#: lib/publicgroupnav.php:89
msgid "Featured users"
msgstr "مستخدمون مختارون"
#. TRANS: Page title for all but first page of featured users.
#. TRANS: %d is the page number being displayed.
-#: actions/featured.php:73
#, php-format
msgid "Featured users, page %d"
msgstr "مستخدمون مختارون، صفحة %d"
#. TRANS: Description on page displaying featured users.
-#: actions/featured.php:102
#, fuzzy, php-format
msgid "A selection of some great users on %s."
msgstr "اختيار لبعض المستخدمين المتميزين على %s"
#. TRANS: Client error displayed when no notice ID was given trying do display a file.
-#: actions/file.php:36
msgid "No notice ID."
msgstr "لا رقم ملاحظة."
#. TRANS: Client error displayed when an invalid notice ID was given trying do display a file.
-#: actions/file.php:41
msgid "No notice."
msgstr "لا ملاحظة."
#. TRANS: Client error displayed when trying do display a file for a notice without a file attachement.
-#: actions/file.php:46
msgid "No attachments."
msgstr "لا مرفقات."
#. TRANS: Client error displayed when trying do display a file for a notice with file attachements
#. TRANS: that could not be found.
-#: actions/file.php:58
msgid "No uploaded attachments."
msgstr "لا مرفقات مرفوعة."
#. TRANS: Client error displayed when subscribing to a remote profile and an unexpected response is received.
-#: actions/finishremotesubscribe.php:69
msgid "Not expecting this response!"
msgstr "لم أتوقع هذا الرد!"
#. TRANS: Client error displayed when subscribing to a remote profile that does not exist.
-#: actions/finishremotesubscribe.php:81
msgid "User being listened to does not exist."
msgstr "المستخدم الذي تستمع إليه غير موجود."
#. TRANS: Client error displayed when subscribing to a remote profile that is a local profile.
-#: actions/finishremotesubscribe.php:89 actions/remotesubscribe.php:58
msgid "You can use the local subscription!"
msgstr "تستطيع استخدام الاشتراك المحلي!"
#. TRANS: Client error displayed when subscribing to a remote profile that is blocked form subscribing to.
-#: actions/finishremotesubscribe.php:102
#, fuzzy
msgid "That user has blocked you from subscribing."
msgstr "لقد منعك المستخدم."
#. TRANS: Client error displayed when subscribing to a remote profile without providing an authorised token.
-#: actions/finishremotesubscribe.php:114
msgid "You are not authorized."
msgstr "لا تملك تصريحًا."
#. TRANS: Client error displayed when subscribing to a remote profile and conversion of the request token to access token fails.
-#: actions/finishremotesubscribe.php:118
msgid "Could not convert request token to access token."
msgstr ""
#. TRANS: Client error displayed when subscribing to a remote profile fails because of an unsupported version of the OMB protocol.
-#: actions/finishremotesubscribe.php:124
msgid "Remote service uses unknown version of OMB protocol."
msgstr ""
#. TRANS: Server error displayed when subscribing to a remote profile fails because the remote profile could not be updated.
-#: actions/finishremotesubscribe.php:145 lib/oauthstore.php:317
msgid "Error updating remote profile."
msgstr "خطأ أثناء تحديث الملف الشخصي البعيد."
#. TRANS: Client error displayed when requesting a non-existent file.
-#: actions/getfile.php:77
msgid "No such file."
msgstr "لا ملف كهذا."
#. TRANS: Client error displayed when requesting a file without having read access to it.
-#: actions/getfile.php:82
msgid "Cannot read file."
msgstr "تعذّرت قراءة الملف."
#. TRANS: Client error displayed when trying to assign an invalid role to a user.
-#: actions/grantrole.php:61 actions/revokerole.php:62
#, fuzzy
msgid "Invalid role."
msgstr "حجم غير صالح."
#. TRANS: Client error displayed when trying to assign an reserved role to a user.
-#: actions/grantrole.php:66 actions/revokerole.php:66
msgid "This role is reserved and cannot be set."
msgstr ""
#. TRANS: Client error displayed when trying to assign a role to a user while not being allowed to set roles.
-#: actions/grantrole.php:76
msgid "You cannot grant user roles on this site."
msgstr "لا يمكنك سحب أدوار المستخدمين على هذا الموقع."
#. TRANS: Client error displayed when trying to assign a role to a user that already has that role.
-#: actions/grantrole.php:84
msgid "User already has this role."
msgstr "لدى المستخدم هذا الدور من قبل."
#. TRANS: Client error displayed trying to block a user from a group while not specifying a to be blocked user profile.
#. TRANS: Client error displayed when trying to unblock a user from a group without providing a profile.
+#. TRANS: Client error displayed when not providing a profile ID on the Make Admin page.
#. TRANS: Client error displayed trying a change a subscription without providing a profile.
#. TRANS: Client error displayed when trying to change user options without specifying a user to work on.
-#: actions/groupblock.php:71 actions/groupunblock.php:71
-#: actions/makeadmin.php:71 actions/subedit.php:49
-#: lib/profileformaction.php:79
msgid "No profile specified."
msgstr "لا ملف شخصي مُحدّد."
#. TRANS: Client error displayed trying to block a user from a group while specifying a non-existing profile.
#. TRANS: Client error displayed when trying to unblock a user from a group without providing an existing profile.
+#. TRANS: Client error displayed when specifying an invalid profile ID on the Make Admin page.
#. TRANS: Client error displayed trying a change a subscription for a non-existant profile ID.
#. TRANS: Client error displayed when trying to change user options without specifying an existing user to work on.
-#: actions/groupblock.php:77 actions/groupunblock.php:77
-#: actions/makeadmin.php:76 actions/subedit.php:57 actions/tagother.php:46
-#: actions/unsubscribe.php:84 lib/profileformaction.php:87
msgid "No profile with that ID."
msgstr "لا ملف شخصي بهذه الهوية."
#. TRANS: Client error displayed trying to block a user from a group while not specifying a group to block a profile from.
#. TRANS: Client error displayed when trying to unblock a user from a group without providing a group.
-#: actions/groupblock.php:83 actions/groupunblock.php:83
-#: actions/makeadmin.php:81
+#. TRANS: Client error displayed when not providing a group ID on the Make Admin page.
msgid "No group specified."
msgstr "لا مجموعة مُحدّدة."
#. TRANS: Client error displayed trying to block a user from a group while not being an admin user.
-#: actions/groupblock.php:95
msgid "Only an admin can block group members."
msgstr ""
#. TRANS: Client error displayed trying to block a user from a group while user is already blocked from the given group.
-#: actions/groupblock.php:100
#, fuzzy
msgid "User is already blocked from group."
msgstr "المستخدم ليس ممنوعًا من المجموعة."
#. TRANS: Client error displayed trying to block a user from a group while user is not a member of given group.
-#: actions/groupblock.php:106
msgid "User is not a member of group."
msgstr "المستخدم ليس عضوًا في المجموعة."
#. TRANS: Title for block user from group page.
#. TRANS: Form legend for form to block user from a group.
-#: actions/groupblock.php:141 actions/groupmembers.php:364
msgid "Block user from group"
msgstr "امنع المستخدم من المجموعة"
#. TRANS: Explanatory text for block user from group form before setting the block.
#. TRANS: %1$s is that to be blocked user, %2$s is the group the user will be blocked from.
-#: actions/groupblock.php:169
#, php-format
msgid ""
"Are you sure you want to block user \"%1$s\" from the group \"%2$s\"? They "
@@ -2938,40 +2360,33 @@ msgid ""
msgstr ""
#. TRANS: Submit button title for 'No' when blocking a user from a group.
-#: actions/groupblock.php:191
#, fuzzy
msgid "Do not block this user from this group."
msgstr "لا تمنع هذا المستخدم من هذه المجموعة"
#. TRANS: Submit button title for 'Yes' when blocking a user from a group.
-#: actions/groupblock.php:198
#, fuzzy
msgid "Block this user from this group."
msgstr "امنع هذا المستخدم من هذه المجموعة"
#. TRANS: Server error displayed when trying to block a user from a group fails because of an application error.
-#: actions/groupblock.php:215
msgid "Database error blocking user from group."
msgstr "خطأ في قاعدة البيانات أثناء منع المستخدم من المجموعة."
#. TRANS: Client error displayed referring to a group's permalink without providing a group ID.
#. TRANS: Client error displayed trying to find a user by ID without providing an ID.
-#: actions/groupbyid.php:73 actions/userbyid.php:70
msgid "No ID."
msgstr "لا هوية."
#. TRANS: Client error displayed trying to change group design settings while not logged in.
-#: actions/groupdesignsettings.php:67
msgid "You must be logged in to edit a group."
msgstr "يجب أن تلج لتُعدّل المجموعات."
#. TRANS: Title group design settings page.
-#: actions/groupdesignsettings.php:146
msgid "Group design"
msgstr "تصميم المجموعة"
#. TRANS: Instructions for group design settings page.
-#: actions/groupdesignsettings.php:157
#, fuzzy
msgid ""
"Customize the way your group looks with a background image and a colour "
@@ -2979,117 +2394,97 @@ msgid ""
msgstr "خصّص أسلوب عرض ملفك بصورة خلفية ومخطط ألوان من اختيارك."
#. TRANS: Form validation error displayed when group design settings could not be updated because of an application issue.
-#: actions/groupdesignsettings.php:262
#, fuzzy
msgid "Unable to update your design settings."
msgstr "تعذّر حفظ إعدادات تصميمك."
#. TRANS: Form text to confirm saved group design settings.
-#: actions/groupdesignsettings.php:307 actions/userdesignsettings.php:222
msgid "Design preferences saved."
msgstr "حُفظت تفضيلات التصميم."
#. TRANS: Title for group logo settings page.
#. TRANS: Group logo form legend.
-#: actions/grouplogo.php:144 actions/grouplogo.php:199
msgid "Group logo"
msgstr "شعار المجموعة"
#. TRANS: Instructions for group logo page.
#. TRANS: %s is the maximum file size for that site.
-#: actions/grouplogo.php:156
#, php-format
msgid ""
"You can upload a logo image for your group. The maximum file size is %s."
msgstr "بإمكانك رفع صورة شعار مجموعتك. أقصى حجم للملف هو %s."
#. TRANS: Submit button for uploading a group logo.
-#: actions/grouplogo.php:243
msgid "Upload"
msgstr "ارفع"
#. TRANS: Button text for cropping an uploaded group logo.
-#: actions/grouplogo.php:299
#, fuzzy
msgid "Crop"
msgstr "مجموعات"
#. TRANS: Form instructions on the group logo page.
-#: actions/grouplogo.php:376
msgid "Pick a square area of the image to be the logo."
msgstr "اختر منطقة مربعة من الصورة لتكون الشعار."
#. TRANS: Form success message after updating a group logo.
-#: actions/grouplogo.php:411
msgid "Logo updated."
msgstr "حُدّث الشعار."
#. TRANS: Form failure message after failing to update a group logo.
-#: actions/grouplogo.php:414
msgid "Failed updating logo."
msgstr "فشل رفع الشعار."
#. TRANS: Title of the page showing group members.
#. TRANS: %s is the name of the group.
-#: actions/groupmembers.php:104
#, php-format
msgid "%s group members"
msgstr "أعضاء مجموعة %s"
#. TRANS: Title of the page showing group members.
#. TRANS: %1$s is the name of the group, %2$d is the page number of the members list.
-#: actions/groupmembers.php:109
#, php-format
msgid "%1$s group members, page %2$d"
msgstr "%1$s أعضاء المجموعة, الصفحة %2$d"
#. TRANS: Page notice for group members page.
-#: actions/groupmembers.php:125
msgid "A list of the users in this group."
msgstr "قائمة بمستخدمي هذه المجموعة."
#. TRANS: Indicator in group members list that this user is a group administrator.
-#: actions/groupmembers.php:190
msgid "Admin"
msgstr "إداري"
#. TRANS: Button text for the form that will block a user from a group.
-#: actions/groupmembers.php:397
msgctxt "BUTTON"
msgid "Block"
msgstr "امنع"
#. TRANS: Submit button title.
-#: actions/groupmembers.php:401
msgctxt "TOOLTIP"
msgid "Block this user"
msgstr "امنع هذا المستخدم"
#. TRANS: Form legend for form to make a user a group admin.
-#: actions/groupmembers.php:488
msgid "Make user an admin of the group"
msgstr "اجعل المستخدم إداريًا في المجموعة"
#. TRANS: Button text for the form that will make a user administrator.
-#: actions/groupmembers.php:521
msgctxt "BUTTON"
msgid "Make Admin"
msgstr "اجعله إداريًا"
#. TRANS: Submit button title.
-#: actions/groupmembers.php:525
msgctxt "TOOLTIP"
msgid "Make this user an admin"
msgstr "اجعل هذا المستخدم إداريًا"
#. TRANS: Message is used as link description. %1$s is a username, %2$s is a site name.
-#: actions/grouprss.php:141
#, fuzzy, php-format
msgid "Updates from members of %1$s on %2$s!"
msgstr "الإشعارات التي فضلها %1$s في %2$s!"
#. TRANS: Title for first page of the groups list.
-#: actions/groups.php:62
#, fuzzy
msgctxt "TITLE"
msgid "Groups"
@@ -3097,7 +2492,6 @@ msgstr "مجموعات"
#. TRANS: Title for all but the first page of the groups list.
#. TRANS: %d is the page number.
-#: actions/groups.php:66
#, fuzzy, php-format
msgctxt "TITLE"
msgid "Groups, page %d"
@@ -3106,7 +2500,6 @@ msgstr "المجموعات، صفحة %d"
#. TRANS: Page notice of group list. %%%%site.name%%%% is the StatusNet site name,
#. TRANS: %%%%action.groupsearch%%%% and %%%%action.newgroup%%%% are URLs. Do not change them.
#. TRANS: This message contains Markdown links in the form [link text](link).
-#: actions/groups.php:95
#, fuzzy, php-format
msgid ""
"%%%%site.name%%%% groups let you find and talk with people of similar "
@@ -3121,12 +2514,10 @@ msgstr ""
"%%action.groupsearch%%%%) أو [ابدأ مجموعتك!](%%%%action.newgroup%%%%)"
#. TRANS: Link to create a new group on the group list page.
-#: actions/groups.php:113 actions/usergroups.php:126 lib/groupeditform.php:115
msgid "Create a new group"
msgstr "أنشئ مجموعة جديدة"
#. TRANS: Instructions for page where groups can be searched. %%site.name%% is the name of the StatusNet site.
-#: actions/groupsearch.php:53
#, php-format
msgid ""
"Search for groups on %%site.name%% by their name, location, or description. "
@@ -3136,21 +2527,17 @@ msgstr ""
"عبارات البحث بمسافات؛ ويجب أن تتكون تلك العبارات من 3 أحرف أو أكثر."
#. TRANS: Title for page where groups can be searched.
-#: actions/groupsearch.php:60
msgid "Group search"
msgstr "بحث في المجموعات"
#. TRANS: Text on page where groups can be searched if no results were found for a query.
#. TRANS: Text for notice search results is the query had no results.
#. TRANS: Message on the "People search" page where a query has no results.
-#: actions/groupsearch.php:82 actions/noticesearch.php:122
-#: actions/peoplesearch.php:87
msgid "No results."
msgstr "لا نتائج."
#. TRANS: Additional text on page where groups can be searched if no results were found for a query for a logged in user.
#. TRANS: This message contains Markdown links in the form [link text](link).
-#: actions/groupsearch.php:87
#, php-format
msgid ""
"If you cannot find the group you're looking for, you can [create it](%%"
@@ -3159,7 +2546,6 @@ msgstr ""
#. TRANS: Additional text on page where groups can be searched if no results were found for a query for a not logged in user.
#. TRANS: This message contains Markdown links in the form [link text](link).
-#: actions/groupsearch.php:92
#, php-format
msgid ""
"Why not [register an account](%%action.register%%) and [create the group](%%"
@@ -3167,218 +2553,182 @@ msgid ""
msgstr ""
#. TRANS: Client error displayed when trying to unblock a user from a group without being an administrator for the group.
-#: actions/groupunblock.php:95
msgid "Only an admin can unblock group members."
msgstr ""
#. TRANS: Client error displayed when trying to unblock a non-blocked user from a group.
-#: actions/groupunblock.php:100
msgid "User is not blocked from group."
msgstr "المستخدم ليس ممنوعًا من المجموعة."
#. TRANS: Server error displayed when unblocking a user from a group fails because of an unknown error.
#. TRANS: Server error displayed when removing a user block.
-#: actions/groupunblock.php:132 actions/unblock.php:86
msgid "Error removing the block."
msgstr "خطأ أثناء منع الحجب."
#. TRANS: Title for Instant Messaging settings.
-#: actions/imsettings.php:58
msgid "IM settings"
msgstr "إعدادات المراسلة الفورية"
#. TRANS: Instant messaging settings page instructions.
#. TRANS: [instant messages] is link text, "(%%doc.im%%)" is the link.
#. TRANS: the order and formatting of link text and link should remain unchanged.
-#: actions/imsettings.php:71
#, php-format
msgid ""
-"You can send and receive notices through Jabber/Google Talk [instant "
-"messages](%%doc.im%%). Configure your address and settings below."
+"You can send and receive notices through instant messaging [instant messages]"
+"(%%doc.im%%). Configure your addresses and settings below."
msgstr ""
-#. TRANS: Message given in the Instant Messaging settings if XMPP is not enabled on the site.
-#: actions/imsettings.php:90
+#. TRANS: Message given in the IM settings if IM is not enabled on the site.
msgid "IM is not available."
msgstr "المراسلة الفورية غير متوفرة."
-#. TRANS: Form legend for Instant Messaging settings form.
-#. TRANS: Field label for Instant Messaging address input in Instant Messaging settings form.
-#: actions/imsettings.php:102 actions/imsettings.php:132
+#, fuzzy, php-format
+msgid "Current confirmed %s address."
+msgstr "عنوان البريد الإلكتروني المُؤكد الحالي."
+
+#. TRANS: Form note in IM settings form.
+#. TRANS: %s is the IM address set for the site.
+#, php-format
+msgid ""
+"Awaiting confirmation on this address. Check your %s account for a message "
+"with further instructions. (Did you add %s to your buddy list?)"
+msgstr ""
+
msgid "IM address"
msgstr "عنوان المراسلة الفورية"
-#: actions/imsettings.php:109
+#, php-format
+msgid "%s screenname."
+msgstr ""
+
+#. TRANS: Header for IM preferences form.
#, fuzzy
-msgid "Current confirmed Jabber/Google Talk address."
-msgstr "عنوان جابر أو محادثة غوغل المعتمد حاليًا."
-
-#. TRANS: Form note in Instant Messaging settings form.
-#. TRANS: %s is the Instant Messaging address set for the site.
-#: actions/imsettings.php:120
-#, php-format
-msgid ""
-"Awaiting confirmation on this address. Check your Jabber/Google Talk account "
-"for a message with further instructions. (Did you add %s to your buddy list?)"
-msgstr ""
-
-#. TRANS: IM address input field instructions in Instant Messaging settings form.
-#. TRANS: %s is the Instant Messaging address set for the site.
-#. TRANS: Do not translate "example.org". It is one of the domain names reserved for use in examples by
-#. TRANS: http://www.rfc-editor.org/rfc/rfc2606.txt. Any other domain may be owned by a legitimate
-#. TRANS: person or organization.
-#: actions/imsettings.php:139
-#, php-format
-msgid ""
-"Jabber or Google Talk address, like \"UserName@example.org\". First, make "
-"sure to add %s to your buddy list in your IM client or on Google Talk."
-msgstr ""
-
-#. TRANS: Form legend for Instant Messaging preferences form.
-#: actions/imsettings.php:154
-msgid "IM preferences"
+msgid "IM Preferences"
msgstr "تفضيلات المحادثة الفورية"
-#. TRANS: Checkbox label in Instant Messaging preferences form.
-#: actions/imsettings.php:159
+#. TRANS: Checkbox label in IM preferences form.
#, fuzzy
-msgid "Send me notices through Jabber/Google Talk."
-msgstr "أرسل لي إشعارات بالاشتراكات الجديدة عبر البريد الإلكتروني."
+msgid "Send me notices"
+msgstr "أرسل إشعارًا"
-#. TRANS: Checkbox label in Instant Messaging preferences form.
-#: actions/imsettings.php:165
-msgid "Post a notice when my Jabber/Google Talk status changes."
+#. TRANS: Checkbox label in IM preferences form.
+msgid "Post a notice when my status changes."
msgstr ""
-#. TRANS: Checkbox label in Instant Messaging preferences form.
-#: actions/imsettings.php:171
-msgid ""
-"Send me replies through Jabber/Google Talk from people I'm not subscribed to."
+#. TRANS: Checkbox label in IM preferences form.
+msgid "Send me replies from people I'm not subscribed to."
msgstr ""
-#. TRANS: Checkbox label in Instant Messaging preferences form.
-#: actions/imsettings.php:178
+#. TRANS: Checkbox label in IM preferences form.
#, fuzzy
-msgid "Publish a MicroID for my Jabber/Google Talk address."
+msgid "Publish a MicroID"
msgstr "انشر هوية مصغّرة لعنوان بريدي الإلكتروني."
-#. TRANS: Confirmation message for successful Instant Messaging preferences save.
+#. TRANS: Server error thrown on database error updating IM preferences.
+#, fuzzy
+msgid "Couldn't update IM preferences."
+msgstr "تعذّر تحديث المستخدم."
+
+#. TRANS: Confirmation message for successful IM preferences save.
#. TRANS: Confirmation message after saving preferences.
-#: actions/imsettings.php:283 actions/othersettings.php:193
msgid "Preferences saved."
msgstr "حُفِظت التفضيلات."
-#. TRANS: Message given saving Instant Messaging address without having provided one.
-#: actions/imsettings.php:304
-msgid "No Jabber ID."
-msgstr "لا هوية جابر."
-
-#. TRANS: Message given saving Instant Messaging address that cannot be normalised.
-#: actions/imsettings.php:312
+#. TRANS: Message given saving IM address without having provided one.
#, fuzzy
-msgid "Cannot normalize that Jabber ID."
+msgid "No screenname."
+msgstr "لا اسم مستعار."
+
+#, fuzzy
+msgid "No transport."
+msgstr "لا ملاحظة."
+
+#. TRANS: Message given saving IM address that cannot be normalised.
+#, fuzzy
+msgid "Cannot normalize that screenname"
msgstr "ليست هوية جابر صالحة"
-#. TRANS: Message given saving Instant Messaging address that not valid.
-#: actions/imsettings.php:317
+#. TRANS: Message given saving IM address that not valid.
#, fuzzy
-msgid "Not a valid Jabber ID."
-msgstr "ليست هوية جابر صالحة"
+msgid "Not a valid screenname"
+msgstr "ليس اسمًا مستعارًا صحيحًا."
-#. TRANS: Message given saving Instant Messaging address that is already set.
-#: actions/imsettings.php:321
+#. TRANS: Message given saving IM address that is already set for another user.
#, fuzzy
-msgid "That is already your Jabber ID."
-msgstr "هذه ليست هويتك في جابر."
-
-#. TRANS: Message given saving Instant Messaging address that is already set for another user.
-#: actions/imsettings.php:325
-#, fuzzy
-msgid "Jabber ID already belongs to another user."
+msgid "Screenname already belongs to another user."
msgstr "هذا البريد الإلكتروني ملك مستخدم آخر بالفعل."
-#. TRANS: Message given saving valid Instant Messaging address that is to be confirmed.
-#. TRANS: %s is the Instant Messaging address set for the site.
-#: actions/imsettings.php:353
-#, php-format
-msgid ""
-"A confirmation code was sent to the IM address you added. You must approve %"
-"s for sending messages to you."
-msgstr ""
+#. TRANS: Message given saving valid IM address that is to be confirmed.
+#, fuzzy
+msgid "A confirmation code was sent to the IM address you added."
+msgstr "رمز التأكيد ليس لك!"
-#. TRANS: Message given canceling Instant Messaging address confirmation for the wrong IM address.
-#: actions/imsettings.php:382
+#. TRANS: Message given canceling IM address confirmation for the wrong IM address.
msgid "That is the wrong IM address."
msgstr "هذا عنوان محادثة فورية خاطئ."
-#. TRANS: Server error thrown on database error canceling Instant Messaging address confirmation.
-#: actions/imsettings.php:391
+#. TRANS: Server error thrown on database error canceling IM address confirmation.
#, fuzzy
-msgid "Could not delete IM confirmation."
+msgid "Couldn't delete confirmation."
msgstr "تعذّر حذف تأكيد البريد المراسلة الفورية."
-#. TRANS: Message given after successfully canceling Instant Messaging address confirmation.
-#: actions/imsettings.php:396
+#. TRANS: Message given after successfully canceling IM address confirmation.
msgid "IM confirmation cancelled."
msgstr "أُلغي تأكيد المراسلة الفورية."
-#. TRANS: Message given trying to remove an Instant Messaging address that is not
+#. TRANS: Message given trying to remove an IM address that is not
#. TRANS: registered for the active user.
-#: actions/imsettings.php:417
-msgid "That is not your Jabber ID."
-msgstr "هذه ليست هويتك في جابر."
+#, fuzzy
+msgid "That is not your screenname."
+msgstr "هذا ليس رقم هاتفك."
+
+#. TRANS: Server error thrown on database error removing a registered IM address.
+#, fuzzy
+msgid "Couldn't update user im prefs."
+msgstr "تعذّر تحديث سجل المستخدم."
#. TRANS: Message given after successfully removing a registered Instant Messaging address.
-#: actions/imsettings.php:440
msgid "The IM address was removed."
msgstr "أزيل عنوان المراسلة الفورية هذا."
#. TRANS: Title for all but the first page of the inbox page.
#. TRANS: %1$s is the user's nickname, %2$s is the page number.
-#: actions/inbox.php:59
#, php-format
msgid "Inbox for %1$s - page %2$d"
msgstr "صندوق %1$s الوارد - صفحة %2$d"
#. TRANS: Title for the first page of the inbox page.
#. TRANS: %s is the user's nickname.
-#: actions/inbox.php:64
#, php-format
msgid "Inbox for %s"
msgstr "صندوق %s الوارد"
#. TRANS: Instructions for user inbox page.
-#: actions/inbox.php:106
msgid "This is your inbox, which lists your incoming private messages."
msgstr "هذا صندوق بريدك الوارد، والذي يسرد رسائلك الخاصة الواردة."
#. TRANS: Client error displayed when trying to sent invites while they have been disabled.
-#: actions/invite.php:41
msgid "Invites have been disabled."
msgstr "تم تعطيل الدعوات."
#. TRANS: Client error displayed when trying to sent invites while not logged in.
#. TRANS: %s is the StatusNet site name.
-#: actions/invite.php:45
#, fuzzy, php-format
msgid "You must be logged in to invite other users to use %s."
msgstr "يجب أن تلج لتُعدّل المجموعات."
#. TRANS: Form validation message when providing an e-mail address that does not validate.
#. TRANS: %s is an invalid e-mail address.
-#: actions/invite.php:78
#, fuzzy, php-format
msgid "Invalid email address: %s."
msgstr "عنوان بريد إلكتروني غير صالح: %s"
#. TRANS: Page title when invitations have been sent.
-#: actions/invite.php:117
#, fuzzy
msgid "Invitations sent"
msgstr "أُرسلت الدعوة"
#. TRANS: Page title when inviting potential users.
-#: actions/invite.php:120
msgid "Invite new users"
msgstr "دعوة مستخدمين جدد"
@@ -3386,7 +2736,6 @@ msgstr "دعوة مستخدمين جدد"
#. TRANS: is already subscribed to one or more users with the given e-mail address(es).
#. TRANS: Plural form is based on the number of reported already subscribed e-mail addresses.
#. TRANS: Followed by a bullet list.
-#: actions/invite.php:140
#, fuzzy
msgid "You are already subscribed to this user:"
msgid_plural "You are already subscribed to these users:"
@@ -3399,7 +2748,6 @@ msgstr[5] "لست مشتركًا بأحد."
#. TRANS: Used as list item for already subscribed users (%1$s is nickname, %2$s is e-mail address).
#. TRANS: Used as list item for already registered people (%1$s is nickname, %2$s is e-mail address).
-#: actions/invite.php:146 actions/invite.php:160
#, fuzzy, php-format
msgctxt "INVITE"
msgid "%1$s (%2$s)"
@@ -3408,7 +2756,6 @@ msgstr "%1$s (%2$s)"
#. TRANS: Message displayed inviting users to use a StatusNet site while the invited user
#. TRANS: already uses a this StatusNet site. Plural form is based on the number of
#. TRANS: reported already present people. Followed by a bullet list.
-#: actions/invite.php:154
msgid "This person is already a user and you were automatically subscribed:"
msgid_plural ""
"These people are already users and you were automatically subscribed to them:"
@@ -3422,7 +2769,6 @@ msgstr[5] ""
#. TRANS: Message displayed inviting users to use a StatusNet site. Plural form is
#. TRANS: based on the number of invitations sent. Followed by a bullet list of
#. TRANS: e-mail addresses to which invitations were sent.
-#: actions/invite.php:168
msgid "Invitation sent to the following person:"
msgid_plural "Invitations sent to the following people:"
msgstr[0] ""
@@ -3434,40 +2780,34 @@ msgstr[5] ""
#. TRANS: Generic message displayed after sending out one or more invitations to
#. TRANS: people to join a StatusNet site.
-#: actions/invite.php:178
msgid ""
"You will be notified when your invitees accept the invitation and register "
"on the site. Thanks for growing the community!"
msgstr ""
#. TRANS: Form instructions.
-#: actions/invite.php:191
msgid ""
"Use this form to invite your friends and colleagues to use this service."
msgstr "استخدم هذا النموذج لدعوة أصدقائك وزملائك لاستخدام هذه الخدمة."
#. TRANS: Field label for a list of e-mail addresses.
-#: actions/invite.php:218
msgid "Email addresses"
msgstr "عناوين البريد الإلكتروني"
#. TRANS: Tooltip for field label for a list of e-mail addresses.
-#: actions/invite.php:221
msgid "Addresses of friends to invite (one per line)."
msgstr ""
#. TRANS: Field label for a personal message to send to invitees.
-#: actions/invite.php:225
msgid "Personal message"
msgstr "رسالة شخصية"
#. TRANS: Tooltip for field label for a personal message to send to invitees.
-#: actions/invite.php:228
msgid "Optionally add a personal message to the invitation."
msgstr ""
#. TRANS: Send button for inviting friends
-#: actions/invite.php:232
+#. TRANS: Button text for sending notice.
msgctxt "BUTTON"
msgid "Send"
msgstr "أرسل"
@@ -3475,7 +2815,6 @@ msgstr "أرسل"
#. TRANS: Subject for invitation email. Note that 'them' is correct as a gender-neutral
#. TRANS: singular 3rd-person pronoun in English. %1$s is the inviting user, $2$s is
#. TRANS: the StatusNet sitename.
-#: actions/invite.php:264
#, fuzzy, php-format
msgid "%1$s has invited you to join them on %2$s"
msgstr "%1$s يستمع الآن إلى إشعاراتك على %2$s."
@@ -3485,7 +2824,6 @@ msgstr "%1$s يستمع الآن إلى إشعاراتك على %2$s."
#. TRANS: StatusNet sitename, %3$s is the site URL, %4$s is the personal message from the
#. TRANS: inviting user, %s%5 a link to the timeline for the inviting user, %s$6 is a link
#. TRANS: to register with the StatusNet site.
-#: actions/invite.php:271
#, php-format
msgid ""
"%1$s has invited you to join them on %2$s (%3$s).\n"
@@ -3517,321 +2855,315 @@ msgid ""
msgstr ""
#. TRANS: Client error displayed when trying to join a group while not logged in.
-#: actions/joingroup.php:59
msgid "You must be logged in to join a group."
msgstr "يجب أن تلج لتنضم إلى مجموعة."
#. TRANS: Title for join group page after joining.
-#: actions/joingroup.php:147
#, fuzzy, php-format
msgctxt "TITLE"
msgid "%1$s joined group %2$s"
msgstr "%1$s انضم للمجموعة %2$s"
#. TRANS: Client error displayed when trying to leave a group while not logged in.
-#: actions/leavegroup.php:59
msgid "You must be logged in to leave a group."
msgstr "يجب أن تلج لتغادر مجموعة."
#. TRANS: Client error displayed when trying to join a group while already a member.
#. TRANS: Error text shown when trying to leave an existing group the user is not a member of.
-#: actions/leavegroup.php:103 lib/command.php:386
msgid "You are not a member of that group."
msgstr "لست عضوا في تلك المجموعة."
#. TRANS: Title for leave group page after leaving.
-#: actions/leavegroup.php:142
#, fuzzy, php-format
msgctxt "TITLE"
msgid "%1$s left group %2$s"
msgstr "%1$s ترك المجموعة %2$s"
#. TRANS: User admin panel title
-#: actions/licenseadminpanel.php:55
msgctxt "TITLE"
msgid "License"
msgstr ""
-#: actions/licenseadminpanel.php:65
+#. TRANS: Form instructions for the site license admin panel.
msgid "License for this StatusNet site"
msgstr ""
-#: actions/licenseadminpanel.php:134
+#. TRANS: Client error displayed selecting an invalid license in the license admin panel.
msgid "Invalid license selection."
msgstr ""
-#: actions/licenseadminpanel.php:144
+#. TRANS: Client error displayed when not specifying an owner for the all rights reserved license in the license admin panel.
msgid ""
"You must specify the owner of the content when using the All Rights Reserved "
"license."
msgstr ""
-#: actions/licenseadminpanel.php:151
+#. TRANS: Client error displayed selecting a too long license title in the license admin panel.
#, fuzzy
msgid "Invalid license title. Maximum length is 255 characters."
msgstr "رسالة ترحيب غير صالحة. أقصى طول هو 255 حرف."
-#: actions/licenseadminpanel.php:163
+#. TRANS: Client error displayed specifying an invalid license URL in the license admin panel.
msgid "Invalid license URL."
msgstr ""
-#: actions/licenseadminpanel.php:166
+#. TRANS: Client error displayed specifying an invalid license image URL in the license admin panel.
msgid "Invalid license image URL."
msgstr ""
-#: actions/licenseadminpanel.php:174
+#. TRANS: Client error displayed specifying an invalid license URL in the license admin panel.
msgid "License URL must be blank or a valid URL."
msgstr ""
-#: actions/licenseadminpanel.php:182
+#. TRANS: Client error displayed specifying an invalid license image URL in the license admin panel.
msgid "License image must be blank or valid URL."
msgstr ""
-#: actions/licenseadminpanel.php:232
+#. TRANS: Form legend in the license admin panel.
msgid "License selection"
msgstr ""
-#: actions/licenseadminpanel.php:238
+#. TRANS: License option in the license admin panel.
msgid "Private"
msgstr "خاص"
-#: actions/licenseadminpanel.php:239
+#. TRANS: License option in the license admin panel.
msgid "All Rights Reserved"
msgstr "جميع الحقوق محفوظة"
-#: actions/licenseadminpanel.php:240
+#. TRANS: License option in the license admin panel.
msgid "Creative Commons"
msgstr "المشاع المبدع"
-#: actions/licenseadminpanel.php:245
+#. TRANS: Dropdown field label in the license admin panel.
msgid "Type"
msgstr "النوع"
-#: actions/licenseadminpanel.php:247
-msgid "Select license"
+#. TRANS: Dropdown field instructions in the license admin panel.
+#, fuzzy
+msgid "Select a license."
msgstr "اختر رخصة"
-#: actions/licenseadminpanel.php:261
+#. TRANS: Form legend in the license admin panel.
msgid "License details"
msgstr ""
-#: actions/licenseadminpanel.php:267
+#. TRANS: Field label in the license admin panel.
msgid "Owner"
msgstr ""
-#: actions/licenseadminpanel.php:268
+#. TRANS: Field title in the license admin panel.
msgid "Name of the owner of the site's content (if applicable)."
msgstr ""
-#: actions/licenseadminpanel.php:276
+#. TRANS: Field label in the license admin panel.
msgid "License Title"
msgstr ""
-#: actions/licenseadminpanel.php:277
+#. TRANS: Field title in the license admin panel.
msgid "The title of the license."
msgstr ""
-#: actions/licenseadminpanel.php:285
+#. TRANS: Field label in the license admin panel.
msgid "License URL"
-msgstr ""
+msgstr "مسار الرخصة"
-#: actions/licenseadminpanel.php:286
+#. TRANS: Field title in the license admin panel.
msgid "URL for more information about the license."
-msgstr ""
+msgstr "مسار مزيد من المعلومات عن الرخصة."
-#: actions/licenseadminpanel.php:293
+#. TRANS: Field label in the license admin panel.
msgid "License Image URL"
-msgstr ""
+msgstr "مسار صورة الرخصة"
-#: actions/licenseadminpanel.php:294
+#. TRANS: Field title in the license admin panel.
msgid "URL for an image to display with the license."
-msgstr ""
+msgstr "مسار الصورة التي ستعرض مع الرخصة."
-#. TRANS: Submit button title.
-#: actions/licenseadminpanel.php:311 actions/sessionsadminpanel.php:199
-#: actions/siteadminpanel.php:292 actions/snapshotadminpanel.php:245
-#: actions/tagother.php:154 lib/applicationeditform.php:357
-msgid "Save"
-msgstr "أرسل"
-
-#: actions/licenseadminpanel.php:311
-msgid "Save license settings"
-msgstr ""
+#. TRANS: Button title in the license admin panel.
+#, fuzzy
+msgid "Save license settings."
+msgstr "احفظ إعدادات الرخصة"
+#. TRANS: Client error displayed when trying to log in while already logged in.
#. TRANS: Client error displayed trying to use "one time password login" when already logged in.
-#: actions/login.php:97 actions/otp.php:62 actions/register.php:137
msgid "Already logged in."
msgstr "والج بالفعل."
-#: actions/login.php:142
+#. TRANS: Form validation error displayed when trying to log in with incorrect credentials.
msgid "Incorrect username or password."
msgstr "اسم المستخدم أو كلمة السر غير صحيحان."
+#. TRANS: Server error displayed when during login a server error occurs.
#. TRANS: Server error displayed when a user object could not be created trying to login using "one time password login".
-#: actions/login.php:148 actions/otp.php:127
msgid "Error setting user. You are probably not authorized."
msgstr "خطأ أثناء ضبط المستخدم. لست مُصرحًا على الأرجح."
-#: actions/login.php:202 actions/login.php:253
+#. TRANS: Page title for login page.
msgid "Login"
msgstr "لُج"
-#: actions/login.php:239
+#. TRANS: Form legend on login page.
msgid "Login to site"
msgstr "لُج إلى الموقع"
-#: actions/login.php:248 actions/register.php:476
+#. TRANS: Checkbox label label on login page.
msgid "Remember me"
msgstr "تذكّرني"
-#: actions/login.php:249 actions/register.php:478
+#. TRANS: Checkbox title on login page.
msgid "Automatically login in the future; not for shared computers!"
msgstr "لُج تلقائيًا في المستقبل؛ هذا الخيار ليس مُعدًا للحواسيب المشتركة!"
-#: actions/login.php:259
+#. TRANS: Button text for log in on login page.
+#, fuzzy
+msgctxt "BUTTON"
+msgid "Login"
+msgstr "لُج"
+
+#. TRANS: Link text for link to "reset password" on login page.
msgid "Lost or forgotten password?"
msgstr "أنسيت كلمة السر؟"
-#: actions/login.php:277
+#. TRANS: Form instructions on login page before being able to change user settings.
msgid ""
"For security reasons, please re-enter your user name and password before "
"changing your settings."
msgstr ""
"لأسباب أمنية، من فضلك أعد إدخال اسم مستخدمك وكلمة سرك قبل تغيير إعداداتك."
-#: actions/login.php:281
-#, fuzzy
+#. TRANS: Form instructions on login page.
msgid "Login with your username and password."
-msgstr "لُج باسم مستخدم وكلمة سر"
+msgstr "ادخل باسم مستخدمك وكلمة سرك."
-#: actions/login.php:284
+#. TRANS: Form instructions on login page. This message contains Markdown links in the form [Link text](Link).
+#. TRANS: %%action.register%% is a link to the registration page.
#, php-format
msgid ""
"Don't have a username yet? [Register](%%action.register%%) a new account."
-msgstr ""
+msgstr "ليس لديك حساب؟ [سجل](%%action.register%%) حسابًا جديدًا"
-#: actions/makeadmin.php:92
+#. TRANS: Client error displayed when trying to make another user admin on the Make Admin page while not an admin.
msgid "Only an admin can make another user an admin."
-msgstr ""
+msgstr "يمكن فقط للإداري أن يجعل مستخدمًا آخرًا إداريًا."
-#: actions/makeadmin.php:96
-#, fuzzy, php-format
+#. TRANS: Client error displayed when trying to make another user admin on the Make Admin page who already is admin.
+#. TRANS: %1$s is the user that is already admin, %2$s is the group user is already admin for.
+#, php-format
msgid "%1$s is already an admin for group \"%2$s\"."
-msgstr "لم يمكن جعل %1$s إداريا للمجموعة %2$s."
+msgstr "%1$s إداري بالفعل لمجموعة \"%2$s\"."
-#: actions/makeadmin.php:133
+#. TRANS: Server error displayed when trying to make another user admin on the Make Admin page fails
+#. TRANS: because the group membership record could not be gotten.
+#. TRANS: %1$s is the to be admin user, %2$s is the group user should be admin for.
#, php-format
msgid "Can't get membership record for %1$s in group %2$s."
msgstr "لم يمكن الحصول على تسجيل العضوية ل%1$s في المجموعة %2$s."
-#: actions/makeadmin.php:146
+#. TRANS: Server error displayed when trying to make another user admin on the Make Admin page fails
+#. TRANS: because the group adminship record coud not be saved properly.
+#. TRANS: %1$s is the to be admin user, %2$s is the group user is already admin for.
#, php-format
msgid "Can't make %1$s an admin for group %2$s."
msgstr "لم يمكن جعل %1$s إداريا للمجموعة %2$s."
-#: actions/microsummary.php:69
+#. TRANS: Client error displayed trying to make a micro summary without providing a status.
msgid "No current status."
msgstr "لا حالة جارية."
#. TRANS: This is the title of the form for adding a new application.
-#: actions/newapplication.php:52
msgid "New application"
msgstr "تطبيق جديد"
#. TRANS: Client error displayed trying to add a new application while not logged in.
-#: actions/newapplication.php:64
msgid "You must be logged in to register an application."
msgstr "يجب أن تكون مسجل الدخول لتسجل تطبيقا."
#. TRANS: Form instructions for registering a new application.
-#: actions/newapplication.php:147
msgid "Use this form to register a new application."
msgstr "استخدم هذا النموذج لتسجل تطبيقا جديدا."
#. TRANS: Validation error shown when not providing a source URL in the "New application" form.
-#: actions/newapplication.php:189
#, fuzzy
msgid "Source URL is required."
msgstr "مسار المصدر ليس صحيحا."
#. TRANS: Server error displayed when an application could not be registered in the database through the "New application" form.
-#: actions/newapplication.php:279 actions/newapplication.php:289
msgid "Could not create application."
msgstr "لم يمكن إنشاء التطبيق."
+#, fuzzy
+msgid "Invalid image."
+msgstr "حجم غير صالح."
+
#. TRANS: Title for form to create a group.
-#: actions/newgroup.php:53
msgid "New group"
msgstr "مجموعة جديدة"
#. TRANS: Client exception thrown when a user tries to create a group while banned.
-#: actions/newgroup.php:73 classes/User_group.php:485
#, fuzzy
msgid "You are not allowed to create groups on this site."
msgstr "لست عضوًا في هذه المجموعة"
#. TRANS: Form instructions for group create form.
-#: actions/newgroup.php:117
msgid "Use this form to create a new group."
msgstr "استخدم هذا النموذج لإنشاء مجموعة جديدة."
-#. TRANS: Group create form validation error.
-#: actions/newgroup.php:200
-msgid "Alias cannot be the same as nickname."
-msgstr ""
-
-#: actions/newmessage.php:71 actions/newmessage.php:234
+#. TRANS: Page title for new direct message page.
+#. TRANS: Page title on page for sending a direct message.
msgid "New message"
msgstr "رسالة جديدة"
-#. TRANS: Error text shown when trying to send a direct message to a user without a mutual subscription (each user must be subscribed to the other).
-#: actions/newmessage.php:121 actions/newmessage.php:164 lib/command.php:501
-msgid "You can't send a message to this user."
+#. TRANS: Client error displayed trying to send a direct message to a user while sender and
+#. TRANS: receiver are not subscribed to each other.
+#, fuzzy
+msgid "You cannot send a message to this user."
msgstr "لا يمكنك إرسال رسائل إلى هذا المستخدم."
+#. TRANS: Form validator error displayed trying to send a direct message without content.
+#. TRANS: Client error displayed trying to send a notice without content.
#. TRANS: Command exception text shown when trying to send a direct message to another user without content.
#. TRANS: Command exception text shown when trying to reply to a notice without providing content for the reply.
-#: actions/newmessage.php:144 actions/newnotice.php:140 lib/command.php:478
-#: lib/command.php:581
msgid "No content!"
msgstr "لا محتوى!"
-#: actions/newmessage.php:161
+#. TRANS: Form validation error displayed trying to send a direct message without specifying a recipient.
msgid "No recipient specified."
msgstr "لا مستلم حُدّد."
+#. TRANS: Client error displayed trying to send a direct message to self.
#. TRANS: Error text shown when trying to send a direct message to self.
-#: actions/newmessage.php:167 lib/command.php:505
msgid ""
"Don't send a message to yourself; just say it to yourself quietly instead."
msgstr "لا ترسل رسالة إلى نفسك، قلها لنفسك في سرك بدلًا من ذلك."
-#: actions/newmessage.php:184
+#. TRANS: Page title after sending a direct message.
msgid "Message sent"
msgstr "أُرسلت الرسالة"
+#. TRANS: Confirmation text after sending a direct message.
+#. TRANS: %s is the direct message recipient.
#. TRANS: Message given have sent a direct message to another user.
#. TRANS: %s is the name of the other user.
-#: actions/newmessage.php:188 lib/command.php:513
#, php-format
msgid "Direct message to %s sent."
msgstr "رسالة مباشرة ل%s تم إرسالها."
-#: actions/newmessage.php:213 actions/newnotice.php:264
+#. TRANS: Page title after an AJAX error occurred on the "send direct message" page.
+#. TRANS: Page title after an AJAX error occurs on the send notice page.
msgid "Ajax Error"
msgstr "خطأ أجاكس"
-#: actions/newnotice.php:69
+#. TRANS: Page title for sending a new notice.
msgid "New notice"
msgstr "إشعار جديد"
-#: actions/newnotice.php:230
+#. TRANS: Page title after sending a notice.
msgid "Notice posted"
msgstr "أُرسل الإشعار"
#. TRANS: Instructions for Notice search page.
#. TRANS: %%site.name%% is the name of the StatusNet site.
-#: actions/noticesearch.php:69
#, php-format
msgid ""
"Search for notices on %%site.name%% by their contents. Separate search terms "
@@ -3841,20 +3173,17 @@ msgstr ""
"ويجب أن تتكون هذه العبارات من 3 أحرف أو أكثر."
#. TRANS: Title of the page where users can search for notices.
-#: actions/noticesearch.php:80
msgid "Text search"
msgstr "بحث في النصوص"
#. TRANS: Test in RSS notice search.
#. TRANS: %1$s is the query, %2$s is the StatusNet site name.
-#: actions/noticesearch.php:95
#, php-format
msgid "Search results for \"%1$s\" on %2$s"
msgstr "نتائج البحث ل\"%1$s\" على %2$s"
#. TRANS: Text for logged in users making a query for notices without results.
#. TRANS: This message contains a Markdown link.
-#: actions/noticesearch.php:128
#, php-format
msgid ""
"Be the first to [post on this topic](%%%%action.newnotice%%%%?"
@@ -3863,7 +3192,6 @@ msgstr ""
#. TRANS: Text for not logged in users making a query for notices without results.
#. TRANS: This message contains Markdown links.
-#: actions/noticesearch.php:133
#, php-format
msgid ""
"Why not [register an account](%%%%action.register%%%%) and be the first to "
@@ -3871,79 +3199,67 @@ msgid ""
msgstr ""
#. TRANS: RSS notice search feed title. %s is the query.
-#: actions/noticesearchrss.php:95
#, php-format
msgid "Updates with \"%s\""
msgstr ""
#. TRANS: RSS notice search feed description.
#. TRANS: %1$s is the query, %2$s is the StatusNet site name.
-#: actions/noticesearchrss.php:99
#, fuzzy, php-format
msgid "Updates matching search term \"%1$s\" on %2$s."
msgstr "الإشعارات التي فضلها %1$s في %2$s!"
-#: actions/nudge.php:85
+#. TRANS: Client error displayed trying to nudge a user that cannot be nudged.
msgid ""
"This user doesn't allow nudges or hasn't confirmed or set their email "
"address yet."
msgstr ""
-#: actions/nudge.php:94
+#. TRANS: Page title after sending a nudge.
msgid "Nudge sent"
msgstr "أرسل التنبيه"
-#: actions/nudge.php:97
+#. TRANS: Confirmation text after sending a nudge.
msgid "Nudge sent!"
msgstr "أُرسل التنبيه!"
#. TRANS: Message displayed to an anonymous user trying to view OAuth application list.
-#: actions/oauthappssettings.php:60
msgid "You must be logged in to list your applications."
msgstr "يجب أن تكون مسجل الدخول لعرض تطبيقاتك."
#. TRANS: Page title for OAuth applications
-#: actions/oauthappssettings.php:76
msgid "OAuth applications"
msgstr "تطبيقات OAuth"
#. TRANS: Page instructions for OAuth applications
-#: actions/oauthappssettings.php:88
msgid "Applications you have registered"
-msgstr ""
+msgstr "التطبيقات التي سجلتها"
#. TRANS: Empty list message on page with OAuth applications.
-#: actions/oauthappssettings.php:141
-#, fuzzy, php-format
+#, php-format
msgid "You have not registered any applications yet."
-msgstr "يجب أن تكون مسجل الدخول لتسجل تطبيقا."
+msgstr "لم تسجل أي تطبيق إلى الآن."
#. TRANS: Title for OAuth connection settings.
-#: actions/oauthconnectionssettings.php:70
-#, fuzzy
msgid "Connected applications"
-msgstr "احذف هذا التطبيق"
+msgstr "التطبيقات المتصلة"
#. TRANS: Instructions for OAuth connection settings.
-#: actions/oauthconnectionssettings.php:81
msgid "The following connections exist for your account."
msgstr ""
#. TRANS: Client error when trying to revoke access for an application while not being a user of it.
-#: actions/oauthconnectionssettings.php:166
msgid "You are not a user of that application."
msgstr "لست مستخدما لهذا التطبيق."
#. TRANS: Client error when revoking access has failed for some reason.
#. TRANS: %s is the application ID revoking access failed for.
-#: actions/oauthconnectionssettings.php:181
#, fuzzy, php-format
msgid "Unable to revoke access for application: %s."
msgstr "استخدم هذا النموذج لتعدل تطبيقك."
#. TRANS: Success message after revoking access for an application.
#. TRANS: %1$s is the application name, %2$s is the first part of the user token.
-#: actions/oauthconnectionssettings.php:200
#, php-format
msgid ""
"You have successfully revoked access for %1$s and the access token starting "
@@ -3951,518 +3267,425 @@ msgid ""
msgstr ""
#. TRANS: Empty list message when no applications have been authorised yet.
-#: actions/oauthconnectionssettings.php:211
msgid "You have not authorized any applications to use your account."
msgstr ""
#. TRANS: Note for developers in the OAuth connection settings form.
#. TRANS: This message contains a Markdown link. Do not separate "](".
#. TRANS: %s is the URL to the OAuth settings.
-#: actions/oauthconnectionssettings.php:231
#, php-format
msgid ""
"Are you a developer? [Register an OAuth client application](%s) to use with "
"this instance of StatusNet."
msgstr ""
-#: actions/oembed.php:64
+#. TRANS: Server error displayed in oEmbed action when path not found.
+#. TRANS: %s is a path.
#, fuzzy, php-format
msgid "\"%s\" not found."
msgstr "لم يُعثرعلى المستخدم."
-#: actions/oembed.php:76
+#. TRANS: Server error displayed in oEmbed action when notice not found.
+#. TRANS: %s is a notice.
#, fuzzy, php-format
msgid "Notice %s not found."
msgstr "لم يتم العثور على وسيلة API."
-#: actions/oembed.php:80 actions/shownotice.php:100
+#. TRANS: Server error displayed in oEmbed action when notice has not profile.
#, fuzzy
msgid "Notice has no profile."
msgstr "ليس للمستخدم ملف شخصي."
-#: actions/oembed.php:83 actions/shownotice.php:172
+#. TRANS: oEmbed title. %1$s is the author name, %2$s is the creation date.
#, php-format
msgid "%1$s's status on %2$s"
msgstr "حالة %1$s في يوم %2$s"
-#: actions/oembed.php:95
+#. TRANS: Server error displayed in oEmbed action when attachment not found.
+#. TRANS: %d is an attachment ID.
#, fuzzy, php-format
msgid "Attachment %s not found."
msgstr "لم يُعثر على المستخدم المستلم."
-#: actions/oembed.php:136
+#. TRANS: Server error displayed in oEmbed request when a path is not supported.
+#. TRANS: %s is a path.
#, php-format
msgid "\"%s\" not supported for oembed requests."
msgstr ""
#. TRANS: Error message displaying attachments. %s is a raw MIME type (eg 'image/png')
-#: actions/oembed.php:168
#, fuzzy, php-format
msgid "Content type %s not supported."
msgstr "نوع المحتوى "
#. TRANS: Error message displaying attachments. %s is the site's base URL.
-#: actions/oembed.php:172
#, php-format
msgid "Only %s URLs over plain HTTP please."
msgstr ""
+#. TRANS: Server error displayed in oEmbed action when request specifies an unsupported data format.
#. TRANS: Client error on an API request with an unsupported data format.
-#: actions/oembed.php:193 actions/oembed.php:212 lib/apiaction.php:1205
-#: lib/apiaction.php:1232 lib/apiaction.php:1367
msgid "Not a supported data format."
msgstr "ليس نسق بيانات مدعوم."
#. TRANS: ShortName in the OpenSearch interface when trying to find users.
-#: actions/opensearch.php:64
msgid "People Search"
msgstr "بحث في الأشخاص"
-#: actions/opensearch.php:68
+#. TRANS: ShortName in the OpenSearch interface when trying to find notices.
msgid "Notice Search"
msgstr "بحث الإشعارات"
-#: actions/othersettings.php:59
-msgid "Other settings"
-msgstr "إعدادات أخرى"
-
-#. TRANS: Instructions for tab "Other" in user profile settings.
-#: actions/othersettings.php:71
-msgid "Manage various other options."
-msgstr "أدر خيارات أخرى عديدة."
-
-#. TRANS: Used as a suffix for free URL shorteners in a dropdown list in the tab "Other" of a
-#. TRANS: user's profile settings. Use of "free" is as in "open", indicating that the
-#. TRANS: information on the shortener site is freely distributable.
-#. TRANS: This message has one space at the beginning. Use your language's word separator
-#. TRANS: here if it has one (most likely a single space).
-#: actions/othersettings.php:113
-msgid " (free service)"
-msgstr " (خدمة حرة)"
-
-#. TRANS: Label for dropdown with URL shortener services.
-#: actions/othersettings.php:122
-msgid "Shorten URLs with"
-msgstr "قصّر المسارات بـ"
-
-#. TRANS: Tooltip for for dropdown with URL shortener services.
-#: actions/othersettings.php:124
-msgid "Automatic shortening service to use."
-msgstr "خدمة التقصير المطلوب استخدامها."
-
-#. TRANS: Label for checkbox.
-#: actions/othersettings.php:130
-msgid "View profile designs"
-msgstr "اعرض تصاميم الملف الشخصي"
-
-#. TRANS: Tooltip for checkbox.
-#: actions/othersettings.php:132
-msgid "Show or hide profile designs."
-msgstr "أظهر أو أخفِ تصاميم الملفات الشخصية."
-
-#. TRANS: Form validation error for form "Other settings" in user profile.
-#: actions/othersettings.php:164
-#, fuzzy
-msgid "URL shortening service is too long (maximum 50 characters)."
-msgstr "الاسم الكامل طويل جدا (الأقصى 255 حرفًا)"
-
#. TRANS: Client error displayed trying to use "one time password login" without specifying a user.
-#: actions/otp.php:70
msgid "No user ID specified."
msgstr "لا هوية مستخدم محددة."
#. TRANS: Client error displayed trying to use "one time password login" without specifying a login token.
-#: actions/otp.php:86
msgid "No login token specified."
msgstr "لا محتوى دخول محدد."
#. TRANS: Client error displayed trying to use "one time password login" without requesting a login token.
-#: actions/otp.php:94
msgid "No login token requested."
msgstr "لا طلب استيثاق."
#. TRANS: Client error displayed trying to use "one time password login" while specifying an invalid login token.
-#: actions/otp.php:100
msgid "Invalid login token specified."
msgstr "توكن دخول غير صحيح محدد."
#. TRANS: Client error displayed trying to use "one time password login" while specifying an expired login token.
-#: actions/otp.php:110
msgid "Login token expired."
msgstr "توكن الدخول انتهى."
#. TRANS: Title for outbox for any but the fist page.
#. TRANS: %1$s is the user nickname, %2$d is the page number.
-#: actions/outbox.php:57
#, php-format
msgid "Outbox for %1$s - page %2$d"
msgstr "صندوق %1$s الصادر - صفحة %2$d"
#. TRANS: Title for first page of outbox.
-#: actions/outbox.php:61
#, php-format
msgid "Outbox for %s"
msgstr "صندوق %s الصادر"
#. TRANS: Instructions for outbox.
-#: actions/outbox.php:103
msgid "This is your outbox, which lists private messages you have sent."
msgstr "هذا صندوق بريدك الصادر، والذي يسرد الرسائل الخاصة التي أرسلتها."
-#: actions/passwordsettings.php:58
+#. TRANS: Title for page where to change password.
+#, fuzzy
+msgctxt "TITLE"
msgid "Change password"
msgstr "تغيير كلمة السر"
-#: actions/passwordsettings.php:69
+#. TRANS: Instructions for page where to change password.
msgid "Change your password."
msgstr "غيّر كلمة سرك."
+#. TRANS: Fieldset legend on page where to change password.
#. TRANS: Fieldset legend for password reset form.
-#: actions/passwordsettings.php:96 actions/recoverpassword.php:251
msgid "Password change"
msgstr "تغيير كلمة السر"
-#: actions/passwordsettings.php:104
+#. TRANS: Field label on page where to change password.
msgid "Old password"
msgstr "كلمة السر القديمة"
+#. TRANS: Field label on page where to change password.
#. TRANS: Field label for password reset form.
-#: actions/passwordsettings.php:108 actions/recoverpassword.php:256
msgid "New password"
msgstr "كلمة السر الجديدة"
-#: actions/passwordsettings.php:109 actions/register.php:423
+#. TRANS: Field title on page where to change password.
#, fuzzy
msgid "6 or more characters."
msgstr "6 أحرف أو أكثر"
+#. TRANS: Field label on page where to change password. In this field the new password should be typed a second time.
+#, fuzzy
+msgctxt "LABEL"
+msgid "Confirm"
+msgstr "أكّد"
+
+#. TRANS: Field title on page where to change password.
#. TRANS: Ttile for field label for password reset form where the password has to be typed again.
-#: actions/passwordsettings.php:113 actions/recoverpassword.php:264
-#: actions/register.php:427
#, fuzzy
msgid "Same as password above."
msgstr "نفس كلمة السر أعلاه"
-#: actions/passwordsettings.php:117
+#. TRANS: Button text on page where to change password.
+#, fuzzy
+msgctxt "BUTTON"
msgid "Change"
msgstr "غيّر"
-#: actions/passwordsettings.php:153 actions/register.php:230
+#. TRANS: Form validation error on page where to change password.
msgid "Password must be 6 or more characters."
msgstr "يجب أن تكون كلمة السر 6 حروف أو أكثر."
-#: actions/passwordsettings.php:156 actions/register.php:233
msgid "Passwords don't match."
msgstr "كلمتا السر غير متطابقتين."
-#: actions/passwordsettings.php:164
-msgid "Incorrect old password"
+#. TRANS: Form validation error on page where to change password.
+#, fuzzy
+msgid "Incorrect old password."
msgstr "كلمة السر القديمة غير صحيحة"
-#: actions/passwordsettings.php:180
+#. TRANS: Form validation error on page where to change password.
msgid "Error saving user; invalid."
msgstr "خطأ أثناء حفظ المستخدم؛ غير صالح."
+#. TRANS: Server error displayed on page where to change password when password change
+#. TRANS: could not be made because of a server error.
#. TRANS: Reset password form validation error message.
-#: actions/passwordsettings.php:185 actions/recoverpassword.php:418
#, fuzzy
msgid "Cannot save new password."
msgstr "تعذّر حفظ كلمة السر الجديدة."
-#: actions/passwordsettings.php:191
+#. TRANS: Form validation notice on page where to change password.
msgid "Password saved."
msgstr "حُفظت كلمة السر."
#. TRANS: Title for Paths admin panel.
#. TRANS: Menu item for site administration
-#: actions/pathsadminpanel.php:58 lib/adminpanelaction.php:371
msgid "Paths"
msgstr "المسارات"
#. TRANS: Form instructions for Path admin panel.
-#: actions/pathsadminpanel.php:69
msgid "Path and server settings for this StatusNet site"
msgstr ""
#. TRANS: Client error in Paths admin panel.
#. TRANS: %s is the directory that could not be read from.
-#: actions/pathsadminpanel.php:155
#, php-format
msgid "Theme directory not readable: %s."
msgstr "لا يمكن قراءة دليل السمات: %s."
#. TRANS: Client error in Paths admin panel.
#. TRANS: %s is the avatar directory that could not be written to.
-#: actions/pathsadminpanel.php:163
#, php-format
msgid "Avatar directory not writable: %s."
msgstr "لا يمكن الكتابة في دليل الأفتارات: %s."
#. TRANS: Client error in Paths admin panel.
#. TRANS: %s is the background directory that could not be written to.
-#: actions/pathsadminpanel.php:171
#, php-format
msgid "Background directory not writable: %s."
msgstr "لا يمكن الكتابة في دليل الخلفيات: %s."
#. TRANS: Client error in Paths admin panel.
#. TRANS: %s is the locales directory that could not be read from.
-#: actions/pathsadminpanel.php:181
#, php-format
msgid "Locales directory not readable: %s."
msgstr "لا يمكن قراءة دليل المحليات: %s."
#. TRANS: Client error in Paths admin panel.
#. TRANS: %s is the SSL server URL that is too long.
-#: actions/pathsadminpanel.php:189
#, fuzzy
msgid "Invalid SSL server. The maximum length is 255 characters."
msgstr "رسالة ترحيب غير صالحة. أقصى طول هو 255 حرف."
#. TRANS: Fieldset legend in Paths admin panel.
-#: actions/pathsadminpanel.php:235 actions/siteadminpanel.php:58
msgid "Site"
msgstr "الموقع"
#. TRANS: Field label in Paths admin panel.
-#: actions/pathsadminpanel.php:241 actions/pathsadminpanel.php:279
-#: actions/pathsadminpanel.php:370 actions/pathsadminpanel.php:425
msgid "Server"
msgstr "خادوم"
-#: actions/pathsadminpanel.php:242
+#. TRANS: Field title in Paths admin panel.
msgid "Site's server hostname."
msgstr "اسم مضيف خادوم الموقع."
#. TRANS: Field label in Paths admin panel.
-#: actions/pathsadminpanel.php:248 actions/pathsadminpanel.php:288
-#: actions/pathsadminpanel.php:379 actions/pathsadminpanel.php:434
msgid "Path"
msgstr "المسار"
-#: actions/pathsadminpanel.php:249
+#. TRANS: Field title in Paths admin panel.
#, fuzzy
msgid "Site path."
msgstr "مسار الموقع"
#. TRANS: Field label in Paths admin panel.
-#: actions/pathsadminpanel.php:255
#, fuzzy
msgid "Locale directory"
msgstr "دليل السمات"
-#: actions/pathsadminpanel.php:256
+#. TRANS: Field title in Paths admin panel.
#, fuzzy
msgid "Directory path to locales."
msgstr "مسار دليل المحليات"
#. TRANS: Checkbox label in Paths admin panel.
-#: actions/pathsadminpanel.php:263
msgid "Fancy URLs"
msgstr "مسارات فاخرة"
-#: actions/pathsadminpanel.php:265
-msgid "Use fancy (more readable and memorable) URLs?"
+#. TRANS: Field title in Paths admin panel.
+#, fuzzy
+msgid "Use fancy URLs (more readable and memorable)?"
msgstr "أأستخدم مسارات فاخرة (يمكن قراءتها وتذكرها بسهولة أكبر)؟"
-#: actions/pathsadminpanel.php:272
+#. TRANS: Fieldset legend in Paths admin panel.
+#, fuzzy
+msgctxt "LEGEND"
msgid "Theme"
msgstr "السمة"
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:281
#, fuzzy
msgid "Server for themes."
msgstr "سمة الموقع."
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:290
msgid "Web path to themes."
msgstr ""
#. TRANS: Field label in Paths admin panel.
-#: actions/pathsadminpanel.php:297 actions/pathsadminpanel.php:388
-#: actions/pathsadminpanel.php:443 actions/pathsadminpanel.php:495
msgid "SSL server"
msgstr "خادم SSL"
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:299
msgid "SSL server for themes (default: SSL server)."
msgstr ""
#. TRANS: Field label in Paths admin panel.
-#: actions/pathsadminpanel.php:306 actions/pathsadminpanel.php:397
-#: actions/pathsadminpanel.php:452
#, fuzzy
msgid "SSL path"
msgstr "مسار الموقع"
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:308
msgid "SSL path to themes (default: /theme/)."
msgstr ""
#. TRANS: Field label in Paths admin panel.
-#: actions/pathsadminpanel.php:315 actions/pathsadminpanel.php:406
-#: actions/pathsadminpanel.php:461
#, fuzzy
msgid "Directory"
msgstr "دليل السمات"
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:317
#, fuzzy
msgid "Directory where themes are located."
msgstr "مسار دليل المحليات"
#. TRANS: Fieldset legend in Paths admin panel.
-#: actions/pathsadminpanel.php:326
msgid "Avatars"
msgstr "أفتارات"
#. TRANS: Field label in Paths admin panel.
-#: actions/pathsadminpanel.php:333
msgid "Avatar server"
msgstr "خادوم الأفتارات"
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:335
#, fuzzy
msgid "Server for avatars."
msgstr "سمة الموقع."
#. TRANS: Field label in Paths admin panel.
-#: actions/pathsadminpanel.php:342
msgid "Avatar path"
msgstr "مسار الأفتارات"
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:344
#, fuzzy
msgid "Web path to avatars."
msgstr "فشل تحديث الأفتار."
#. TRANS: Field label in Paths admin panel.
-#: actions/pathsadminpanel.php:351
msgid "Avatar directory"
msgstr "دليل الأفتار."
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:353
#, fuzzy
msgid "Directory where avatars are located."
msgstr "مسار دليل المحليات"
#. TRANS: Fieldset legend in Paths admin panel.
-#: actions/pathsadminpanel.php:364
msgid "Backgrounds"
msgstr "خلفيات"
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:372
#, fuzzy
msgid "Server for backgrounds."
msgstr "سمة الموقع."
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:381
msgid "Web path to backgrounds."
msgstr ""
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:390
msgid "Server for backgrounds on SSL pages."
msgstr ""
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:399
msgid "Web path to backgrounds on SSL pages."
msgstr ""
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:408
#, fuzzy
msgid "Directory where backgrounds are located."
msgstr "مسار دليل المحليات"
#. TRANS: Fieldset legens in Paths admin panel.
-#. TRANS: DT element label in attachment list.
-#: actions/pathsadminpanel.php:419 lib/attachmentlist.php:98
msgid "Attachments"
msgstr "مرفقات"
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:427
#, fuzzy
msgid "Server for attachments."
msgstr "سمة الموقع."
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:436
#, fuzzy
msgid "Web path to attachments."
msgstr "لا مرفقات."
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:445
#, fuzzy
msgid "Server for attachments on SSL pages."
msgstr "سمة الموقع."
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:454
msgid "Web path to attachments on SSL pages."
msgstr ""
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:463
#, fuzzy
msgid "Directory where attachments are located."
msgstr "مسار دليل المحليات"
#. TRANS: Fieldset legend in Paths admin panel.
-#: actions/pathsadminpanel.php:472
+#, fuzzy
+msgctxt "LEGEND"
msgid "SSL"
msgstr "SSL"
#. TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
-#: actions/pathsadminpanel.php:477 actions/snapshotadminpanel.php:202
msgid "Never"
msgstr "مطلقا"
#. TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
-#: actions/pathsadminpanel.php:479
msgid "Sometimes"
msgstr "أحيانًا"
#. TRANS: Drop down option in Paths admin panel (option for "When to use SSL").
-#: actions/pathsadminpanel.php:481
msgid "Always"
msgstr "دائمًا"
-#: actions/pathsadminpanel.php:485
msgid "Use SSL"
msgstr "استخدم SSL"
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:487
msgid "When to use SSL."
msgstr ""
#. TRANS: Tooltip for field label in Paths admin panel.
-#: actions/pathsadminpanel.php:497
msgid "Server to direct SSL requests to."
msgstr ""
#. TRANS: Button title text to store form data in the Paths admin panel.
-#: actions/pathsadminpanel.php:514
msgid "Save paths"
msgstr "احفظ المسارات"
#. TRANS: Instructions for the "People search" page.
#. TRANS: %%site.name%% is the name of the StatusNet site.
-#: actions/peoplesearch.php:54
#, php-format
msgid ""
"Search for people on %%site.name%% by their name, location, or interests. "
@@ -4472,69 +3695,109 @@ msgstr ""
"عبارات البحث بمسافات؛ ويجب أن تتكون تلك العبارات من 3 أحرف أو أكثر."
#. TRANS: Title of a page where users can search for other users.
-#: actions/peoplesearch.php:61
msgid "People search"
msgstr "بحث في الأشخاص"
-#: actions/peopletag.php:68
+#. TRANS: Client error displayed when trying to tag a profile with an invalid tag.
+#. TRANS: %s is the invalid tag.
#, php-format
msgid "Not a valid people tag: %s."
msgstr "ليس وسم أشخاص صالح: %s."
-#: actions/peopletag.php:142
+#. TRANS: Page title for users with a certain self-tag.
+#. TRANS: %1$s is the tag, %2$s is the page number.
#, php-format
msgid "Users self-tagged with %1$s - page %2$d"
msgstr "المستخدمون الذين وسموا أنفسهم ب%1$s - الصفحة %2$d"
-#: actions/postnotice.php:95
+#. TRANS: Page title for AJAX form return when a disabling a plugin.
+msgctxt "plugin"
+msgid "Disabled"
+msgstr ""
+
+#. TRANS: Client error displayed when trying to use another method than POST.
+#. TRANS: Do not translate POST.
+#. TRANS: Client error displayed trying to perform any request method other than POST.
+#. TRANS: Do not translate POST.
+msgid "This action only accepts POST requests."
+msgstr "هذا الإجراء يقبل طلبات POST فقط."
+
+#. TRANS: Client error displayed when trying to enable or disable a plugin without access rights.
+#, fuzzy
+msgid "You cannot administer plugins."
+msgstr "لا يمكنك حذف المستخدمين."
+
+#. TRANS: Client error displayed when trying to enable or disable a non-existing plugin.
+#, fuzzy
+msgid "No such plugin."
+msgstr "لا صفحة كهذه."
+
+#. TRANS: Page title for AJAX form return when enabling a plugin.
+msgctxt "plugin"
+msgid "Enabled"
+msgstr ""
+
+#. TRANS: Tab and title for plugins admin panel.
+#, fuzzy
+msgctxt "TITLE"
+msgid "Plugins"
+msgstr "الملحقات"
+
+#. TRANS: Instructions at top of plugin admin page.
+msgid ""
+"Additional plugins can be enabled and configured manually. See the online plugin documentation for more "
+"details."
+msgstr ""
+
+#. TRANS: Admin form section header
+#, fuzzy
+msgid "Default plugins"
+msgstr "اللغة المبدئية"
+
+#. TRANS: Text displayed on plugin admin page when no plugin are enabled.
+msgid ""
+"All default plugins have been disabled from the site's configuration file."
+msgstr ""
+
+#. TRANS: Client error displayed if the notice posted has too many characters.
msgid "Invalid notice content."
msgstr "محتوى إشعار غير صالح."
-#: actions/postnotice.php:101
+#. TRANS: Exception thrown if a notice's license is not compatible with the StatusNet site license.
+#. TRANS: %1$s is the notice license, %2$s is the StatusNet site's license.
#, php-format
-msgid "Notice license ‘%1$s’ is not compatible with site license ‘%2$s’."
+msgid "Notice license \"%1$s\" is not compatible with site license \"%2$s\"."
msgstr ""
#. TRANS: Page title for profile settings.
-#: actions/profilesettings.php:59
msgid "Profile settings"
msgstr "إعدادات الملف الشخصي"
#. TRANS: Usage instructions for profile settings.
-#: actions/profilesettings.php:70
msgid ""
"You can update your personal profile info here so people know more about you."
msgstr "بإمكانك تحديث بيانات ملفك الشخصي ليعرف عنك الناس أكثر."
#. TRANS: Profile settings form legend.
-#: actions/profilesettings.php:98
msgid "Profile information"
msgstr "معلومات الملف الشخصي"
#. TRANS: Tooltip for field label in form for profile settings.
-#: actions/profilesettings.php:109 actions/register.php:419
#, fuzzy
msgid "1-64 lowercase letters or numbers, no punctuation or spaces."
msgstr "1-64 حرفًا إنجليزيًا أو رقمًا بدون نقاط أو مسافات"
#. TRANS: Field label in form for profile settings.
-#. TRANS: Label for full group name (dt). Text hidden by default.
-#. TRANS: DT for full name in a profile.
-#: actions/profilesettings.php:113 actions/register.php:441
-#: actions/showgroup.php:255 actions/tagother.php:104
-#: lib/groupeditform.php:150 lib/userprofile.php:156
msgid "Full name"
msgstr "الاسم الكامل"
#. TRANS: Field label in form for profile settings.
#. TRANS: Form input field label.
-#: actions/profilesettings.php:118 actions/register.php:446
-#: lib/applicationeditform.php:236 lib/groupeditform.php:154
msgid "Homepage"
msgstr "الصفحة الرئيسية"
#. TRANS: Tooltip for field label in form for profile settings.
-#: actions/profilesettings.php:121 actions/register.php:448
#, fuzzy
msgid "URL of your homepage, blog, or profile on another site."
msgstr "مسار صفحتك الرئيسية أو مدونتك أو ملفك الشخصي على موقع آخر"
@@ -4542,7 +3805,6 @@ msgstr "مسار صفحتك الرئيسية أو مدونتك أو ملفك ا
#. TRANS: Tooltip for field label in form for profile settings. Plural
#. TRANS: is decided by the number of characters available for the
#. TRANS: biography (%d).
-#: actions/profilesettings.php:129 actions/register.php:457
#, fuzzy, php-format
msgid "Describe yourself and your interests in %d character"
msgid_plural "Describe yourself and your interests in %d characters"
@@ -4554,47 +3816,31 @@ msgstr[4] "تكلم عن نفسك واهتمامتك في %d حرف"
msgstr[5] "تكلم عن نفسك واهتمامتك في %d حرف"
#. TRANS: Tooltip for field label in form for profile settings.
-#: actions/profilesettings.php:135 actions/register.php:462
msgid "Describe yourself and your interests"
msgstr "صِف نفسك واهتماماتك"
#. TRANS: Text area label in form for profile settings where users can provide.
#. TRANS: their biography.
-#: actions/profilesettings.php:139 actions/register.php:464
msgid "Bio"
msgstr "السيرة"
#. TRANS: Field label in form for profile settings.
-#. TRANS: Label for group location (dt). Text hidden by default.
-#. TRANS: DT element on Authorise Subscription page.
-#. TRANS: DT for location in a profile.
-#: actions/profilesettings.php:145 actions/register.php:469
-#: actions/showgroup.php:265 actions/tagother.php:112
-#: actions/userauthorization.php:174 lib/groupeditform.php:173
-#: lib/userprofile.php:172
msgid "Location"
msgstr "الموقع"
#. TRANS: Tooltip for field label in form for profile settings.
-#: actions/profilesettings.php:148
msgid "Where you are, like \"City, State (or Region), Country\""
msgstr "مكان تواجدك، على سبيل المثال \"المدينة، الولاية (أو المنطقة)، الدولة\""
#. TRANS: Checkbox label in form for profile settings.
-#: actions/profilesettings.php:153
msgid "Share my current location when posting notices"
msgstr "شارك مكاني الحالي عند إرسال إشعارات"
#. TRANS: Field label in form for profile settings.
-#. TRANS: DT for tags in a profile.
-#: actions/profilesettings.php:161 actions/tagother.php:149
-#: actions/tagother.php:209 lib/subscriptionlist.php:106
-#: lib/subscriptionlist.php:108 lib/userprofile.php:220
msgid "Tags"
msgstr "الوسوم"
#. TRANS: Tooltip for field label in form for profile settings.
-#: actions/profilesettings.php:164
#, fuzzy
msgid ""
"Tags for yourself (letters, numbers, -, ., and _), comma- or space- "
@@ -4603,28 +3849,23 @@ msgstr ""
"سِم نفسك (حروف وأرقام و \"-\" و \".\" و \"_\")، افصلها بفاصلة (',') أو مسافة."
#. TRANS: Dropdownlist label in form for profile settings.
-#: actions/profilesettings.php:169
msgid "Language"
msgstr "اللغة"
#. TRANS: Tooltip for dropdown list label in form for profile settings.
-#: actions/profilesettings.php:171
#, fuzzy
msgid "Preferred language."
msgstr "اللغة المفضلة"
#. TRANS: Dropdownlist label in form for profile settings.
-#: actions/profilesettings.php:181
msgid "Timezone"
msgstr "المنطقة الزمنية"
#. TRANS: Tooltip for dropdown list label in form for profile settings.
-#: actions/profilesettings.php:183
msgid "What timezone are you normally in?"
msgstr "ما المنطقة الزمنية التي تتواجد فيها عادة؟"
#. TRANS: Checkbox label in form for profile settings.
-#: actions/profilesettings.php:189
#, fuzzy
msgid ""
"Automatically subscribe to whoever subscribes to me (best for non-humans)."
@@ -4633,7 +3874,6 @@ msgstr "اشترك تلقائيًا بأي شخص يشترك بي (يفضل أن
#. TRANS: Validation error in form for profile settings.
#. TRANS: Plural form is used based on the maximum number of allowed
#. TRANS: characters for the biography (%d).
-#: actions/profilesettings.php:258 actions/register.php:221
#, fuzzy, php-format
msgid "Bio is too long (maximum %d character)."
msgid_plural "Bio is too long (maximum %d characters)."
@@ -4645,103 +3885,100 @@ msgstr[4] "الاسم طويل جدا (الأقصى %d حرفا)."
msgstr[5] "الاسم طويل جدا (الأقصى %d حرفا)."
#. TRANS: Validation error in form for profile settings.
-#: actions/profilesettings.php:269 actions/siteadminpanel.php:151
msgid "Timezone not selected."
msgstr "لم تُختر المنطقة الزمنية."
#. TRANS: Validation error in form for profile settings.
-#: actions/profilesettings.php:277
#, fuzzy
msgid "Language is too long (maximum 50 characters)."
msgstr "الاسم طويل جدا (الأقصى 255 حرفا)."
#. TRANS: Validation error in form for profile settings.
#. TRANS: %s is an invalid tag.
-#: actions/profilesettings.php:291
+#. TRANS: Form validation error when entering an invalid tag.
+#. TRANS: %s is the invalid tag.
#, fuzzy, php-format
msgid "Invalid tag: \"%s\"."
msgstr "وسم غير صالح: \"%s\""
#. TRANS: Server error thrown when user profile settings could not be updated to
#. TRANS: automatically subscribe to any subscriber.
-#: actions/profilesettings.php:347
#, fuzzy
msgid "Could not update user for autosubscribe."
msgstr "تعذّر تحديث سجل المستخدم."
#. TRANS: Server error thrown when user profile location preference settings could not be updated.
-#: actions/profilesettings.php:405
#, fuzzy
msgid "Could not save location prefs."
msgstr "لم يمكن حفظ تفضيلات الموقع."
#. TRANS: Server error thrown when user profile settings tags could not be saved.
-#: actions/profilesettings.php:427 actions/tagother.php:200
msgid "Could not save tags."
msgstr "تعذّر حفظ الوسوم."
#. TRANS: Confirmation shown when user profile settings are saved.
#. TRANS: Message after successful saving of administrative settings.
-#: actions/profilesettings.php:436 lib/adminpanelaction.php:138
msgid "Settings saved."
msgstr "حُفظت الإعدادات."
#. TRANS: Option in profile settings to restore the account of the currently logged in user from a backup.
#. TRANS: Page title for page where a user account can be restored from backup.
-#: actions/profilesettings.php:483 actions/restoreaccount.php:60
#, fuzzy
msgid "Restore account"
msgstr "أنشئ حسابًا"
-#: actions/public.php:83
+#. TRANS: Client error displayed when requesting a public timeline page beyond the page limit.
+#. TRANS: %s is the page limit.
#, php-format
msgid "Beyond the page limit (%s)."
msgstr "بعد حد الصفحة (%s)."
-#: actions/public.php:92
+#. TRANS: Server error displayed when a public timeline cannot be retrieved.
#, fuzzy
msgid "Could not retrieve public stream."
msgstr "تعذّر إنشاء الكنى."
-#: actions/public.php:130
+#. TRANS: Title for all public timeline pages but the first.
+#. TRANS: %d is the page number.
#, php-format
msgid "Public timeline, page %d"
msgstr "المسار الزمني العام، صفحة %d"
-#: actions/public.php:132 lib/publicgroupnav.php:79
+#. TRANS: Title for the first public timeline page.
msgid "Public timeline"
msgstr "المسار الزمني العام"
-#: actions/public.php:160
+#. TRANS: Link description for public timeline feed.
msgid "Public Stream Feed (RSS 1.0)"
msgstr ""
-#: actions/public.php:164
+#. TRANS: Link description for public timeline feed.
msgid "Public Stream Feed (RSS 2.0)"
msgstr ""
-#: actions/public.php:168
+#. TRANS: Link description for public timeline feed.
msgid "Public Stream Feed (Atom)"
msgstr ""
-#: actions/public.php:188
+#. TRANS: Text displayed for public feed when there are no public notices.
#, php-format
msgid ""
"This is the public timeline for %%site.name%% but no one has posted anything "
"yet."
msgstr ""
-#: actions/public.php:191
+#. TRANS: Additional text displayed for public feed when there are no public notices for a logged in user.
msgid "Be the first to post!"
msgstr "كن أول من يُرسل!"
-#: actions/public.php:195
+#. TRANS: Additional text displayed for public feed when there are no public notices for a not logged in user.
#, php-format
msgid ""
"Why not [register an account](%%action.register%%) and be the first to post!"
msgstr ""
-#: actions/public.php:242
+#. TRANS: Message for not logged in users at an invite-only site trying to view the public feed of notices.
+#. TRANS: This message contains Markdown links. Please mind the formatting.
#, php-format
msgid ""
"This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-"
@@ -4754,7 +3991,8 @@ msgstr ""
"الآن](%%action.register%%) لتشارك اشعاراتك مع أصدقائك وعائلتك وزملائك! "
"([اقرأ المزيد](%%doc.help%%))"
-#: actions/public.php:247
+#. TRANS: Message for not logged in users at a closed site trying to view the public feed of notices.
+#. TRANS: This message contains Markdown links. Please mind the formatting.
#, php-format
msgid ""
"This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-"
@@ -4765,19 +4003,16 @@ msgstr ""
"blogging) المبنية على البرنامج الحر [StatusNet](http://status.net/)."
#. TRANS: Public RSS feed description. %s is the StatusNet site name.
-#: actions/publicrss.php:106
#, php-format
msgid "%s updates from everyone."
msgstr ""
#. TRANS: Title for public tag cloud.
-#: actions/publictagcloud.php:57
msgid "Public tag cloud"
msgstr "سحابة الوسوم العمومية"
#. TRANS: Instructions (more used like an explanation/header).
#. TRANS: %s is the StatusNet sitename.
-#: actions/publictagcloud.php:65
#, fuzzy, php-format
msgid "These are most popular recent tags on %s"
msgstr "هذه هي أكثر الوسوم شهرة على %s "
@@ -4785,14 +4020,12 @@ msgstr "هذه هي أكثر الوسوم شهرة على %s "
#. TRANS: This message contains a Markdown URL. The link description is between
#. TRANS: square brackets, and the link between parentheses. Do not separate "]("
#. TRANS: and do not change the URL part.
-#: actions/publictagcloud.php:74
#, php-format
msgid "No one has posted a notice with a [hashtag](%%doc.tags%%) yet."
msgstr ""
#. TRANS: Message shown to a logged in user for the public tag cloud
#. TRANS: while no tags exist yet. "One" refers to the non-existing hashtag.
-#: actions/publictagcloud.php:79
msgid "Be the first to post one!"
msgstr "كن أول من يُرسل!"
@@ -4801,278 +4034,229 @@ msgstr "كن أول من يُرسل!"
#. TRANS: This message contains a Markdown URL. The link description is between
#. TRANS: square brackets, and the link between parentheses. Do not separate "]("
#. TRANS: and do not change the URL part.
-#: actions/publictagcloud.php:87
#, php-format
msgid ""
"Why not [register an account](%%action.register%%) and be the first to post "
"one!"
msgstr ""
-#: actions/publictagcloud.php:146
-msgid "Tag cloud"
-msgstr "سحابة الوسوم"
-
#. TRANS: Client error displayed trying to recover password while already logged in.
-#: actions/recoverpassword.php:37
msgid "You are already logged in!"
msgstr "أنت والج بالفعل!"
#. TRANS: Client error displayed when password recovery code is not correct.
-#: actions/recoverpassword.php:64
msgid "No such recovery code."
msgstr "لا رمز استعادة كهذا."
#. TRANS: Client error displayed when no proper password recovery code was submitted.
-#: actions/recoverpassword.php:69
msgid "Not a recovery code."
msgstr "ليس رمز استعادة."
#. TRANS: Server error displayed trying to recover password without providing a user.
-#: actions/recoverpassword.php:77
msgid "Recovery code for unknown user."
msgstr "رمز استعادة لمستخدم غير معروف."
#. TRANS: Server error displayed removing a password recovery code from the database.
-#: actions/recoverpassword.php:91
msgid "Error with confirmation code."
msgstr "خطأ في رمز التأكيد."
#. TRANS: Client error displayed trying to recover password with too old a recovery code.
-#: actions/recoverpassword.php:103
msgid "This confirmation code is too old. Please start again."
msgstr "رمز التأكيد هذا قديم جدًا. من فضلك ابدأ من جديد."
#. TRANS: Server error displayed when updating a user's e-mail address in the database fails while recovering a password.
-#: actions/recoverpassword.php:118
#, fuzzy
msgid "Could not update user with confirmed email address."
msgstr "عنوان البريد الإلكتروني المُؤكد الحالي."
#. TRANS: Page notice for password recovery page.
-#: actions/recoverpassword.php:160
msgid ""
"If you have forgotten or lost your password, you can get a new one sent to "
"the email address you have stored in your account."
msgstr ""
-#: actions/recoverpassword.php:167
msgid "You have been identified. Enter a new password below."
msgstr ""
#. TRANS: Fieldset legend for password recovery page.
-#: actions/recoverpassword.php:198
msgid "Password recovery"
msgstr "استعادة كلمة السر"
#. TRANS: Field label on password recovery page.
-#: actions/recoverpassword.php:202
msgid "Nickname or email address"
msgstr "الاسم المستعار أو البريد الإلكتروني"
#. TRANS: Title for field label on password recovery page.
-#: actions/recoverpassword.php:205
msgid "Your nickname on this server, or your registered email address."
msgstr ""
#. TRANS: Field label on password recovery page.
-#: actions/recoverpassword.php:212
msgid "Recover"
msgstr "استرجع"
#. TRANS: Button text on password recovery page.
-#: actions/recoverpassword.php:214
#, fuzzy
msgctxt "BUTTON"
msgid "Recover"
msgstr "استرجع"
#. TRANS: Title for password recovery page in password reset mode.
-#: actions/recoverpassword.php:223
msgid "Reset password"
msgstr "أعد ضبط كلمة السر"
#. TRANS: Title for password recovery page in password recover mode.
-#: actions/recoverpassword.php:225
msgid "Recover password"
msgstr "استعد كلمة السر"
#. TRANS: Title for password recovery page in email sent mode.
#. TRANS: Subject for password recovery e-mail.
-#: actions/recoverpassword.php:227 actions/recoverpassword.php:366
msgid "Password recovery requested"
msgstr "طُلبت استعادة كلمة السر"
#. TRANS: Title for password recovery page in password saved mode.
-#: actions/recoverpassword.php:229
#, fuzzy
msgid "Password saved"
msgstr "حُفظت كلمة السر."
#. TRANS: Title for password recovery page when an unknown action has been specified.
-#: actions/recoverpassword.php:232
msgid "Unknown action"
msgstr "إجراء غير معروف"
#. TRANS: Title for field label for password reset form.
-#: actions/recoverpassword.php:258
#, fuzzy
msgid "6 or more characters, and do not forget it!"
msgstr "6 أحرف أو أكثر"
#. TRANS: Button text for password reset form.
#. TRANS: Button text on profile design page to reset all colour settings to default without saving.
-#: actions/recoverpassword.php:268 lib/designsettings.php:264
#, fuzzy
msgctxt "BUTTON"
msgid "Reset"
msgstr "أعد الضبط"
#. TRANS: Form instructions for password recovery form.
-#: actions/recoverpassword.php:278
msgid "Enter a nickname or email address."
msgstr "أدخل اسمًا مستعارًا أو عنوان بريد إلكتروني."
#. TRANS: Information on password recovery form if no known username or e-mail address was specified.
-#: actions/recoverpassword.php:309
#, fuzzy
msgid "No user with that email address or username."
msgstr "لا يوجد عنوان بريد إلكتروني مُسجّل لهذا المستخدم."
#. TRANS: Client error displayed on password recovery form if a user does not have a registered e-mail address.
-#: actions/recoverpassword.php:327
msgid "No registered email address for that user."
msgstr "لا يوجد عنوان بريد إلكتروني مُسجّل لهذا المستخدم."
#. TRANS: Server error displayed if e-mail address confirmation fails in the database on the password recovery form.
-#: actions/recoverpassword.php:342
msgid "Error saving address confirmation."
msgstr "خطأ أثناء حفظ تأكيد العنوان."
#. TRANS: User notification after an e-mail with instructions was sent from the password recovery form.
-#: actions/recoverpassword.php:370
msgid ""
"Instructions for recovering your password have been sent to the email "
"address registered to your account."
msgstr ""
#. TRANS: Client error displayed when trying to reset as password without providing a user.
-#: actions/recoverpassword.php:391
#, fuzzy
msgid "Unexpected password reset."
msgstr "أعد ضبط كلمة السر"
#. TRANS: Reset password form validation error message.
-#: actions/recoverpassword.php:400
#, fuzzy
msgid "Password must be 6 characters or more."
msgstr "يجب أن تكون كلمة السر 6 محارف أو أكثر."
#. TRANS: Reset password form validation error message.
-#: actions/recoverpassword.php:405
#, fuzzy
msgid "Password and confirmation do not match."
msgstr "كلمتا السر غير متطابقتين."
#. TRANS: Server error displayed when something does wrong with the user object during password reset.
-#: actions/recoverpassword.php:426 actions/register.php:248
msgid "Error setting user."
msgstr "خطأ أثناء ضبط المستخدم."
#. TRANS: Success message for user after password reset.
-#: actions/recoverpassword.php:434
msgid "New password successfully saved. You are now logged in."
msgstr "حُفظت كلمة السر الجديدة بنجاح. أنت الآن والج."
-#: actions/register.php:87 actions/register.php:188 actions/register.php:399
+#, fuzzy
+msgid "No id parameter"
+msgstr "لا مدخل هوية."
+
+#, fuzzy, php-format
+msgid "No such file \"%d\""
+msgstr "لا ملف كهذا."
+
msgid "Sorry, only invited people can register."
msgstr "عذرًا، الأشخاص المدعوون وحدهم يستطيعون التسجيل."
-#: actions/register.php:94
msgid "Sorry, invalid invitation code."
msgstr "عذرا، رمز دعوة غير صالح."
-#: actions/register.php:113
msgid "Registration successful"
msgstr "نجح التسجيل"
-#: actions/register.php:115 actions/register.php:497
msgid "Register"
msgstr "سجّل"
-#: actions/register.php:135
msgid "Registration not allowed."
msgstr "لا يُسمح بالتسجيل."
-#: actions/register.php:201
#, fuzzy
msgid "You cannot register if you don't agree to the license."
msgstr "لا يمكنك تكرار ملاحظتك الشخصية."
-#: actions/register.php:210
msgid "Email address already exists."
msgstr "عنوان البريد الإلكتروني موجود مسبقًا."
-#: actions/register.php:243 actions/register.php:265
msgid "Invalid username or password."
msgstr "اسم مستخدم أو كلمة سر غير صالحة."
-#: actions/register.php:340
msgid ""
"With this form you can create a new account. You can then post notices and "
"link up to friends and colleagues."
msgstr ""
-#. TRANS: Link description in user account settings menu.
-#: actions/register.php:431 actions/register.php:435
-#: actions/siteadminpanel.php:238 lib/accountsettingsaction.php:127
msgid "Email"
msgstr "البريد الإلكتروني"
-#: actions/register.php:432 actions/register.php:436
#, fuzzy
msgid "Used only for updates, announcements, and password recovery."
msgstr "لا يُستخدم إلا عند التحديثات، والتعميمات، ولاستعادة كلمة السر"
-#: actions/register.php:443
#, fuzzy
msgid "Longer name, preferably your \"real\" name."
msgstr "اسم أطول. يُفضَّل استخدام اسمك الحقيقي"
-#: actions/register.php:471
#, fuzzy
msgid "Where you are, like \"City, State (or Region), Country\"."
msgstr "مكان تواجدك، على سبيل المثال \"المدينة، الولاية (أو المنطقة)، الدولة\""
-#: actions/register.php:510
#, php-format
msgid ""
"I understand that content and data of %1$s are private and confidential."
msgstr ""
-#: actions/register.php:520
#, php-format
msgid "My text and files are copyright by %1$s."
msgstr ""
#. TRANS: Copyright checkbox label in registration dialog, for all rights reserved with ownership left to contributors.
-#: actions/register.php:524
msgid "My text and files remain under my own copyright."
msgstr ""
#. TRANS: Copyright checkbox label in registration dialog, for all rights reserved.
-#: actions/register.php:527
msgid "All rights reserved."
msgstr "جميع الحقوق محفوظة."
#. TRANS: Copyright checkbox label in registration dialog, for Creative Commons-style licenses.
-#: actions/register.php:532
#, php-format
msgid ""
"My text and files are available under %s except this private data: password, "
"email address, IM address, and phone number."
msgstr ""
-#: actions/register.php:573
#, php-format
msgid ""
"Congratulations, %1$s! And welcome to %%%%site.name%%%%. From here, you may "
@@ -5091,13 +4275,11 @@ msgid ""
"Thanks for signing up and we hope you enjoy using this service."
msgstr ""
-#: actions/register.php:597
msgid ""
"(You should receive a message by email momentarily, with instructions on how "
"to confirm your email address.)"
msgstr ""
-#: actions/remotesubscribe.php:97
#, php-format
msgid ""
"To subscribe, you can [login](%%action.login%%), or [register](%%action."
@@ -5105,123 +4287,95 @@ msgid ""
"microblogging site](%%doc.openmublog%%), enter your profile URL below."
msgstr ""
-#: actions/remotesubscribe.php:111
msgid "Remote subscribe"
msgstr "اشتراك بعيد"
-#: actions/remotesubscribe.php:123
msgid "Subscribe to a remote user"
msgstr "اشترك بمستخدم بعيد"
-#: actions/remotesubscribe.php:128
msgid "User nickname"
msgstr "اسم المستخدم المستعار"
-#: actions/remotesubscribe.php:129
msgid "Nickname of the user you want to follow."
msgstr ""
-#: actions/remotesubscribe.php:132
msgid "Profile URL"
msgstr "مسار الملف الشخصي"
-#: actions/remotesubscribe.php:133
msgid "URL of your profile on another compatible microblogging service."
msgstr ""
#. TRANS: Link text for link that will subscribe to a remote profile.
-#: actions/remotesubscribe.php:136 lib/subscribeform.php:139
-#: lib/userprofile.php:431
msgid "Subscribe"
msgstr "اشترك"
-#: actions/remotesubscribe.php:158
#, fuzzy
msgid "Invalid profile URL (bad format)."
msgstr "حجم غير صالح."
-#: actions/remotesubscribe.php:167
msgid "Not a valid profile URL (no YADIS document or invalid XRDS defined)."
msgstr ""
-#: actions/remotesubscribe.php:175
#, fuzzy
msgid "That is a local profile! Login to subscribe."
msgstr "هذا ملف شخصي محلي! لُج لتشترك."
-#: actions/remotesubscribe.php:182
#, fuzzy
msgid "Could not get a request token."
msgstr "تعذّر إدراج الرسالة."
-#: actions/repeat.php:56
msgid "Only logged-in users can repeat notices."
msgstr "يستطيع المستخدمون الوالجون وحدهم تكرار الإشعارات."
-#: actions/repeat.php:63 actions/repeat.php:70
msgid "No notice specified."
msgstr "لا ملاحظة محددة."
-#: actions/repeat.php:75
#, fuzzy
msgid "You cannot repeat your own notice."
msgstr "لا يمكنك تكرار ملاحظتك الشخصية."
-#: actions/repeat.php:89
msgid "You already repeated that notice."
msgstr "أنت كررت هذه الملاحظة بالفعل."
-#: actions/repeat.php:112 lib/noticelist.php:692
msgid "Repeated"
msgstr "مكرر"
-#: actions/repeat.php:117
msgid "Repeated!"
msgstr "مكرر!"
#. TRANS: RSS reply feed title. %s is a user nickname.
-#. TRANS: Tooltip for personal group navigation menu option when logged in for viewing @-replies.
-#: actions/replies.php:126 actions/repliesrss.php:68
-#: lib/personalgroupnav.php:109
#, php-format
msgid "Replies to %s"
msgstr "الردود على %s"
-#: actions/replies.php:128
#, php-format
msgid "Replies to %1$s, page %2$d"
msgstr "الردود على %1$s، الصفحة %2$d"
-#: actions/replies.php:145
#, php-format
msgid "Replies feed for %s (RSS 1.0)"
msgstr ""
-#: actions/replies.php:152
#, php-format
msgid "Replies feed for %s (RSS 2.0)"
msgstr ""
-#: actions/replies.php:159
#, fuzzy, php-format
msgid "Replies feed for %s (Atom)"
msgstr "الردود على %s"
-#: actions/replies.php:199
#, php-format
msgid ""
"This is the timeline showing replies to %1$s but %2$s hasn't received a "
"notice to them yet."
msgstr ""
-#: actions/replies.php:204
#, php-format
msgid ""
"You can engage other users in a conversation, subscribe to more people or "
"[join groups](%%action.groups%%)."
msgstr ""
-#: actions/replies.php:206
#, php-format
msgid ""
"You can try to [nudge %1$s](../%2$s) or [post something to them](%%%%action."
@@ -5230,278 +4384,188 @@ msgstr ""
#. TRANS: RSS reply feed description.
#. TRANS: %1$s is a user nickname, %2$s is the StatusNet site name.
-#: actions/repliesrss.php:74
#, fuzzy, php-format
msgid "Replies to %1$s on %2$s."
msgstr "الردود على %1$s، الصفحة %2$d"
#. TRANS: Client exception displayed when trying to restore an account while not logged in.
-#: actions/restoreaccount.php:78
#, fuzzy
msgid "Only logged-in users can restore their account."
msgstr "يستطيع المستخدمون الوالجون وحدهم تكرار الإشعارات."
#. TRANS: Client exception displayed when trying to restore an account without having restore rights.
-#: actions/restoreaccount.php:83
#, fuzzy
msgid "You may not restore your account."
msgstr "يجب أن تكون مسجل الدخول لتسجل تطبيقا."
#. TRANS: Client exception displayed trying to restore an account while something went wrong uploading a file.
#. TRANS: Client exception. No file; probably just a non-AJAX submission.
-#: actions/restoreaccount.php:121 actions/restoreaccount.php:146
#, fuzzy
msgid "No uploaded file."
msgstr "ارفع ملفًا"
#. TRANS: Client exception thrown when an uploaded file is larger than set in php.ini.
-#: actions/restoreaccount.php:129 lib/mediafile.php:194
msgid "The uploaded file exceeds the upload_max_filesize directive in php.ini."
msgstr ""
#. TRANS: Client exception.
-#: actions/restoreaccount.php:135 lib/mediafile.php:200
msgid ""
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in "
"the HTML form."
msgstr ""
#. TRANS: Client exception.
-#: actions/restoreaccount.php:141 lib/mediafile.php:206
msgid "The uploaded file was only partially uploaded."
msgstr ""
#. TRANS: Client exception thrown when a temporary folder is not present to store a file upload.
-#: actions/restoreaccount.php:150 lib/mediafile.php:214
msgid "Missing a temporary folder."
msgstr ""
#. TRANS: Client exception thrown when writing to disk is not possible during a file upload operation.
-#: actions/restoreaccount.php:154 lib/mediafile.php:218
msgid "Failed to write file to disk."
msgstr "فشل في كتابة الملف إلى القرص."
#. TRANS: Client exception thrown when a file upload operation has been stopped by an extension.
-#: actions/restoreaccount.php:158 lib/mediafile.php:222
msgid "File upload stopped by extension."
msgstr "أوقفت إضافة رفع الملف."
#. TRANS: Client exception thrown when a file upload operation has failed with an unknown reason.
#. TRANS: Exception thrown when uploading an image fails for an unknown reason.
#. TRANS: Client exception thrown when a file upload operation has failed with an unknown reason.
-#: actions/restoreaccount.php:164 lib/imagefile.php:106 lib/mediafile.php:228
msgid "System error uploading file."
msgstr ""
#. TRANS: Client exception thrown when a feed is not an Atom feed.
-#: actions/restoreaccount.php:207
#, fuzzy
msgid "Not an Atom feed."
msgstr "جميع الأعضاء"
#. TRANS: Success message when a feed has been restored.
-#: actions/restoreaccount.php:241
msgid ""
"Feed has been restored. Your old posts should now appear in search and your "
"profile page."
msgstr ""
#. TRANS: Message when a feed restore is in progress.
-#: actions/restoreaccount.php:245
msgid "Feed will be restored. Please wait a few minutes for results."
msgstr ""
#. TRANS: Form instructions for feed restore.
-#: actions/restoreaccount.php:342
msgid ""
"You can upload a backed-up stream in Activity Streams format."
msgstr ""
#. TRANS: Title for submit button to confirm upload of a user backup file for account restore.
-#: actions/restoreaccount.php:373
#, fuzzy
msgid "Upload the file"
msgstr "ارفع ملفًا"
-#: actions/revokerole.php:75
msgid "You cannot revoke user roles on this site."
msgstr "لا يمكنك سحب أدوار المستخدمين على هذا الموقع."
-#: actions/revokerole.php:82
msgid "User doesn't have this role."
msgstr "ليس للمستخدم هذا الدور."
-#: actions/rsd.php:142 actions/version.php:159
msgid "StatusNet"
msgstr "ستاتس نت"
-#: actions/sandbox.php:65 actions/unsandbox.php:65
#, fuzzy
msgid "You cannot sandbox users on this site."
msgstr "لا يمكنك إسكات المستخدمين على هذا الموقع."
-#: actions/sandbox.php:72
#, fuzzy
msgid "User is already sandboxed."
msgstr "المستخدم مسكت من قبل."
#. TRANS: Menu item for site administration
-#: actions/sessionsadminpanel.php:54 actions/sessionsadminpanel.php:170
-#: lib/adminpanelaction.php:379
msgid "Sessions"
msgstr "الجلسات"
-#: actions/sessionsadminpanel.php:65
msgid "Session settings for this StatusNet site"
msgstr ""
-#: actions/sessionsadminpanel.php:175
#, fuzzy
msgid "Handle sessions"
msgstr "الجلسات"
-#: actions/sessionsadminpanel.php:177
msgid "Whether to handle sessions ourselves."
msgstr ""
-#: actions/sessionsadminpanel.php:181
msgid "Session debugging"
msgstr "تنقيح الجلسة"
-#: actions/sessionsadminpanel.php:183
msgid "Turn on debugging output for sessions."
msgstr "مكّن تنقيح مُخرجات الجلسة."
-#: actions/sessionsadminpanel.php:199 actions/siteadminpanel.php:292
+#. TRANS: Submit button title.
+msgid "Save"
+msgstr "أرسل"
+
msgid "Save site settings"
msgstr "اذف إعدادت الموقع"
-#: actions/showapplication.php:78
msgid "You must be logged in to view an application."
msgstr "يجب أن تكون مسجل الدخول لرؤية تطبيق."
-#: actions/showapplication.php:151
#, fuzzy
msgid "Application profile"
msgstr "معلومات التطبيق"
-#. TRANS: Form input field label for application icon.
-#: actions/showapplication.php:153 lib/applicationeditform.php:177
-msgid "Icon"
-msgstr "أيقونة"
-
-#. TRANS: Form input field label for application name.
-#: actions/showapplication.php:163 actions/version.php:197
-#: lib/applicationeditform.php:190
-msgid "Name"
-msgstr "الاسم"
-
-#. TRANS: Form input field label.
-#: actions/showapplication.php:172 lib/applicationeditform.php:227
-msgid "Organization"
-msgstr "المنظمة"
-
-#. TRANS: Form input field label.
-#: actions/showapplication.php:181 actions/version.php:200
-#: lib/applicationeditform.php:208 lib/groupeditform.php:168
-msgid "Description"
-msgstr "الوصف"
-
-#. TRANS: Header for group statistics on a group page (h2).
-#. TRANS: H2 text for user statistics.
-#: actions/showapplication.php:186 actions/showgroup.php:460
-#: lib/profileaction.php:205
-msgid "Statistics"
-msgstr "إحصاءات"
-
-#: actions/showapplication.php:197
#, php-format
msgid "Created by %1$s - %2$s access by default - %3$d users"
msgstr ""
-#: actions/showapplication.php:207
#, fuzzy
msgid "Application actions"
msgstr "معلومات التطبيق"
-#: actions/showapplication.php:230
msgid "Reset key & secret"
msgstr ""
#. TRANS: Title of form for deleting a user.
-#: actions/showapplication.php:246 lib/deletegroupform.php:121
-#: lib/deleteuserform.php:64 lib/noticelist.php:673
msgid "Delete"
msgstr "احذف"
-#: actions/showapplication.php:255
msgid "Application info"
msgstr "معلومات التطبيق"
-#: actions/showapplication.php:257
-msgid "Consumer key"
-msgstr ""
-
-#: actions/showapplication.php:262
-msgid "Consumer secret"
-msgstr ""
-
-#: actions/showapplication.php:267
-msgid "Request token URL"
-msgstr ""
-
-#: actions/showapplication.php:272
-msgid "Access token URL"
-msgstr ""
-
-#: actions/showapplication.php:277
-msgid "Authorize URL"
-msgstr "اسمح بالمسار"
-
-#: actions/showapplication.php:282
msgid ""
"Note: We support HMAC-SHA1 signatures. We do not support the plaintext "
"signature method."
msgstr ""
-#: actions/showapplication.php:302
msgid "Are you sure you want to reset your consumer key and secret?"
msgstr "أمتأكد من أنك تريد إعادة ضبط مفتاح المستهلك وكلمة سره؟"
#. TRANS: Title for all but the first page of favourite notices of a user.
#. TRANS: %1$s is the user for whom the favourite notices are displayed, %2$d is the page number.
-#: actions/showfavorites.php:80
#, php-format
msgid "%1$s's favorite notices, page %2$d"
msgstr "إشعارات %1$s المُفضلة، الصفحة %2$d"
#. TRANS: Server error displayed when favourite notices could not be retrieved from the database.
-#: actions/showfavorites.php:134
#, fuzzy
msgid "Could not retrieve favorite notices."
msgstr "تعذّر إنشاء مفضلة."
#. TRANS: Feed link text. %s is a username.
-#: actions/showfavorites.php:172
#, php-format
msgid "Feed for favorites of %s (RSS 1.0)"
msgstr ""
#. TRANS: Feed link text. %s is a username.
-#: actions/showfavorites.php:180
#, php-format
msgid "Feed for favorites of %s (RSS 2.0)"
msgstr ""
#. TRANS: Feed link text. %s is a username.
-#: actions/showfavorites.php:188
#, php-format
msgid "Feed for favorites of %s (Atom)"
msgstr ""
#. TRANS: Text displayed instead of favourite notices for the current logged in user that has no favourites.
-#: actions/showfavorites.php:209
msgid ""
"You haven't chosen any favorite notices yet. Click the fave button on "
"notices you like to bookmark them for later or shed a spotlight on them."
@@ -5509,7 +4573,6 @@ msgstr ""
#. TRANS: Text displayed instead of favourite notices for a user that has no favourites while logged in.
#. TRANS: %s is a username.
-#: actions/showfavorites.php:213
#, fuzzy, php-format
msgid ""
"%s hasn't added any favorite notices yet. Post something interesting they "
@@ -5521,7 +4584,6 @@ msgstr ""
#. TRANS: Text displayed instead of favourite notices for a user that has no favourites while not logged in.
#. TRANS: %s is a username, %%%%action.register%%%% is a link to the user registration page.
#. TRANS: (link text)[link] is a Mark Down link.
-#: actions/showfavorites.php:220
#, fuzzy, php-format
msgid ""
"%s hasn't added any favorite notices yet. Why not [register an account](%%%%"
@@ -5532,81 +4594,41 @@ msgstr ""
"register%%%%) وترسل شيئًا شيقًا ليضيفه إلى مفضلته. :)"
#. TRANS: Page notice for show favourites page.
-#: actions/showfavorites.php:251
msgid "This is a way to share what you like."
msgstr "إنها إحدى وسائل مشاركة ما تحب."
#. TRANS: Page title for first group page. %s is a group name.
-#: actions/showgroup.php:75
#, php-format
msgid "%s group"
msgstr "مجموعة %s"
#. TRANS: Page title for any but first group page.
#. TRANS: %1$s is a group name, $2$s is a page number.
-#: actions/showgroup.php:79
#, php-format
msgid "%1$s group, page %2$d"
msgstr "مجموعة %1$s، الصفحة %2$d"
-#. TRANS: Group profile header (h2). Text hidden by default.
-#: actions/showgroup.php:223
-msgid "Group profile"
-msgstr "ملف المجموعة الشخصي"
-
-#. TRANS: Label for group URL (dt). Text hidden by default.
-#. TRANS: DT element on Authorise Subscription page.
-#. TRANS: DT for URL in a profile.
-#: actions/showgroup.php:273 actions/tagother.php:118
-#: actions/userauthorization.php:184 lib/userprofile.php:186
-msgid "URL"
-msgstr "مسار"
-
-#. TRANS: Label for group description or group note (dt). Text hidden by default.
-#. TRANS: DT element on Authorise Subscription page where bio is displayed.
-#. TRANS: DT for note in a profile.
-#: actions/showgroup.php:285 actions/tagother.php:128
-#: actions/userauthorization.php:197 lib/userprofile.php:204
-msgid "Note"
-msgstr "ملاحظة"
-
-#. TRANS: Label for group aliases (dt). Text hidden by default.
-#: actions/showgroup.php:296 lib/groupeditform.php:180
-msgid "Aliases"
-msgstr "الكنى"
-
-#. TRANS: Group actions header (h2). Text hidden by default.
-#: actions/showgroup.php:313
-#, fuzzy
-msgid "Group actions"
-msgstr "تصرفات المستخدم"
-
#. TRANS: Tooltip for feed link. %s is a group nickname.
-#: actions/showgroup.php:357
#, php-format
msgid "Notice feed for %s group (RSS 1.0)"
msgstr ""
#. TRANS: Tooltip for feed link. %s is a group nickname.
-#: actions/showgroup.php:364
#, php-format
msgid "Notice feed for %s group (RSS 2.0)"
msgstr ""
#. TRANS: Tooltip for feed link. %s is a group nickname.
-#: actions/showgroup.php:371
#, php-format
msgid "Notice feed for %s group (Atom)"
msgstr ""
#. TRANS: Tooltip for feed link. %s is a group nickname.
-#: actions/showgroup.php:377
#, fuzzy, php-format
msgid "FOAF for %s group"
msgstr "مجموعة %s"
#. TRANS: Header for mini list of group members on a group page (h2).
-#: actions/showgroup.php:414
msgid "Members"
msgstr "الأعضاء"
@@ -5614,26 +4636,24 @@ msgstr "الأعضاء"
#. TRANS: Text for user subscription statistics if the user has no subscriptions.
#. TRANS: Text for user subscriber statistics if user has no subscribers.
#. TRANS: Text for user user group membership statistics if user is not a member of any group.
-#: actions/showgroup.php:420 lib/profileaction.php:137
-#: lib/profileaction.php:174 lib/profileaction.php:298 lib/section.php:95
-#: lib/subscriptionlist.php:127 lib/tagcloudsection.php:71
msgid "(None)"
msgstr "(لا شيء)"
#. TRANS: Link to all group members from mini list of group members if group has more than n members.
-#: actions/showgroup.php:429
msgid "All members"
msgstr "جميع الأعضاء"
-#. TRANS: Label for creation date in statistics on group page.
-#: actions/showgroup.php:465
+#. TRANS: Header for group statistics on a group page (h2).
+#. TRANS: H2 text for user statistics.
+msgid "Statistics"
+msgstr "إحصاءات"
+
#, fuzzy
msgctxt "LABEL"
msgid "Created"
msgstr "أنشئت"
#. TRANS: Label for member count in statistics on group page.
-#: actions/showgroup.php:473
#, fuzzy
msgctxt "LABEL"
msgid "Members"
@@ -5643,7 +4663,6 @@ msgstr "الأعضاء"
#. TRANS: **%s** is the group alias, %%%%site.name%%%% is the site name,
#. TRANS: %%%%action.register%%%% is the URL for registration, %%%%doc.help%%%% is a URL to help.
#. TRANS: This message contains Markdown links. Ensure they are formatted correctly: [Description](link).
-#: actions/showgroup.php:488
#, php-format
msgid ""
"**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en."
@@ -5661,7 +4680,6 @@ msgstr ""
#. TRANS: Notice on group pages for anonymous users for StatusNet sites that accept no new registrations.
#. TRANS: **%s** is the group alias, %%%%site.name%%%% is the site name,
#. TRANS: This message contains Markdown links. Ensure they are formatted correctly: [Description](link).
-#: actions/showgroup.php:498
#, php-format
msgid ""
"**%s** is a user group on %%%%site.name%%%%, a [micro-blogging](http://en."
@@ -5674,99 +4692,86 @@ msgstr ""
"(http://status.net/). يتشارك أعضاؤها رسائل قصيرة عن حياتهم واهتماماتهم. "
#. TRANS: Header for list of group administrators on a group page (h2).
-#: actions/showgroup.php:527
msgid "Admins"
msgstr "الإداريون"
#. TRANS: Client error displayed requesting a single message that does not exist.
-#: actions/showmessage.php:76
msgid "No such message."
msgstr "لا رسالة كهذه."
#. TRANS: Client error displayed requesting a single direct message the requesting user was not a party in.
-#: actions/showmessage.php:86
msgid "Only the sender and recipient may read this message."
msgstr "يحق للمُرسل والمستلم فقط قراءة هذه الرسالة."
#. TRANS: Page title for single direct message display when viewing user is the sender.
#. TRANS: %1$s is the addressed user's nickname, $2$s is a timestamp.
-#: actions/showmessage.php:105
#, fuzzy, php-format
msgid "Message to %1$s on %2$s"
msgstr "الإشعارات التي فضلها %1$s في %2$s!"
#. TRANS: Page title for single message display.
#. TRANS: %1$s is the sending user's nickname, $2$s is a timestamp.
-#: actions/showmessage.php:113
#, fuzzy, php-format
msgid "Message from %1$s on %2$s"
msgstr "نتائج البحث ل\"%1$s\" على %2$s"
-#: actions/shownotice.php:90
msgid "Notice deleted."
msgstr "حُذف الإشعار."
+msgid "Notice"
+msgstr "إشعارات"
+
#. TRANS: Page title showing tagged notices in one user's stream. %1$s is the username, %2$s is the hash tag.
-#: actions/showstream.php:70
#, fuzzy, php-format
msgid "%1$s tagged %2$s"
msgstr "%1$s، الصفحة %2$d"
#. TRANS: Page title showing tagged notices in one user's stream.
#. TRANS: %1$s is the username, %2$s is the hash tag, %3$d is the page number.
-#: actions/showstream.php:74
#, fuzzy, php-format
msgid "%1$s tagged %2$s, page %3$d"
msgstr "الإشعارات الموسومة ب%s، الصفحة %2$d"
#. TRANS: Extended page title showing tagged notices in one user's stream.
#. TRANS: %1$s is the username, %2$d is the page number.
-#: actions/showstream.php:82
#, php-format
msgid "%1$s, page %2$d"
msgstr "%1$s، الصفحة %2$d"
#. TRANS: Title for link to notice feed.
#. TRANS: %1$s is a user nickname, %2$s is a hashtag.
-#: actions/showstream.php:127
#, php-format
msgid "Notice feed for %1$s tagged %2$s (RSS 1.0)"
msgstr ""
#. TRANS: Title for link to notice feed.
#. TRANS: %s is a user nickname.
-#: actions/showstream.php:136
#, php-format
msgid "Notice feed for %s (RSS 1.0)"
msgstr ""
#. TRANS: Title for link to notice feed.
#. TRANS: %s is a user nickname.
-#: actions/showstream.php:145
#, php-format
msgid "Notice feed for %s (RSS 2.0)"
msgstr ""
-#: actions/showstream.php:152
#, php-format
msgid "Notice feed for %s (Atom)"
msgstr ""
#. TRANS: Title for link to notice feed. FOAF stands for Friend of a Friend.
#. TRANS: More information at http://www.foaf-project.org. %s is a user nickname.
-#: actions/showstream.php:159
#, fuzzy, php-format
msgid "FOAF for %s"
msgstr "صندوق %s الصادر"
#. TRANS: First sentence of empty list message for a stream. $1%s is a user nickname.
-#: actions/showstream.php:211
#, php-format
msgid "This is the timeline for %1$s, but %1$s hasn't posted anything yet."
msgstr ""
#. TRANS: Second sentence of empty list message for a stream for the user themselves.
-#: actions/showstream.php:217
msgid ""
"Seen anything interesting recently? You haven't posted any notices yet, now "
"would be a good time to start :)"
@@ -5774,7 +4779,6 @@ msgstr ""
#. TRANS: Second sentence of empty list message for a non-self stream. %1$s is a user nickname, %2$s is a part of a URL.
#. TRANS: This message contains a Markdown link. Keep "](" together.
-#: actions/showstream.php:221
#, php-format
msgid ""
"You can try to nudge %1$s or [post something to them](%%%%action.newnotice%%%"
@@ -5783,7 +4787,6 @@ msgstr ""
#. TRANS: Announcement for anonymous users showing a stream if site registrations are open.
#. TRANS: This message contains a Markdown link. Keep "](" together.
-#: actions/showstream.php:264
#, php-format
msgid ""
"**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en."
@@ -5799,7 +4802,6 @@ msgstr ""
#. TRANS: Announcement for anonymous users showing a stream if site registrations are closed or invite only.
#. TRANS: This message contains a Markdown link. Keep "](" together.
-#: actions/showstream.php:271
#, php-format
msgid ""
"**%s** has an account on %%%%site.name%%%%, a [micro-blogging](http://en."
@@ -5811,259 +4813,206 @@ msgstr ""
"(http://status.net/). "
#. TRANS: Link to the author of a repeated notice. %s is a linked nickname.
-#: actions/showstream.php:328
#, php-format
msgid "Repeat of %s"
msgstr "تكرار ل%s"
-#: actions/silence.php:65 actions/unsilence.php:65
msgid "You cannot silence users on this site."
msgstr "لا يمكنك إسكات المستخدمين على هذا الموقع."
-#: actions/silence.php:72
msgid "User is already silenced."
msgstr "المستخدم مسكت من قبل."
-#: actions/siteadminpanel.php:69
msgid "Basic settings for this StatusNet site"
msgstr "الإعدادات الأساسية لموقع StatusNet هذا."
-#: actions/siteadminpanel.php:133
msgid "Site name must have non-zero length."
msgstr "يجب ألا يكون طول اسم الموقع صفرًا."
-#: actions/siteadminpanel.php:141
msgid "You must have a valid contact email address."
msgstr "يجب أن تملك عنوان بريد إلكتروني صحيح."
-#: actions/siteadminpanel.php:159
#, php-format
msgid "Unknown language \"%s\"."
msgstr "لغة غير معروفة \"%s\"."
-#: actions/siteadminpanel.php:165
msgid "Minimum text limit is 0 (unlimited)."
msgstr "حد النص الأدنى 0 (غير محدود)."
-#: actions/siteadminpanel.php:171
msgid "Dupe limit must be one or more seconds."
msgstr ""
-#: actions/siteadminpanel.php:221
msgid "General"
msgstr "عام"
-#: actions/siteadminpanel.php:224
msgid "Site name"
msgstr "اسم الموقع"
-#: actions/siteadminpanel.php:225
msgid "The name of your site, like \"Yourcompany Microblog\""
msgstr "اسم موقعك، \"التدوين المصغر لشركتك\" مثلا"
-#: actions/siteadminpanel.php:229
msgid "Brought by"
msgstr ""
-#: actions/siteadminpanel.php:230
msgid "Text used for credits link in footer of each page"
msgstr ""
-#: actions/siteadminpanel.php:234
msgid "Brought by URL"
msgstr ""
-#: actions/siteadminpanel.php:235
msgid "URL used for credits link in footer of each page"
msgstr ""
-#: actions/siteadminpanel.php:239
msgid "Contact email address for your site"
msgstr "عنوان البريد الإلكتروني للاتصال بموقعك"
-#: actions/siteadminpanel.php:245
msgid "Local"
msgstr "محلي"
-#: actions/siteadminpanel.php:256
msgid "Default timezone"
msgstr "المنطقة الزمنية المبدئية"
-#: actions/siteadminpanel.php:257
msgid "Default timezone for the site; usually UTC."
msgstr "المنطقة الزمنية المبدئية للموقع؛ تعم عادة."
-#: actions/siteadminpanel.php:262
msgid "Default language"
msgstr "اللغة المبدئية"
-#: actions/siteadminpanel.php:263
msgid "Site language when autodetection from browser settings is not available"
msgstr "لغة الموقع إذا لم يتوفر اكتشاف اللغة آليًا من إعدادات المتصفح"
-#: actions/siteadminpanel.php:271
msgid "Limits"
msgstr "الحدود"
-#: actions/siteadminpanel.php:274
msgid "Text limit"
msgstr "حد النص"
-#: actions/siteadminpanel.php:274
msgid "Maximum number of characters for notices."
msgstr "أقصى عدد للحروف في الإشعارات."
-#: actions/siteadminpanel.php:278
#, fuzzy
msgid "Dupe limit"
msgstr "حد النص"
-#: actions/siteadminpanel.php:278
msgid "How long users must wait (in seconds) to post the same thing again."
msgstr ""
"الفترة (بالثواني) التي ينبغي أن ينتظرها المستخدمون قبل أن ينشروا الرسالة "
"نفسها مجددًا"
#. TRANS: Page title for site-wide notice tab in admin panel.
-#: actions/sitenoticeadminpanel.php:55
msgid "Site Notice"
msgstr "إشعار الموقع"
#. TRANS: Instructions for site-wide notice tab in admin panel.
-#: actions/sitenoticeadminpanel.php:66
msgid "Edit site-wide message"
msgstr "عدّل رسالة الموقع العامة"
#. TRANS: Server error displayed when saving a site-wide notice was impossible.
-#: actions/sitenoticeadminpanel.php:101
msgid "Unable to save site notice."
msgstr "تعذّر حفظ إشعار الموقع."
#. TRANS: Client error displayed when a site-wide notice was longer than allowed.
-#: actions/sitenoticeadminpanel.php:112
msgid "Maximum length for the site-wide notice is 255 characters."
msgstr ""
#. TRANS: Label for site-wide notice text field in admin panel.
-#: actions/sitenoticeadminpanel.php:176
msgid "Site notice text"
msgstr "نص إشعار الموقع"
#. TRANS: Tooltip for site-wide notice text field in admin panel.
-#: actions/sitenoticeadminpanel.php:179
#, fuzzy
msgid "Site-wide notice text (255 characters maximum; HTML allowed)"
msgstr "نص إشعار عام للموقع (255 حرف كحد أقصى؛ يسمح بHTML)"
#. TRANS: Title for button to save site notice in admin panel.
-#: actions/sitenoticeadminpanel.php:201
#, fuzzy
msgid "Save site notice."
msgstr "احفظ إشعار الموقع"
#. TRANS: Title for SMS settings.
-#: actions/smssettings.php:57
msgid "SMS settings"
msgstr "إعدادات الرسائل القصيرة"
#. TRANS: SMS settings page instructions.
#. TRANS: %%site.name%% is the name of the site.
-#: actions/smssettings.php:71
#, php-format
msgid "You can receive SMS messages through email from %%site.name%%."
msgstr "لا يمكنك استلام رسائل قصيرة عبر البريد الإلكرتوني من %%site.name%%."
#. TRANS: Message given in the SMS settings if SMS is not enabled on the site.
-#: actions/smssettings.php:93
msgid "SMS is not available."
msgstr "الرسائل القصيرة غير متوفرة."
#. TRANS: Form legend for SMS settings form.
-#: actions/smssettings.php:107
msgid "SMS address"
msgstr "عنوان الرسائل القصيرة"
#. TRANS: Form guide in SMS settings form.
-#: actions/smssettings.php:116
#, fuzzy
msgid "Current confirmed SMS-enabled phone number."
msgstr "عنوان البريد الإلكتروني المُؤكد الحالي."
#. TRANS: Form guide in IM settings form.
-#: actions/smssettings.php:129
#, fuzzy
msgid "Awaiting confirmation on this phone number."
msgstr "إن رقم التأكيد هذا خاطئ."
#. TRANS: Field label for SMS address input in SMS settings form.
-#: actions/smssettings.php:138
msgid "Confirmation code"
msgstr "رمز التأكيد"
#. TRANS: Form field instructions in SMS settings form.
-#: actions/smssettings.php:140
msgid "Enter the code you received on your phone."
msgstr "أدخِل الرمز الذي تلقيته على هاتفك."
#. TRANS: Button label to confirm SMS confirmation code in SMS settings.
-#: actions/smssettings.php:144
msgctxt "BUTTON"
msgid "Confirm"
msgstr "أكّد"
#. TRANS: Field label for SMS phone number input in SMS settings form.
-#: actions/smssettings.php:149
msgid "SMS phone number"
msgstr "رقم هاتف SMS"
#. TRANS: SMS phone number input field instructions in SMS settings form.
-#: actions/smssettings.php:152
#, fuzzy
msgid "Phone number, no punctuation or spaces, with area code."
msgstr "رقم الهاتف بدون شرطات أو مسافات مع رمز المنطقة"
#. TRANS: Form legend for SMS preferences form.
-#: actions/smssettings.php:191
msgid "SMS preferences"
msgstr "تفضيلات الرسائل القصيرة"
#. TRANS: Checkbox label in SMS preferences form.
-#: actions/smssettings.php:197
msgid ""
"Send me notices through SMS; I understand I may incur exorbitant charges "
"from my carrier."
msgstr ""
#. TRANS: Confirmation message for successful SMS preferences save.
-#: actions/smssettings.php:308
msgid "SMS preferences saved."
msgstr "حُفظت تفضيلات الرسائل القصيرة."
#. TRANS: Message given saving SMS phone number without having provided one.
-#: actions/smssettings.php:330
msgid "No phone number."
msgstr "لا رقم هاتف."
#. TRANS: Message given saving SMS phone number without having selected a carrier.
-#: actions/smssettings.php:336
#, fuzzy
msgid "No carrier selected."
msgstr "حُذف الإشعار."
#. TRANS: Message given saving SMS phone number that is already set.
-#: actions/smssettings.php:344
#, fuzzy
msgid "That is already your phone number."
msgstr "هذا ليس رقم هاتفك."
#. TRANS: Message given saving SMS phone number that is already set for another user.
-#: actions/smssettings.php:348
#, fuzzy
msgid "That phone number already belongs to another user."
msgstr "هذا البريد الإلكتروني ملك مستخدم آخر بالفعل."
#. TRANS: Message given saving valid SMS phone number that is to be confirmed.
-#: actions/smssettings.php:376
msgid ""
"A confirmation code was sent to the phone number you added. Check your phone "
"for the code and instructions on how to use it."
@@ -6072,39 +5021,32 @@ msgstr ""
"والتعليمات لكيفية استخدامه."
#. TRANS: Message given canceling SMS phone number confirmation for the wrong phone number.
-#: actions/smssettings.php:404
msgid "That is the wrong confirmation number."
msgstr "إن رقم التأكيد هذا خاطئ."
#. TRANS: Message given after successfully canceling SMS phone number confirmation.
-#: actions/smssettings.php:418
msgid "SMS confirmation cancelled."
msgstr "أُلغي تأكيد الرسائل القصيرة."
#. TRANS: Message given trying to remove an SMS phone number that is not
#. TRANS: registered for the active user.
-#: actions/smssettings.php:438
msgid "That is not your phone number."
msgstr "هذا ليس رقم هاتفك."
#. TRANS: Message given after successfully removing a registered SMS phone number.
-#: actions/smssettings.php:460
msgid "The SMS phone number was removed."
msgstr "تمت إزالة رقم هاتف الرسائل القصيرة."
#. TRANS: Label for mobile carrier dropdown menu in SMS settings.
-#: actions/smssettings.php:499
msgid "Mobile carrier"
msgstr "شركة الهاتف الخليوي"
#. TRANS: Default option for mobile carrier dropdown menu in SMS settings.
-#: actions/smssettings.php:504
msgid "Select a carrier"
msgstr "اختر شركة"
#. TRANS: Form instructions for mobile carrier dropdown menu in SMS settings.
#. TRANS: %s is an administrative contact's e-mail address.
-#: actions/smssettings.php:513
#, php-format
msgid ""
"Mobile carrier for your phone. If you know a carrier that accepts SMS over "
@@ -6112,130 +5054,99 @@ msgid ""
msgstr ""
#. TRANS: Message given saving SMS phone number confirmation code without having provided one.
-#: actions/smssettings.php:535
#, fuzzy
msgid "No code entered."
msgstr "لم تدخل رمزًا"
#. TRANS: Menu item for site administration
-#: actions/snapshotadminpanel.php:54 actions/snapshotadminpanel.php:196
-#: lib/adminpanelaction.php:395
msgid "Snapshots"
msgstr ""
-#: actions/snapshotadminpanel.php:65
#, fuzzy
msgid "Manage snapshot configuration"
msgstr "غيّر ضبط الموقع"
-#: actions/snapshotadminpanel.php:127
#, fuzzy
msgid "Invalid snapshot run value."
msgstr "محتوى إشعار غير صالح."
-#: actions/snapshotadminpanel.php:133
msgid "Snapshot frequency must be a number."
msgstr ""
-#: actions/snapshotadminpanel.php:144
#, fuzzy
msgid "Invalid snapshot report URL."
msgstr "مسار شعار غير صالح."
-#: actions/snapshotadminpanel.php:200
msgid "Randomly during web hit"
msgstr ""
-#: actions/snapshotadminpanel.php:201
msgid "In a scheduled job"
msgstr "في مهمة مُجدولة"
-#: actions/snapshotadminpanel.php:206
msgid "Data snapshots"
msgstr ""
-#: actions/snapshotadminpanel.php:208
msgid "When to send statistical data to status.net servers"
msgstr ""
-#: actions/snapshotadminpanel.php:217
msgid "Frequency"
msgstr "التكرار"
-#: actions/snapshotadminpanel.php:218
msgid "Snapshots will be sent once every N web hits"
msgstr ""
-#: actions/snapshotadminpanel.php:226
msgid "Report URL"
msgstr "بلّغ عن المسار"
-#: actions/snapshotadminpanel.php:227
msgid "Snapshots will be sent to this URL"
msgstr ""
-#: actions/snapshotadminpanel.php:248
#, fuzzy
msgid "Save snapshot settings"
msgstr "اذف إعدادت الموقع"
#. TRANS: Client error displayed trying a change a subscription for a non-subscribed profile.
-#: actions/subedit.php:75
#, fuzzy
msgid "You are not subscribed to that profile."
msgstr "لست مُشتركًا بأي أحد."
#. TRANS: Server error displayed when updating a subscription fails with a database error.
#. TRANS: Exception thrown when a subscription could not be stored on the server.
-#: actions/subedit.php:89 classes/Subscription.php:141
msgid "Could not save subscription."
msgstr "تعذّر حفظ الاشتراك."
-#. TRANS: Client error displayed trying to perform any request method other than POST.
-#. TRANS: Do not translate POST.
-#: actions/subscribe.php:77
-msgid "This action only accepts POST requests."
-msgstr "هذا الإجراء يقبل طلبات POST فقط."
-
#. TRANS: Client error displayed trying to subscribe to an OMB 0.1 remote profile.
-#: actions/subscribe.php:121
msgid "You cannot subscribe to an OMB 0.1 remote profile with this action."
msgstr ""
#. TRANS: Page title when subscription succeeded.
-#: actions/subscribe.php:149
msgid "Subscribed"
msgstr "مُشترك"
#. TRANS: Header for list of subscribers for a user (first page).
#. TRANS: %s is the user's nickname.
-#: actions/subscribers.php:51
#, php-format
msgid "%s subscribers"
msgstr "مشتركو %s"
#. TRANS: Header for list of subscribers for a user (not first page).
#. TRANS: %1$s is the user's nickname, $2$d is the page number.
-#: actions/subscribers.php:55
#, php-format
msgid "%1$s subscribers, page %2$d"
msgstr "مشتركو %1$s, الصفحة %2$d"
#. TRANS: Page notice for page with an overview of all subscribers
#. TRANS: of the logged in user's own profile.
-#: actions/subscribers.php:68
msgid "These are the people who listen to your notices."
msgstr "هؤلاء هم الأشخاص الذين يستمعون إلى إشعاراتك."
#. TRANS: Page notice for page with an overview of all subscribers of a user other
#. TRANS: than the logged in user. %s is the user nickname.
-#: actions/subscribers.php:74
#, fuzzy, php-format
msgid "These are the people who listen to %s's notices."
msgstr "هؤلاء هم الأشخاص الذين يستمعون إلى إشعاراتك."
#. TRANS: Subscriber list text when the logged in user has no subscribers.
-#: actions/subscribers.php:114
msgid ""
"You have no subscribers. Try subscribing to people you know and they might "
"return the favor."
@@ -6243,7 +5154,6 @@ msgstr "ليس لديك مشتركون. جرِّب الاشتراك مع أشخ
#. TRANS: Subscriber list text when looking at the subscribers for a of a user other
#. TRANS: than the logged in user that has no subscribers. %s is the user nickname.
-#: actions/subscribers.php:118
#, php-format
msgid "%s has no subscribers. Want to be the first?"
msgstr ""
@@ -6253,7 +5163,6 @@ msgstr ""
#. TRANS: This message contains a Markdown URL. The link description is between
#. TRANS: square brackets, and the link between parentheses. Do not separate "]("
#. TRANS: and do not change the URL part.
-#: actions/subscribers.php:127
#, php-format
msgid ""
"%s has no subscribers. Why not [register an account](%%%%action.register%%%"
@@ -6262,20 +5171,17 @@ msgstr ""
#. TRANS: Header for subscriptions overview for a user (not first page).
#. TRANS: %1$s is a user nickname, %2$d is the page number.
-#: actions/subscriptions.php:55
#, php-format
msgid "%1$s subscriptions, page %2$d"
msgstr "اشتراكات%1$s, الصفحة %2$d"
#. TRANS: Page notice for page with an overview of all subscriptions
#. TRANS: of the logged in user's own profile.
-#: actions/subscriptions.php:68
msgid "These are the people whose notices you listen to."
msgstr "هؤلاء الأشخاص الذي تستمع إليهم."
#. TRANS: Page notice for page with an overview of all subscriptions of a user other
#. TRANS: than the logged in user. %s is the user nickname.
-#: actions/subscriptions.php:74
#, php-format
msgid "These are the people whose notices %s listens to."
msgstr "هؤلاء الأشخاص الذي يستمع %s إليهم."
@@ -6284,7 +5190,6 @@ msgstr "هؤلاء الأشخاص الذي يستمع %s إليهم."
#. TRANS: This message contains Markdown URLs. The link description is between
#. TRANS: square brackets, and the link between parentheses. Do not separate "]("
#. TRANS: and do not change the URL part.
-#: actions/subscriptions.php:133
#, php-format
msgid ""
"You're not listening to anyone's notices right now, try subscribing to "
@@ -6298,226 +5203,230 @@ msgstr ""
#. TRANS: than the logged in user that has no subscriptions. %s is the user nickname.
#. TRANS: Subscription list text when looking at the subscriptions for a of a user that has none
#. TRANS: as an anonymous user. %s is the user nickname.
-#: actions/subscriptions.php:141 actions/subscriptions.php:147
#, fuzzy, php-format
msgid "%s is not listening to anyone."
msgstr "%1$s يستمع الآن إلى إشعاراتك على %2$s."
#. TRANS: Atom feed title. %s is a profile nickname.
-#: actions/subscriptions.php:176
#, fuzzy, php-format
msgid "Subscription feed for %s (Atom)"
msgstr "الردود على %s"
#. TRANS: Checkbox label for enabling Jabber messages for a profile in a subscriptions list.
-#: actions/subscriptions.php:239
-msgid "Jabber"
-msgstr "جابر"
+msgid "IM"
+msgstr "محادثة فورية"
#. TRANS: Checkbox label for enabling SMS messages for a profile in a subscriptions list.
-#: actions/subscriptions.php:254
msgid "SMS"
msgstr "رسائل قصيرة"
-#: actions/tag.php:69
+#. TRANS: Title for all but the first page of notices with tags.
+#. TRANS: %1$s is the tag, %2$d is the page number.
#, php-format
msgid "Notices tagged with %1$s, page %2$d"
msgstr "الإشعارات الموسومة ب%s، الصفحة %2$d"
-#: actions/tag.php:87
#, php-format
msgid "Notice feed for tag %s (RSS 1.0)"
msgstr ""
-#: actions/tag.php:93
#, php-format
msgid "Notice feed for tag %s (RSS 2.0)"
msgstr ""
-#: actions/tag.php:99
#, php-format
msgid "Notice feed for tag %s (Atom)"
msgstr ""
-#: actions/tagother.php:39
msgid "No ID argument."
msgstr "لا مدخل هوية."
-#: actions/tagother.php:65
#, fuzzy, php-format
msgid "Tag %s"
msgstr "الوسوم"
-#. TRANS: H2 for user profile information.
-#: actions/tagother.php:77 lib/userprofile.php:76
msgid "User profile"
msgstr "ملف المستخدم الشخصي"
-#. TRANS: DT element on Authorise Subscription page.
-#. TRANS: DT element in area for user avatar.
-#: actions/tagother.php:81 actions/userauthorization.php:138
-#: lib/userprofile.php:108
-msgid "Photo"
-msgstr "صورة"
-
-#: actions/tagother.php:141
msgid "Tag user"
msgstr "اوسم المستخدم"
-#: actions/tagother.php:151
#, fuzzy
msgid ""
-"Tags for this user (letters, numbers, -, ., and _), comma- or space- "
-"separated"
+"Tags for this user (letters, numbers, -, ., and _), separated by commas or "
+"spaces."
msgstr ""
"سِم نفسك (حروف وأرقام و \"-\" و \".\" و \"_\")، افصلها بفاصلة (',') أو مسافة."
-#: actions/tagother.php:178
-#, php-format
-msgid "Invalid tag: \"%s\""
-msgstr "وسم غير صالح: \"%s\""
-
-#: actions/tagother.php:193
msgid ""
"You can only tag people you are subscribed to or who are subscribed to you."
msgstr ""
-#: actions/tagother.php:236
#, fuzzy
msgid "Use this form to add tags to your subscribers or subscriptions."
msgstr "استخدم هذا النموذج لتعدل تطبيقك."
-#: actions/tagrss.php:35
msgid "No such tag."
msgstr "لا وسم كهذا."
#. TRANS: Client error displayed when trying to unblock a non-blocked user.
-#: actions/unblock.php:59
msgid "You haven't blocked that user."
msgstr "لم تمنع هذا المستخدم."
-#: actions/unsandbox.php:72
msgid "User is not sandboxed."
msgstr "المستخدم ليس في صندوق الرمل."
-#: actions/unsilence.php:72
msgid "User is not silenced."
msgstr "المستخدم ليس مُسكتًا."
-#: actions/unsubscribe.php:77
#, fuzzy
msgid "No profile ID in request."
msgstr "لا طلب استيثاق."
-#: actions/unsubscribe.php:98
msgid "Unsubscribed"
msgstr "غير مشترك"
-#: actions/updateprofile.php:64
#, php-format
msgid ""
"Listenee stream license ‘%1$s’ is not compatible with site license ‘%2$s’."
msgstr ""
+#, fuzzy
+msgid "URL settings"
+msgstr "إعدادات المراسلة الفورية"
+
+#. TRANS: Instructions for tab "Other" in user profile settings.
+msgid "Manage various other options."
+msgstr "أدر خيارات أخرى عديدة."
+
+#. TRANS: Used as a suffix for free URL shorteners in a dropdown list in the tab "Other" of a
+#. TRANS: user's profile settings. This message has one space at the beginning. Use your
+#. TRANS: language's word separator here if it has one (most likely a single space).
+msgid " (free service)"
+msgstr " (خدمة حرة)"
+
+#, fuzzy
+msgid "[none]"
+msgstr "لا شيء"
+
+msgid "[internal]"
+msgstr ""
+
+#. TRANS: Label for dropdown with URL shortener services.
+msgid "Shorten URLs with"
+msgstr "قصّر المسارات بـ"
+
+#. TRANS: Tooltip for for dropdown with URL shortener services.
+msgid "Automatic shortening service to use."
+msgstr "خدمة التقصير المطلوب استخدامها."
+
+msgid "URL longer than"
+msgstr ""
+
+msgid "URLs longer than this will be shortened, 0 means always shorten."
+msgstr ""
+
+msgid "Text longer than"
+msgstr ""
+
+msgid ""
+"URLs in notices longer than this will be shortened, 0 means always shorten."
+msgstr ""
+
+#. TRANS: Form validation error for form "Other settings" in user profile.
+#, fuzzy
+msgid "URL shortening service is too long (maximum 50 characters)."
+msgstr "الاسم الكامل طويل جدا (الأقصى 255 حرفًا)"
+
+msgid "Invalid number for max url length."
+msgstr ""
+
+#, fuzzy
+msgid "Invalid number for max notice length."
+msgstr "محتوى إشعار غير صالح."
+
+msgid "Error saving user URL shortening preferences."
+msgstr ""
+
#. TRANS: User admin panel title
-#: actions/useradminpanel.php:58
msgctxt "TITLE"
msgid "User"
msgstr "المستخدم"
#. TRANS: Instruction for user admin panel.
-#: actions/useradminpanel.php:69
msgid "User settings for this StatusNet site"
msgstr ""
#. TRANS: Form validation error in user admin panel when a non-numeric character limit was set.
-#: actions/useradminpanel.php:147
msgid "Invalid bio limit. Must be numeric."
msgstr ""
#. TRANS: Form validation error in user admin panel when welcome text is too long.
-#: actions/useradminpanel.php:154
#, fuzzy
msgid "Invalid welcome text. Maximum length is 255 characters."
msgstr "رسالة ترحيب غير صالحة. أقصى طول هو 255 حرف."
#. TRANS: Client error displayed when trying to set a non-existing user as default subscription for new
#. TRANS: users in user admin panel. %1$s is the invalid nickname.
-#: actions/useradminpanel.php:166
#, php-format
msgid "Invalid default subscripton: \"%1$s\" is not a user."
msgstr ""
-#. TRANS: Link description in user account settings menu.
-#: actions/useradminpanel.php:215 lib/accountsettingsaction.php:106
msgid "Profile"
msgstr "الملف الشخصي"
#. TRANS: Field label in user admin panel for setting the character limit for the bio field.
-#: actions/useradminpanel.php:220
msgid "Bio Limit"
msgstr "حد السيرة"
#. TRANS: Tooltip in user admin panel for setting the character limit for the bio field.
-#: actions/useradminpanel.php:222
msgid "Maximum length of a profile bio in characters."
msgstr "الحد الأقصى لطول التعريف الشخصي في حساب المستخدم (بالأحرف)."
#. TRANS: Form legend in user admin panel.
-#: actions/useradminpanel.php:231
msgid "New users"
msgstr "مستخدمون جدد"
#. TRANS: Field label in user admin panel for setting new user welcome text.
-#: actions/useradminpanel.php:236
msgid "New user welcome"
msgstr "ترحيب المستخدمين الجدد"
#. TRANS: Tooltip in user admin panel for setting new user welcome text.
-#: actions/useradminpanel.php:238
#, fuzzy
msgid "Welcome text for new users (maximum 255 characters)."
msgstr "نص الترحيب بالمستخدمين الجدد (255 حرفًا كحد أقصى)."
#. TRANS: Field label in user admin panel for setting default subscription for new users.
-#: actions/useradminpanel.php:244
msgid "Default subscription"
msgstr "الاشتراك المبدئي"
#. TRANS: Tooltip in user admin panel for setting default subscription for new users.
-#: actions/useradminpanel.php:246
msgid "Automatically subscribe new users to this user."
msgstr "أشرك المستخدمين الجدد بهذا المستخدم تلقائيًا."
#. TRANS: Form legend in user admin panel.
-#: actions/useradminpanel.php:256
msgid "Invitations"
msgstr "الدعوات"
#. TRANS: Field label for checkbox in user admin panel for allowing users to invite friend using site e-mail.
-#: actions/useradminpanel.php:262
msgid "Invitations enabled"
msgstr "الدعوات مُفعلة"
#. TRANS: Tooltip for checkbox in user admin panel for allowing users to invite friend using site e-mail.
-#: actions/useradminpanel.php:265
msgid "Whether to allow users to invite new users."
msgstr "اسمح للمستخدمين بدعوة مستخدمين جدد."
#. TRANS: Title for button to save user settings in user admin panel.
-#: actions/useradminpanel.php:302
#, fuzzy
msgid "Save user settings."
msgstr "احفظ إعدادات المستخدم"
#. TRANS: Page title.
-#: actions/userauthorization.php:109
#, fuzzy
msgid "Authorize subscription"
msgstr "جميع الاشتراكات"
#. TRANS: Page notice on "Auhtorize subscription" page.
-#: actions/userauthorization.php:115
#, fuzzy
msgid ""
"Please check these details to make sure that you want to subscribe to this "
@@ -6527,51 +5436,37 @@ msgstr ""
"يُرجى التحقق من هذه التفاصيل للتأكد من أنك تريد الاستماع لإشعارات هذا "
"المستخدم. إذا لم تطلب للتو الاستماع لإشعارات شخص ما فانقر \"ارفض\"."
-#. TRANS: DT element on Authorise Subscription page where license is displayed.
-#. TRANS: Menu item for site administration
-#: actions/userauthorization.php:207 actions/version.php:167
-#: lib/adminpanelaction.php:403
-msgid "License"
-msgstr "الرخصة"
-
#. TRANS: Button text on Authorise Subscription page.
-#: actions/userauthorization.php:229
#, fuzzy
msgctxt "BUTTON"
msgid "Accept"
msgstr "اقبل"
#. TRANS: Title for button on Authorise Subscription page.
-#: actions/userauthorization.php:231
#, fuzzy
msgid "Subscribe to this user."
msgstr "اشترك بهذا المستخدم"
#. TRANS: Button text on Authorise Subscription page.
-#: actions/userauthorization.php:233
#, fuzzy
msgctxt "BUTTON"
msgid "Reject"
msgstr "ارفض"
#. TRANS: Title for button on Authorise Subscription page.
-#: actions/userauthorization.php:235
#, fuzzy
msgid "Reject this subscription."
msgstr "ارفض هذا الاشتراك"
#. TRANS: Client error displayed for an empty authorisation request.
-#: actions/userauthorization.php:248
msgid "No authorization request!"
msgstr "لا طلب استيثاق!"
#. TRANS: Accept message header from Authorise subscription page.
-#: actions/userauthorization.php:271
#, fuzzy
msgid "Subscription authorized"
msgstr "رُفض الاشتراك"
-#: actions/userauthorization.php:274
msgid ""
"The subscription has been authorized, but no callback URL was passed. Check "
"with the site’s instructions for details on how to authorize the "
@@ -6579,11 +5474,9 @@ msgid ""
msgstr ""
#. TRANS: Reject message header from Authorise subscription page.
-#: actions/userauthorization.php:285
msgid "Subscription rejected"
msgstr "رُفض الاشتراك"
-#: actions/userauthorization.php:288
msgid ""
"The subscription has been rejected, but no callback URL was passed. Check "
"with the site’s instructions for details on how to fully reject the "
@@ -6592,35 +5485,30 @@ msgstr ""
#. TRANS: Exception thrown when no valid user is found for an authorisation request.
#. TRANS: %s is a listener URI.
-#: actions/userauthorization.php:325
#, php-format
msgid "Listener URI \"%s\" not found here."
msgstr ""
#. TRANS: Exception thrown when listenee URI is too long for an authorisation request.
#. TRANS: %s is a listenee URI.
-#: actions/userauthorization.php:332
#, fuzzy, php-format
msgid "Listenee URI \"%s\" is too long."
msgstr "المسار المصدر طويل جدًا."
#. TRANS: Exception thrown when listenee URI is a local user for an authorisation request.
#. TRANS: %s is a listenee URI.
-#: actions/userauthorization.php:340
#, php-format
msgid "Listenee URI \"%s\" is a local user."
msgstr ""
#. TRANS: Exception thrown when profile URL is a local user for an authorisation request.
#. TRANS: %s is a profile URL.
-#: actions/userauthorization.php:358
#, php-format
msgid "Profile URL \"%s\" is for a local user."
msgstr ""
#. TRANS: Exception thrown when licenses are not compatible for an authorisation request.
#. TRANS: %1$s is the license for the listenee, %2$s is the license for "this" StatusNet site.
-#: actions/userauthorization.php:368
#, php-format
msgid ""
"Listenee stream license \"%1$s\" is not compatible with site license \"%2$s"
@@ -6629,57 +5517,61 @@ msgstr ""
#. TRANS: Exception thrown when avatar URL is invalid for an authorisation request.
#. TRANS: %s is an avatar URL.
-#: actions/userauthorization.php:378
#, fuzzy, php-format
msgid "Avatar URL \"%s\" is not valid."
msgstr "مسار المصدر ليس صحيحا."
#. TRANS: Exception thrown when avatar URL could not be read for an authorisation request.
#. TRANS: %s is an avatar URL.
-#: actions/userauthorization.php:385
#, fuzzy, php-format
msgid "Cannot read avatar URL \"%s\"."
msgstr "تعذر قراءة رابط الأفتار ‘%s’."
#. TRANS: Exception thrown when avatar URL return an invalid image type for an authorisation request.
#. TRANS: %s is an avatar URL.
-#: actions/userauthorization.php:392
#, php-format
msgid "Wrong image type for avatar URL \"%s\"."
msgstr ""
#. TRANS: Page title for profile design page.
-#: actions/userdesignsettings.php:74 lib/designsettings.php:63
msgid "Profile design"
msgstr "تصميم الملف الشخصي"
#. TRANS: Instructions for profile design page.
-#: actions/userdesignsettings.php:84 lib/designsettings.php:74
msgid ""
"Customize the way your profile looks with a background image and a colour "
"palette of your choice."
msgstr "خصّص أسلوب عرض ملفك بصورة خلفية ومخطط ألوان من اختيارك."
-#: actions/userdesignsettings.php:272
msgid "Enjoy your hotdog!"
msgstr "استمتع بالنقانق!"
+#, fuzzy
+msgid "Design settings"
+msgstr "اذف إعدادت الموقع"
+
+msgid "View profile designs"
+msgstr "اعرض تصاميم الملف الشخصي"
+
+msgid "Show or hide profile designs."
+msgstr "أظهر أو أخفِ تصاميم الملفات الشخصية."
+
+#, fuzzy
+msgid "Background file"
+msgstr "الخلفية"
+
#. TRANS: Message is used as a page title. %1$s is a nick name, %2$d is a page number.
-#: actions/usergroups.php:66
#, php-format
msgid "%1$s groups, page %2$d"
msgstr "مجموعات %1$s، الصفحة %2$d"
-#: actions/usergroups.php:132
msgid "Search for more groups"
msgstr "ابحث عن المزيد من المجموعات"
-#: actions/usergroups.php:159
#, php-format
msgid "%s is not a member of any group."
msgstr "%s ليس عضوًا في أي مجموعة."
-#: actions/usergroups.php:164
#, php-format
msgid "Try [searching for groups](%%action.groupsearch%%) and joining them."
msgstr "جرّب [البحث عن مجموعات](%%action.groupsearch%%) والانضمام إليها."
@@ -6689,18 +5581,14 @@ msgstr "جرّب [البحث عن مجموعات](%%action.groupsearch%%) وال
#. TRANS: %1$s is a group name, %2$s is a site name.
#. TRANS: Message is used as a subtitle in atom user notice feed.
#. TRANS: %1$s is a user name, %2$s is a site name.
-#: actions/userrss.php:97 lib/atomgroupnoticefeed.php:70
-#: lib/atomusernoticefeed.php:95
#, fuzzy, php-format
msgid "Updates from %1$s on %2$s!"
msgstr "الإشعارات التي فضلها %1$s في %2$s!"
-#: actions/version.php:75
#, php-format
msgid "StatusNet %s"
msgstr "ستاتس نت %s"
-#: actions/version.php:155
#, php-format
msgid ""
"This site is powered by %1$s version %2$s, Copyright 2008-2010 StatusNet, "
@@ -6709,11 +5597,13 @@ msgstr ""
"هذا الموقع يشغله %1$s النسخة %2$s، حقوق النشر 2008-2010 StatusNet, Inc "
"ومساهموها."
-#: actions/version.php:163
msgid "Contributors"
msgstr "المساهمون"
-#: actions/version.php:170
+#. TRANS: Menu item for site administration
+msgid "License"
+msgstr "الرخصة"
+
msgid ""
"StatusNet is free software: you can redistribute it and/or modify it under "
"the terms of the GNU Affero General Public License as published by the Free "
@@ -6724,7 +5614,6 @@ msgstr ""
"العمومية كما نشرتها مؤسسة البرمجيات الحرة، برخصتها الثالثة أو أي نسخة تليها "
"(أيهما تشاء)."
-#: actions/version.php:176
msgid ""
"This program is distributed in the hope that it will be useful, but WITHOUT "
"ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or "
@@ -6732,53 +5621,53 @@ msgid ""
"for more details. "
msgstr ""
-#: actions/version.php:182
#, php-format
msgid ""
"You should have received a copy of the GNU Affero General Public License "
"along with this program. If not, see %s."
msgstr ""
-#: actions/version.php:191
+#. TRANS: Menu item for site administration
msgid "Plugins"
msgstr "الملحقات"
+#. TRANS: Form input field label for application name.
+msgid "Name"
+msgstr "الاسم"
+
#. TRANS: Secondary navigation menu option leading to version information on the StatusNet site.
-#: actions/version.php:198 lib/action.php:898
msgid "Version"
msgstr "النسخة"
-#: actions/version.php:199
msgid "Author(s)"
msgstr "المؤلف(ون)"
+#. TRANS: Form input field label.
+msgid "Description"
+msgstr "الوصف"
+
#. TRANS: Activity title when marking a notice as favorite.
-#: classes/Fave.php:164
msgid "Favor"
msgstr "فضّل"
#. TRANS: Ntofication given when a user marks a notice as favorite.
#. TRANS: %1$s is a user nickname or full name, %2$s is a notice URI.
-#: classes/Fave.php:167
#, fuzzy, php-format
msgid "%1$s marked notice %2$s as a favorite."
msgstr "لقد أضاف %s (@%s) إشعارك إلى مفضلاته"
#. TRANS: Server exception thrown when a URL cannot be processed.
-#: classes/File.php:162
#, php-format
msgid "Cannot process URL '%s'"
msgstr ""
#. TRANS: Server exception thrown when... Robin thinks something is impossible!
-#: classes/File.php:194
msgid "Robin thinks something is impossible."
msgstr ""
#. TRANS: Message given if an upload is larger than the configured maximum.
#. TRANS: %1$d is the byte limit for uploads, %2$d is the byte count for the uploaded file.
#. TRANS: %1$s is used for plural.
-#: classes/File.php:210
#, php-format
msgid ""
"No file may be larger than %1$d byte and the file you sent was %2$d bytes. "
@@ -6795,7 +5684,6 @@ msgstr[5] ""
#. TRANS: Message given if an upload would exceed user quota.
#. TRANS: %d (number) is the user quota in bytes and is used for plural.
-#: classes/File.php:223
#, php-format
msgid "A file this large would exceed your user quota of %d byte."
msgid_plural "A file this large would exceed your user quota of %d bytes."
@@ -6808,7 +5696,6 @@ msgstr[5] ""
#. TRANS: Message given id an upload would exceed a user's monthly quota.
#. TRANS: $d (number) is the monthly user quota in bytes and is used for plural.
-#: classes/File.php:235
#, php-format
msgid "A file this large would exceed your monthly quota of %d byte."
msgid_plural "A file this large would exceed your monthly quota of %d bytes."
@@ -6820,160 +5707,133 @@ msgstr[4] ""
msgstr[5] ""
#. TRANS: Client exception thrown if a file upload does not have a valid name.
-#: classes/File.php:282 classes/File.php:297
#, fuzzy
msgid "Invalid filename."
msgstr "حجم غير صالح."
#. TRANS: Exception thrown when joining a group fails.
-#: classes/Group_member.php:51
msgid "Group join failed."
msgstr "الانضمام للمجموعة فشل."
#. TRANS: Exception thrown when trying to leave a group the user is not a member of.
-#: classes/Group_member.php:64
msgid "Not part of group."
msgstr "ليس جزءا من المجموعة."
#. TRANS: Exception thrown when trying to leave a group fails.
-#: classes/Group_member.php:72
msgid "Group leave failed."
msgstr "ترك المجموعة فشل."
#. TRANS: Exception thrown providing an invalid profile ID.
#. TRANS: %s is the invalid profile ID.
-#: classes/Group_member.php:85
#, php-format
msgid "Profile ID %s is invalid."
msgstr ""
#. TRANS: Exception thrown providing an invalid group ID.
#. TRANS: %s is the invalid group ID.
-#: classes/Group_member.php:98
#, fuzzy, php-format
msgid "Group ID %s is invalid."
msgstr "خطأ أثناء حفظ المستخدم؛ غير صالح."
#. TRANS: Activity title.
-#: classes/Group_member.php:147 lib/joinform.php:114
msgid "Join"
msgstr "انضم"
#. TRANS: Success message for subscribe to group attempt through OStatus.
#. TRANS: %1$s is the member name, %2$s is the subscribed group's name.
-#: classes/Group_member.php:151
#, php-format
msgid "%1$s has joined group %2$s."
msgstr ""
#. TRANS: Server exception thrown when updating a local group fails.
-#: classes/Local_group.php:42
msgid "Could not update local group."
msgstr "تعذر تحديث المجموعة المحلية."
#. TRANS: Exception thrown when trying creating a login token failed.
#. TRANS: %s is the user nickname for which token creation failed.
-#: classes/Login_token.php:78
#, php-format
msgid "Could not create login token for %s"
msgstr "لم يمكن إنشاء توكن الولوج ل%s"
#. TRANS: Exception thrown when database name or Data Source Name could not be found.
-#: classes/Memcached_DataObject.php:542
msgid "No database name or DSN found anywhere."
msgstr ""
#. TRANS: Client exception thrown when a user tries to send a direct message while being banned from sending them.
-#: classes/Message.php:45
msgid "You are banned from sending direct messages."
msgstr "أنت ممنوع من إرسال رسائل مباشرة."
#. TRANS: Message given when a message could not be stored on the server.
-#: classes/Message.php:69
msgid "Could not insert message."
msgstr "تعذّر إدراج الرسالة."
#. TRANS: Message given when a message could not be updated on the server.
-#: classes/Message.php:80
#, fuzzy
msgid "Could not update message with new URI."
msgstr "تعذّر تحليل الرسالة."
#. TRANS: Server exception thrown when a user profile for a notice cannot be found.
#. TRANS: %1$d is a profile ID (number), %2$d is a notice ID (number).
-#: classes/Notice.php:98
#, php-format
msgid "No such profile (%1$d) for notice (%2$d)."
msgstr ""
#. TRANS: Server exception. %s are the error details.
-#: classes/Notice.php:199
#, php-format
msgid "Database error inserting hashtag: %s"
msgstr "خطأ في قاعدة البيانات أثناء حذف مستخدم تطبيق OAuth."
#. TRANS: Client exception thrown if a notice contains too many characters.
-#: classes/Notice.php:279
msgid "Problem saving notice. Too long."
msgstr "مشكلة في حفظ الإشعار. طويل جدًا."
#. TRANS: Client exception thrown when trying to save a notice for an unknown user.
-#: classes/Notice.php:284
msgid "Problem saving notice. Unknown user."
msgstr "مشكلة في حفظ الإشعار. مستخدم غير معروف."
#. TRANS: Client exception thrown when a user tries to post too many notices in a given time frame.
-#: classes/Notice.php:290
msgid ""
"Too many notices too fast; take a breather and post again in a few minutes."
msgstr ""
#. TRANS: Client exception thrown when a user tries to post too many duplicate notices in a given time frame.
-#: classes/Notice.php:297
msgid ""
"Too many duplicate messages too quickly; take a breather and post again in a "
"few minutes."
msgstr ""
#. TRANS: Client exception thrown when a user tries to post while being banned.
-#: classes/Notice.php:305
#, fuzzy
msgid "You are banned from posting notices on this site."
msgstr "أنت ممنوع من إرسال رسائل مباشرة."
#. TRANS: Server exception thrown when a notice cannot be saved.
#. TRANS: Server exception thrown when a notice cannot be updated.
-#: classes/Notice.php:372 classes/Notice.php:399
msgid "Problem saving notice."
msgstr "مشكلة أثناء حفظ الإشعار."
#. TRANS: Server exception thrown when no array is provided to the method saveKnownGroups().
-#: classes/Notice.php:929
msgid "Bad type provided to saveKnownGroups."
msgstr ""
#. TRANS: Server exception thrown when an update for a group inbox fails.
-#: classes/Notice.php:1028
#, fuzzy
msgid "Problem saving group inbox."
msgstr "مشكلة أثناء حفظ الإشعار."
#. TRANS: Server exception thrown when a reply cannot be saved.
#. TRANS: %1$d is a notice ID, %2$d is the ID of the mentioned user.
-#: classes/Notice.php:1142
#, fuzzy, php-format
msgid "Could not save reply for %1$d, %2$d."
msgstr "تعذر تحديث المجموعة المحلية."
#. TRANS: Message used to repeat a notice. RT is the abbreviation of 'retweet'.
#. TRANS: %1$s is the repeated user's name, %2$s is the repeated notice.
-#: classes/Notice.php:1661
#, php-format
msgid "RT @%1$s %2$s"
msgstr "RT @%1$s %2$s"
#. TRANS: Full name of a profile or group followed by nickname in parens
-#: classes/Profile.php:172 classes/User_group.php:242
#, fuzzy, php-format
msgctxt "FANCYNAME"
msgid "%1$s (%2$s)"
@@ -6981,389 +5841,187 @@ msgstr "%1$s (%2$s)"
#. TRANS: Exception thrown when trying to revoke an existing role for a user that does not exist.
#. TRANS: %1$s is the role name, %2$s is the user ID (number).
-#: classes/Profile.php:775
#, php-format
msgid "Cannot revoke role \"%1$s\" for user #%2$d; does not exist."
msgstr ""
#. TRANS: Exception thrown when trying to revoke a role for a user with a failing database query.
#. TRANS: %1$s is the role name, %2$s is the user ID (number).
-#: classes/Profile.php:784
#, php-format
msgid "Cannot revoke role \"%1$s\" for user #%2$d; database error."
msgstr ""
#. TRANS: Exception thrown when a right for a non-existing user profile is checked.
-#: classes/Remote_profile.php:54
#, fuzzy
msgid "Missing profile."
msgstr "ليس للمستخدم ملف شخصي."
#. TRANS: Exception thrown when a tag cannot be saved.
-#: classes/Status_network.php:338
#, fuzzy
msgid "Unable to save tag."
msgstr "تعذّر حفظ إشعار الموقع."
#. TRANS: Exception thrown when trying to subscribe while being banned from subscribing.
-#: classes/Subscription.php:77 lib/oauthstore.php:482
msgid "You have been banned from subscribing."
msgstr ""
#. TRANS: Exception thrown when trying to subscribe while already subscribed.
-#: classes/Subscription.php:82
msgid "Already subscribed!"
msgstr "مُشترك أصلا!"
#. TRANS: Exception thrown when trying to subscribe to a user who has blocked the subscribing user.
-#: classes/Subscription.php:87
msgid "User has blocked you."
msgstr "لقد منعك المستخدم."
#. TRANS: Exception thrown when trying to unsibscribe without a subscription.
-#: classes/Subscription.php:176
msgid "Not subscribed!"
msgstr "غير مشترك!"
#. TRANS: Exception thrown when trying to unsubscribe a user from themselves.
-#: classes/Subscription.php:183
msgid "Could not delete self-subscription."
msgstr "تعذّر حذف الاشتراك الذاتي."
#. TRANS: Exception thrown when the OMB token for a subscription could not deleted on the server.
-#: classes/Subscription.php:211
msgid "Could not delete subscription OMB token."
msgstr "تعذّر حفظ الاشتراك."
#. TRANS: Exception thrown when a subscription could not be deleted on the server.
-#: classes/Subscription.php:223
msgid "Could not delete subscription."
msgstr "تعذّر حفظ الاشتراك."
#. TRANS: Activity tile when subscribing to another person.
-#: classes/Subscription.php:265
msgid "Follow"
msgstr ""
#. TRANS: Notification given when one person starts following another.
#. TRANS: %1$s is the subscriber, %2$s is the subscribed.
-#: classes/Subscription.php:268
#, fuzzy, php-format
msgid "%1$s is now following %2$s."
msgstr "%1$s يستمع الآن إلى إشعاراتك على %2$s."
#. TRANS: Notice given on user registration.
#. TRANS: %1$s is the sitename, $2$s is the registering user's nickname.
-#: classes/User.php:395
#, php-format
msgid "Welcome to %1$s, @%2$s!"
msgstr "أهلا بكم في %1$s يا @%2$s!"
#. TRANS: Server exception.
-#: classes/User.php:918
msgid "No single user defined for single-user mode."
msgstr ""
#. TRANS: Server exception.
-#: classes/User.php:922
msgid "Single-user mode code called when not enabled."
msgstr ""
#. TRANS: Server exception thrown when creating a group failed.
-#: classes/User_group.php:522
msgid "Could not create group."
msgstr "تعذّر إنشاء المجموعة."
#. TRANS: Server exception thrown when updating a group URI failed.
-#: classes/User_group.php:532
msgid "Could not set group URI."
msgstr "تعذّر إنشاء المجموعة."
#. TRANS: Server exception thrown when setting group membership failed.
-#: classes/User_group.php:555
msgid "Could not set group membership."
msgstr "تعذّر ضبط عضوية المجموعة."
#. TRANS: Server exception thrown when saving local group information failed.
-#: classes/User_group.php:570
msgid "Could not save local group info."
msgstr "تعذر تحديث المجموعة المحلية."
#. TRANS: Exception thrown when an account could not be located when it should be moved.
#. TRANS: %s is the remote site.
-#: lib/accountmover.php:65
#, fuzzy, php-format
msgid "Cannot locate account %s."
msgstr "لا يمكنك حذف المستخدمين."
#. TRANS: Exception thrown when a service document could not be located account move.
#. TRANS: %s is the remote site.
-#: lib/accountmover.php:106
#, php-format
msgid "Cannot find XRD for %s."
msgstr ""
#. TRANS: Exception thrown when an account could not be located when it should be moved.
#. TRANS: %s is the remote site.
-#: lib/accountmover.php:131
#, php-format
msgid "No AtomPub API service for %s."
msgstr ""
-#. TRANS: Link title attribute in user account settings menu.
-#: lib/accountsettingsaction.php:104
-msgid "Change your profile settings"
-msgstr "غيّر إعدادات ملفك الشخصي"
+#. TRANS: H2 for user actions in a profile.
+#. TRANS: H2 for entity actions in a profile.
+msgid "User actions"
+msgstr "تصرفات المستخدم"
-#. TRANS: Link title attribute in user account settings menu.
-#: lib/accountsettingsaction.php:111
-msgid "Upload an avatar"
-msgstr "ارفع أفتارًا"
+#. TRANS: Text shown in user profile of not yet compeltely deleted users.
+msgid "User deletion in progress..."
+msgstr "حذف المستخدم قيد التنفيذ..."
-#. TRANS: Link title attribute in user account settings menu.
-#: lib/accountsettingsaction.php:118
-msgid "Change your password"
-msgstr "غير كلمة سرّك"
+#. TRANS: Link title for link on user profile.
+msgid "Edit profile settings"
+msgstr "عدّل إعدادات الملف الشخصي"
-#. TRANS: Link title attribute in user account settings menu.
-#: lib/accountsettingsaction.php:125
-msgid "Change email handling"
-msgstr "غير أسلوب التعامل مع البريد الإلكتروني"
+#. TRANS: Link text for link on user profile.
+msgid "Edit"
+msgstr "عدّل"
-#. TRANS: Link title attribute in user account settings menu.
-#: lib/accountsettingsaction.php:132
-msgid "Design your profile"
-msgstr "صمّم ملفك الشخصي"
+#. TRANS: Link title for link on user profile.
+msgid "Send a direct message to this user"
+msgstr "أرسل رسالة مباشرة إلى هذا المستخدم"
-#. TRANS: Link title attribute in user account settings menu.
-#: lib/accountsettingsaction.php:139
-msgid "Other options"
-msgstr "خيارات أخرى"
+#. TRANS: Link text for link on user profile.
+msgid "Message"
+msgstr "رسالة"
-#. TRANS: Link description in user account settings menu.
-#: lib/accountsettingsaction.php:141
-msgid "Other"
-msgstr "أخرى"
+#. TRANS: Label text on user profile to select a user role.
+msgid "Moderate"
+msgstr "راقب"
+
+#. TRANS: Label text on user profile to select a user role.
+msgid "User role"
+msgstr "دور المستخدم"
+
+#. TRANS: Role that can be set for a user profile.
+msgctxt "role"
+msgid "Administrator"
+msgstr "إداري"
+
+#. TRANS: Role that can be set for a user profile.
+msgctxt "role"
+msgid "Moderator"
+msgstr "مراقب"
#. TRANS: Page title. %1$s is the title, %2$s is the site name.
-#: lib/action.php:161
#, php-format
msgid "%1$s - %2$s"
msgstr "%1$s - %2$s"
#. TRANS: Page title for a page without a title set.
-#: lib/action.php:177
msgid "Untitled page"
msgstr "صفحة غير مُعنونة"
#. TRANS: Localized tooltip for '...' expansion button on overlong remote messages.
-#: lib/action.php:325
msgctxt "TOOLTIP"
msgid "Show more"
msgstr ""
-#. TRANS: DT element for primary navigation menu. String is hidden in default CSS.
-#: lib/action.php:544
+#. TRANS: Inline reply form submit button: submits a reply comment.
#, fuzzy
-msgid "Primary site navigation"
-msgstr "ضبط الموقع الأساسي"
+msgctxt "BUTTON"
+msgid "Reply"
+msgstr "رُد"
-#. TRANS: Tooltip for main menu option "Personal".
-#: lib/action.php:550
-msgctxt "TOOLTIP"
-msgid "Personal profile and friends timeline"
-msgstr "الملف الشخصي ومسار الأصدقاء الزمني"
+#. TRANS: Placeholder text for inline reply form. Clicking in this box will turn it into a mini notice form.
+msgid "Write a reply..."
+msgstr ""
-#. TRANS: Main menu option when logged in for access to personal profile and friends timeline.
-#. TRANS: Personal group navigation menu option when logged in for viewing timeline of self and friends.
-#: lib/action.php:553 lib/personalgroupnav.php:100
-msgctxt "MENU"
-msgid "Personal"
-msgstr "الصفحة الشخصية"
-
-#. TRANS: Tooltip for main menu option "Account".
-#: lib/action.php:555
-msgctxt "TOOLTIP"
-msgid "Change your email, avatar, password, profile"
-msgstr "غير بريدك الإلكتروني وكلمة سرّك وأفتارك وملفك الشخصي"
-
-#. TRANS: Main menu option when logged in for access to user settings.
-#: lib/action.php:558
-msgid "Account"
-msgstr "الحساب"
-
-#. TRANS: Tooltip for main menu option "Services".
-#: lib/action.php:560
-msgctxt "TOOLTIP"
-msgid "Connect to services"
-msgstr "اتصل بالخدمات"
-
-#. TRANS: Main menu option when logged in and connection are possible for access to options to connect to other services.
-#: lib/action.php:563
-msgid "Connect"
-msgstr "اتصل"
-
-#. TRANS: Tooltip for menu option "Admin".
-#: lib/action.php:566
-msgctxt "TOOLTIP"
-msgid "Change site configuration"
-msgstr "غيّر ضبط الموقع"
-
-#. TRANS: Main menu option when logged in and site admin for access to site configuration.
-#. TRANS: Menu item in the group navigation page. Only shown for group administrators.
-#: lib/action.php:569 lib/groupnav.php:117
-msgctxt "MENU"
-msgid "Admin"
-msgstr "إداري"
-
-#. TRANS: Tooltip for main menu option "Invite".
-#: lib/action.php:573
-#, php-format
-msgctxt "TOOLTIP"
-msgid "Invite friends and colleagues to join you on %s"
-msgstr "ادعُ أصدقائك وزملائك للانضمام إليك في %s"
-
-#. TRANS: Main menu option when logged in and invitations are allowed for inviting new users.
-#: lib/action.php:576
-msgctxt "MENU"
-msgid "Invite"
-msgstr "ادعُ"
-
-#. TRANS: Tooltip for main menu option "Logout"
-#: lib/action.php:582
-msgctxt "TOOLTIP"
-msgid "Logout from the site"
-msgstr "اخرج من الموقع"
-
-#. TRANS: Main menu option when logged in to log out the current user.
-#: lib/action.php:585
-msgctxt "MENU"
-msgid "Logout"
-msgstr "اخرج"
-
-#. TRANS: Tooltip for main menu option "Register".
-#: lib/action.php:590
-msgctxt "TOOLTIP"
-msgid "Create an account"
-msgstr "أنشئ حسابًا"
-
-#. TRANS: Main menu option when not logged in to register a new account.
-#. TRANS: Menu item for registering with the StatusNet site.
-#: lib/action.php:593 lib/logingroupnav.php:85
-msgctxt "MENU"
-msgid "Register"
-msgstr "سجّل"
-
-#. TRANS: Tooltip for main menu option "Login".
-#: lib/action.php:596
-msgctxt "TOOLTIP"
-msgid "Login to the site"
-msgstr "لُج إلى الموقع"
-
-#. TRANS: Main menu option when not logged in to log in.
-#. TRANS: Menu item for logging in to the StatusNet site.
-#: lib/action.php:599 lib/logingroupnav.php:77
-msgctxt "MENU"
-msgid "Login"
-msgstr "لُج"
-
-#. TRANS: Tooltip for main menu option "Help".
-#: lib/action.php:602
-msgctxt "TOOLTIP"
-msgid "Help me!"
-msgstr "ساعدني!"
-
-#. TRANS: Main menu option for help on the StatusNet site.
-#: lib/action.php:605
-msgctxt "MENU"
-msgid "Help"
-msgstr "مساعدة"
-
-#. TRANS: Tooltip for main menu option "Search".
-#: lib/action.php:608
-msgctxt "TOOLTIP"
-msgid "Search for people or text"
-msgstr "ابحث عن أشخاص أو نصوص"
-
-#. TRANS: Main menu option when logged in or when the StatusNet instance is not private.
-#: lib/action.php:611
-msgctxt "MENU"
-msgid "Search"
-msgstr "ابحث"
-
-#. TRANS: DT element for site notice. String is hidden in default CSS.
-#. TRANS: Menu item for site administration
-#: lib/action.php:633 lib/adminpanelaction.php:387
-msgid "Site notice"
-msgstr "إشعار الموقع"
-
-#. TRANS: DT element for local views block. String is hidden in default CSS.
-#: lib/action.php:700
-msgid "Local views"
-msgstr "المشاهدات المحلية"
-
-#. TRANS: DT element for page notice. String is hidden in default CSS.
-#: lib/action.php:770
-msgid "Page notice"
-msgstr "إشعار الصفحة"
-
-#. TRANS: DT element for secondary navigation menu. String is hidden in default CSS.
-#: lib/action.php:871
#, fuzzy
-msgid "Secondary site navigation"
-msgstr "ضبط الموقع الأساسي"
-
-#. TRANS: Secondary navigation menu option leading to help on StatusNet.
-#: lib/action.php:877
-msgid "Help"
-msgstr "مساعدة"
-
-#. TRANS: Secondary navigation menu option leading to text about StatusNet site.
-#: lib/action.php:880
-msgid "About"
-msgstr "عن"
-
-#. TRANS: Secondary navigation menu option leading to Frequently Asked Questions.
-#: lib/action.php:883
-msgid "FAQ"
-msgstr "الأسئلة المكررة"
-
-#. TRANS: Secondary navigation menu option leading to Terms of Service.
-#: lib/action.php:888
-msgid "TOS"
-msgstr "الشروط"
-
-#. TRANS: Secondary navigation menu option leading to privacy policy.
-#: lib/action.php:892
-msgid "Privacy"
-msgstr "خصوصية"
-
-#. TRANS: Secondary navigation menu option. Leads to information about StatusNet and its license.
-#: lib/action.php:895
-msgid "Source"
-msgstr "المصدر"
-
-#. TRANS: Secondary navigation menu option leading to e-mail contact information on the
-#. TRANS: StatusNet site, where to report bugs, ...
-#: lib/action.php:902
-msgid "Contact"
-msgstr "اتصل"
-
-#. TRANS: Secondary navigation menu option. Leads to information about embedding a timeline widget.
-#: lib/action.php:905
-msgid "Badge"
-msgstr "الجسر"
-
-#. TRANS: DT element for StatusNet software license.
-#: lib/action.php:934
-msgid "StatusNet software license"
-msgstr "رخصة برنامج StatusNet"
+msgid "Status"
+msgstr "ستاتس نت"
#. TRANS: First sentence of the StatusNet site license. Used if 'broughtby' is set.
#. TRANS: Text between [] is a link description, text between () is the link itself.
#. TRANS: Make sure there is no whitespace between "]" and "(".
#. TRANS: "%%site.broughtby%%" is the value of the variable site.broughtby
-#: lib/action.php:941
#, fuzzy, php-format
msgid ""
"**%%site.name%%** is a microblogging service brought to you by [%%site."
@@ -7373,7 +6031,6 @@ msgstr ""
"broughtbyurl%%). "
#. TRANS: First sentence of the StatusNet site license. Used if 'broughtby' is not set.
-#: lib/action.php:944
#, php-format
msgid "**%%site.name%%** is a microblogging service."
msgstr ""
@@ -7382,7 +6039,6 @@ msgstr ""
#. TRANS: Make sure there is no whitespace between "]" and "(".
#. TRANS: Text between [] is a link description, text between () is the link itself.
#. TRANS: %s is the version of StatusNet that is being used.
-#: lib/action.php:951
#, php-format
msgid ""
"It runs the [StatusNet](http://status.net/) microblogging software, version %"
@@ -7393,119 +6049,90 @@ msgstr ""
"المتوفر تحت [رخصة غنو أفيرو العمومية](http://www.fsf.org/licensing/licenses/"
"agpl-3.0.html)."
-#. TRANS: DT element for StatusNet site content license.
-#: lib/action.php:967
-msgid "Site content license"
-msgstr "رخصة محتوى الموقع"
-
#. TRANS: Content license displayed when license is set to 'private'.
#. TRANS: %1$s is the site name.
-#: lib/action.php:974
#, php-format
msgid "Content and data of %1$s are private and confidential."
msgstr ""
#. TRANS: Content license displayed when license is set to 'allrightsreserved'.
#. TRANS: %1$s is the copyright owner.
-#: lib/action.php:981
#, php-format
msgid "Content and data copyright by %1$s. All rights reserved."
msgstr ""
#. TRANS: Content license displayed when license is set to 'allrightsreserved' and no owner is set.
-#: lib/action.php:985
msgid "Content and data copyright by contributors. All rights reserved."
msgstr ""
#. TRANS: license message in footer.
#. TRANS: %1$s is the site name, %2$s is a link to the license URL, with a licence name set in configuration.
-#: lib/action.php:1017
#, php-format
msgid "All %1$s content and data are available under the %2$s license."
msgstr ""
-#. TRANS: DT element for pagination (previous/next, etc.).
-#: lib/action.php:1353
-#, fuzzy
-msgid "Pagination"
-msgstr "تسجيل"
-
#. TRANS: Pagination message to go to a page displaying information more in the
#. TRANS: present than the currently displayed information.
-#: lib/action.php:1364
msgid "After"
msgstr "بعد"
#. TRANS: Pagination message to go to a page displaying information more in the
#. TRANS: past than the currently displayed information.
-#: lib/action.php:1374
msgid "Before"
msgstr "قبل"
#. TRANS: Client exception thrown when a feed instance is a DOMDocument.
-#: lib/activity.php:125
msgid "Expecting a root feed element but got a whole XML document."
msgstr ""
#. TRANS: Client exception thrown when using an unknown verb for the activity importer.
-#: lib/activityimporter.php:81
#, fuzzy, php-format
msgid "Unknown verb: \"%s\"."
msgstr "لغة غير معروفة \"%s\"."
#. TRANS: Client exception thrown when trying to force a subscription for an untrusted user.
-#: lib/activityimporter.php:107
msgid "Cannot force subscription for untrusted user."
msgstr ""
#. TRANS: Client exception thrown when trying to for a remote user to subscribe.
-#: lib/activityimporter.php:117
#, fuzzy
msgid "Cannot force remote user to subscribe."
msgstr "تعذّر تحديث سجل المستخدم."
#. TRANS: Client exception thrown when trying to subscribe to an unknown profile.
-#: lib/activityimporter.php:132
#, fuzzy
msgid "Unknown profile."
msgstr "نوع ملف غير معروف"
#. TRANS: Client exception thrown when trying to import an event not related to the importing user.
-#: lib/activityimporter.php:138
msgid "This activity seems unrelated to our user."
msgstr ""
#. TRANS: Client exception thrown when trying to join a remote group that is not a group.
-#: lib/activityimporter.php:154
msgid "Remote profile is not a group!"
msgstr ""
#. TRANS: Client exception thrown when trying to join a group the importing user is already a member of.
-#: lib/activityimporter.php:163
#, fuzzy
msgid "User is already a member of this group."
msgstr "أنت بالفعل عضو في هذه المجموعة"
#. TRANS: Client exception thrown when trying to import a notice by another user.
#. TRANS: %1$s is the source URI of the notice, %2$s is the URI of the author.
-#: lib/activityimporter.php:201
#, php-format
msgid "Already know about notice %1$s and it has a different author %2$s."
msgstr ""
#. TRANS: Client exception thrown when trying to overwrite the author information for a non-trusted user during import.
-#: lib/activityimporter.php:207
msgid "Not overwriting author info for non-trusted user."
msgstr ""
#. TRANS: Client exception thrown when trying to import a notice without content.
#. TRANS: %s is the notice URI.
-#: lib/activityimporter.php:223
#, fuzzy, php-format
msgid "No content for notice %s."
msgstr "ابحث عن محتويات في الإشعارات"
-#: lib/activitymover.php:84
#, fuzzy, php-format
msgid "No such user %s."
msgstr "لا مستخدم كهذا."
@@ -7516,200 +6143,179 @@ msgstr "لا مستخدم كهذا."
#. TRANS: %1$s is a URL, %2$s is the status, %s$s is the fail reason.
#. TRANS: Exception thrown when post to collection fails with a status that is not handled.
#. TRANS: %1$s is a URL, %2$s is the status, %s$s is the fail reason.
-#: lib/activitysink.php:163 lib/activitysink.php:167 lib/activitysink.php:172
#, fuzzy, php-format
msgctxt "URLSTATUSREASON"
msgid "%1$s %2$s %3$s"
msgstr "%1$s - %2$s"
#. TRANS: Client exception thrown when there is no source attribute.
-#: lib/activityutils.php:200
msgid "Can't handle remote content yet."
msgstr ""
#. TRANS: Client exception thrown when there embedded XML content is found that cannot be processed yet.
-#: lib/activityutils.php:237
msgid "Can't handle embedded XML content yet."
msgstr ""
#. TRANS: Client exception thrown when base64 encoded content is found that cannot be processed yet.
-#: lib/activityutils.php:242
msgid "Can't handle embedded Base64 content yet."
msgstr ""
#. TRANS: Client error message thrown when a user tries to change admin settings but has no access rights.
-#: lib/adminpanelaction.php:96
msgid "You cannot make changes to this site."
msgstr "لا يمكنك إجراء تغييرات على هذا الموقع."
#. TRANS: Client error message throw when a certain panel's settings cannot be changed.
-#: lib/adminpanelaction.php:108
msgid "Changes to that panel are not allowed."
msgstr "التغييرات لهذه اللوحة غير مسموح بها."
#. TRANS: Client error message.
-#: lib/adminpanelaction.php:222
#, fuzzy
msgid "showForm() not implemented."
msgstr "الأمر لم يُجهزّ بعد."
#. TRANS: Client error message
-#: lib/adminpanelaction.php:250
#, fuzzy
msgid "saveSettings() not implemented."
msgstr "الأمر لم يُجهزّ بعد."
#. TRANS: Client error message thrown if design settings could not be deleted in
#. TRANS: the admin panel Design.
-#: lib/adminpanelaction.php:274
msgid "Unable to delete design setting."
msgstr "تعذّر حذف إعدادات التصميم."
+msgid "Home"
+msgstr "الرئيسية"
+
#. TRANS: Menu item title/tooltip
-#: lib/adminpanelaction.php:337
msgid "Basic site configuration"
msgstr "ضبط الموقع الأساسي"
#. TRANS: Menu item for site administration
-#: lib/adminpanelaction.php:339
msgctxt "MENU"
msgid "Site"
msgstr "الموقع"
#. TRANS: Menu item title/tooltip
-#: lib/adminpanelaction.php:345
msgid "Design configuration"
msgstr "ضبط التصميم"
#. TRANS: Menu item for site administration
#. TRANS: Menu item in the group navigation page. Only shown for group administrators.
-#: lib/adminpanelaction.php:347 lib/groupnav.php:135
msgctxt "MENU"
msgid "Design"
msgstr "التصميم"
#. TRANS: Menu item title/tooltip
-#: lib/adminpanelaction.php:353
msgid "User configuration"
msgstr "ضبط المستخدم"
#. TRANS: Menu item for site administration
-#: lib/adminpanelaction.php:355 lib/personalgroupnav.php:122
msgid "User"
msgstr "المستخدم"
#. TRANS: Menu item title/tooltip
-#: lib/adminpanelaction.php:361
msgid "Access configuration"
msgstr "ضبط الحساب"
#. TRANS: Menu item title/tooltip
-#: lib/adminpanelaction.php:369
msgid "Paths configuration"
msgstr "ضبط المسارات"
#. TRANS: Menu item title/tooltip
-#: lib/adminpanelaction.php:377
msgid "Sessions configuration"
msgstr "ضبط الجلسات"
#. TRANS: Menu item title/tooltip
-#: lib/adminpanelaction.php:385
msgid "Edit site notice"
msgstr "عدّل إشعار الموقع"
+#. TRANS: Menu item for site administration
+msgid "Site notice"
+msgstr "إشعار الموقع"
+
#. TRANS: Menu item title/tooltip
-#: lib/adminpanelaction.php:393
#, fuzzy
msgid "Snapshots configuration"
msgstr "ضبط المسارات"
#. TRANS: Menu item title/tooltip
-#: lib/adminpanelaction.php:401
msgid "Set site license"
msgstr ""
+#. TRANS: Menu item title/tooltip
+#, fuzzy
+msgid "Plugins configuration"
+msgstr "ضبط المسارات"
+
#. TRANS: Client error 401.
-#: lib/apiauth.php:111
msgid "API resource requires read-write access, but you only have read access."
msgstr ""
#. TRANS: OAuth exception thrown when no application is found for a given consumer key.
-#: lib/apiauth.php:177
msgid "No application for that consumer key."
msgstr ""
-#: lib/apiauth.php:202 lib/apiauth.php:284
msgid "Not allowed to use API."
msgstr ""
#. TRANS: OAuth exception given when an incorrect access token was given for a user.
-#: lib/apiauth.php:225
msgid "Bad access token."
msgstr ""
#. TRANS: OAuth exception given when no user was found for a given token (no token was found).
-#: lib/apiauth.php:230
msgid "No user for that token."
msgstr ""
#. TRANS: Client error thrown when authentication fails becaus a user clicked "Cancel".
#. TRANS: Client error thrown when authentication fails.
-#: lib/apiauth.php:272 lib/apiauth.php:302
msgid "Could not authenticate you."
msgstr ""
#. TRANS: Server error displayed when trying to create an anynymous OAuth consumer.
-#: lib/apioauthstore.php:45
#, fuzzy
msgid "Could not create anonymous consumer."
msgstr "تعذّر إنشاء الكنى."
#. TRANS: Server error displayed when trying to create an anynymous OAuth application.
-#: lib/apioauthstore.php:69
#, fuzzy
msgid "Could not create anonymous OAuth application."
msgstr "لم يمكن إنشاء التطبيق."
#. TRANS: Exception thrown when no token association could be found.
-#: lib/apioauthstore.php:151
msgid ""
"Could not find a profile and application associated with the request token."
msgstr ""
#. TRANS: Exception thrown when no access token can be issued.
-#: lib/apioauthstore.php:209
#, fuzzy
msgid "Could not issue access token."
msgstr "تعذّر إدراج الرسالة."
-#: lib/apioauthstore.php:317
msgid "Database error inserting OAuth application user."
msgstr "خطأ في قاعدة البيانات أثناء حذف مستخدم تطبيق OAuth."
-#: lib/apioauthstore.php:345
#, fuzzy
msgid "Database error updating OAuth application user."
msgstr "خطأ في قاعدة البيانات أثناء حذف مستخدم تطبيق OAuth."
#. TRANS: Exception thrown when an attempt is made to revoke an unknown token.
-#: lib/apioauthstore.php:371
msgid "Tried to revoke unknown token."
msgstr ""
#. TRANS: Exception thrown when an attempt is made to remove a revoked token.
-#: lib/apioauthstore.php:376
msgid "Failed to delete revoked token."
msgstr ""
+#. TRANS: Form input field label for application icon.
+msgid "Icon"
+msgstr "أيقونة"
+
#. TRANS: Form guide.
-#: lib/applicationeditform.php:182
msgid "Icon for this application"
msgstr "أيقونة لهذا التطبيق"
#. TRANS: Form input field instructions.
#. TRANS: %d is the number of available characters for the description.
-#: lib/applicationeditform.php:201
#, fuzzy, php-format
msgid "Describe your application in %d character"
msgid_plural "Describe your application in %d characters"
@@ -7721,220 +6327,176 @@ msgstr[4] "صف تطبيقك"
msgstr[5] "صف تطبيقك"
#. TRANS: Form input field instructions.
-#: lib/applicationeditform.php:205
msgid "Describe your application"
msgstr "صف تطبيقك"
#. TRANS: Form input field instructions.
-#: lib/applicationeditform.php:216
msgid "URL of the homepage of this application"
msgstr "مسار صفحة هذا التطبيق"
#. TRANS: Form input field label.
-#: lib/applicationeditform.php:218
msgid "Source URL"
msgstr "مسار المصدر"
#. TRANS: Form input field instructions.
-#: lib/applicationeditform.php:225
#, fuzzy
msgid "Organization responsible for this application"
msgstr "أيقونة لهذا التطبيق"
+#. TRANS: Form input field label.
+msgid "Organization"
+msgstr "المنظمة"
+
#. TRANS: Form input field instructions.
-#: lib/applicationeditform.php:234
#, fuzzy
msgid "URL for the homepage of the organization"
msgstr "مسار صفحة هذا التطبيق"
#. TRANS: Form input field instructions.
-#: lib/applicationeditform.php:243
msgid "URL to redirect to after authentication"
msgstr ""
#. TRANS: Radio button label for application type
-#: lib/applicationeditform.php:271
msgid "Browser"
msgstr "متصفح"
#. TRANS: Radio button label for application type
-#: lib/applicationeditform.php:288
msgid "Desktop"
msgstr ""
#. TRANS: Form guide.
-#: lib/applicationeditform.php:290
msgid "Type of application, browser or desktop"
msgstr ""
#. TRANS: Radio button label for access type.
-#: lib/applicationeditform.php:314
msgid "Read-only"
msgstr ""
#. TRANS: Radio button label for access type.
-#: lib/applicationeditform.php:334
msgid "Read-write"
msgstr ""
#. TRANS: Form guide.
-#: lib/applicationeditform.php:336
msgid "Default access for this application: read-only, or read-write"
msgstr ""
#. TRANS: Submit button title.
-#: lib/applicationeditform.php:353
msgid "Cancel"
msgstr "ألغِ"
-#: lib/applicationlist.php:247
msgid " by "
msgstr ""
#. TRANS: Application access type
-#: lib/applicationlist.php:260
msgid "read-write"
msgstr ""
#. TRANS: Application access type
-#: lib/applicationlist.php:262
msgid "read-only"
msgstr ""
#. TRANS: Used in application list. %1$s is a modified date, %2$s is access type ("read-write" or "read-only")
-#: lib/applicationlist.php:268
#, php-format
msgid "Approved %1$s - \"%2$s\" access."
msgstr ""
#. TRANS: Access token in the application list.
#. TRANS: %s are the first 7 characters of the access token.
-#: lib/applicationlist.php:282
#, php-format
msgid "Access token starting with: %s"
msgstr ""
#. TRANS: Button label
-#: lib/applicationlist.php:298
msgctxt "BUTTON"
msgid "Revoke"
msgstr "أزل"
-#: lib/atom10feed.php:113
msgid "Author element must contain a name element."
msgstr ""
#. TRANS: Server exception thrown when using the method setActivitySubject() in the class Atom10Feed.
-#: lib/atom10feed.php:160
#, fuzzy
msgid "Do not use this method!"
msgstr "لا تحذف هذا الإشعار"
-#. TRANS: DT element label in attachment list item.
-#: lib/attachmentlist.php:293
-msgid "Author"
-msgstr "المؤلف"
-
-#. TRANS: DT element label in attachment list item.
-#: lib/attachmentlist.php:307
-msgid "Provider"
-msgstr "المزود"
-
#. TRANS: Title.
-#: lib/attachmentnoticesection.php:67
#, fuzzy
msgid "Notices where this attachment appears"
msgstr "وسوم هذا المرفق"
#. TRANS: Title.
-#: lib/attachmenttagcloudsection.php:48
msgid "Tags for this attachment"
msgstr "وسوم هذا المرفق"
#. TRANS: Exception thrown when a password change fails.
-#: lib/authenticationplugin.php:221 lib/authenticationplugin.php:227
#, fuzzy
msgid "Password changing failed."
msgstr "تغيير كلمة السر فشل"
#. TRANS: Exception thrown when a password change attempt fails because it is not allowed.
-#: lib/authenticationplugin.php:238
#, fuzzy
msgid "Password changing is not allowed."
msgstr "تغيير كلمة السر غير مسموح به"
#. TRANS: Title for the form to block a user.
-#: lib/blockform.php:68
msgid "Block"
msgstr "امنع"
#. TRANS: Description of the form to block a user.
-#: lib/blockform.php:79
msgid "Block this user"
msgstr "امنع هذا المستخدم"
#. TRANS: Title for command results.
-#: lib/channel.php:160 lib/channel.php:181
msgid "Command results"
msgstr "نتائج الأمر"
#. TRANS: Title for command results.
-#: lib/channel.php:194
#, fuzzy
msgid "AJAX error"
msgstr "خطأ أجاكس"
#. TRANS: E-mail subject when a command has completed.
-#: lib/channel.php:233 lib/mailhandler.php:143
msgid "Command complete"
msgstr "اكتمل الأمر"
#. TRANS: E-mail subject when a command has failed.
-#: lib/channel.php:244
msgid "Command failed"
msgstr "فشل الأمر"
#. TRANS: Command exception text shown when a notice ID is requested that does not exist.
-#: lib/command.php:82 lib/command.php:106
#, fuzzy
msgid "Notice with that id does not exist."
msgstr "لا ملف بهذه الهوية."
#. TRANS: Command exception text shown when a last user notice is requested and it does not exist.
#. TRANS: Error text shown when a last user notice is requested and it does not exist.
-#: lib/command.php:99 lib/command.php:630
#, fuzzy
msgid "User has no last notice."
msgstr "ليس للمستخدم إشعار أخير"
#. TRANS: Message given requesting a profile for a non-existing user.
#. TRANS: %s is the nickname of the user for which the profile could not be found.
-#: lib/command.php:128
#, fuzzy, php-format
msgid "Could not find a user with nickname %s."
msgstr "تعذّر إيجاد المستخدم الهدف."
#. TRANS: Message given getting a non-existing user.
#. TRANS: %s is the nickname of the user that could not be found.
-#: lib/command.php:148
#, php-format
msgid "Could not find a local user with nickname %s."
msgstr ""
#. TRANS: Error text shown when an unimplemented command is given.
-#: lib/command.php:183
#, fuzzy
msgid "Sorry, this command is not yet implemented."
msgstr "الأمر لم يُجهزّ بعد."
#. TRANS: Command exception text shown when a user tries to nudge themselves.
-#: lib/command.php:229
msgid "It does not make a lot of sense to nudge yourself!"
msgstr ""
#. TRANS: Message given having nudged another user.
#. TRANS: %s is the nickname of the user that was nudged.
-#: lib/command.php:238
#, fuzzy, php-format
msgid "Nudge sent to %s."
msgstr "أرسل التنبيه"
@@ -7943,7 +6505,6 @@ msgstr "أرسل التنبيه"
#. TRANS: %1$s is the number of other user the user is subscribed to.
#. TRANS: %2$s is the number of users that are subscribed to the user.
#. TRANS: %3$s is the number of notices the user has sent.
-#: lib/command.php:268
#, php-format
msgid ""
"Subscriptions: %1$s\n"
@@ -7954,36 +6515,36 @@ msgstr ""
"المشتركون: %2$s\n"
"الإشعارات: %3$s"
+#. TRANS: Error message text shown when a favorite could not be set because it has already been favorited.
+#, fuzzy
+msgid "Could not create favorite: already favorited."
+msgstr "تعذّر إنشاء مفضلة."
+
#. TRANS: Text shown when a notice has been marked as favourite successfully.
-#: lib/command.php:312
#, fuzzy
msgid "Notice marked as fave."
msgstr "هذا الإشعار مفضلة مسبقًا!"
#. TRANS: Message given having added a user to a group.
#. TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
-#: lib/command.php:357
#, php-format
msgid "%1$s joined group %2$s."
msgstr ""
#. TRANS: Message given having removed a user from a group.
#. TRANS: %1$s is the nickname of the user, %2$s is the nickname of the group.
-#: lib/command.php:405
#, php-format
msgid "%1$s left group %2$s."
msgstr ""
#. TRANS: Whois output.
#. TRANS: %1$s nickname of the queried user, %2$s is their profile URL.
-#: lib/command.php:426
#, fuzzy, php-format
msgctxt "WHOIS"
msgid "%1$s (%2$s)"
msgstr "%1$s (%2$s)"
#. TRANS: Whois output. %s is the full name of the queried user.
-#: lib/command.php:430
#, php-format
msgid "Fullname: %s"
msgstr "الاسم الكامل: %s"
@@ -7991,7 +6552,6 @@ msgstr "الاسم الكامل: %s"
#. TRANS: Whois output. %s is the location of the queried user.
#. TRANS: Profile info line in new-subscriber notification e-mail.
#. TRANS: %s is a location.
-#: lib/command.php:434 lib/mail.php:270
#, php-format
msgid "Location: %s"
msgstr "الموقع: %s"
@@ -7999,20 +6559,17 @@ msgstr "الموقع: %s"
#. TRANS: Whois output. %s is the homepage of the queried user.
#. TRANS: Profile info line in new-subscriber notification e-mail.
#. TRANS: %s is a homepage.
-#: lib/command.php:438 lib/mail.php:274
#, php-format
msgid "Homepage: %s"
msgstr "الصفحة الرئيسية: %s"
#. TRANS: Whois output. %s is the bio information of the queried user.
-#: lib/command.php:442
#, php-format
msgid "About: %s"
msgstr "عن: %s"
#. TRANS: Command exception text shown when trying to send a direct message to a remote user (a user not registered at the current server).
#. TRANS: %s is a remote profile.
-#: lib/command.php:471
#, php-format
msgid ""
"%s is a remote profile; you can only send direct messages to users on the "
@@ -8021,7 +6578,6 @@ msgstr ""
#. TRANS: Message given if content is too long. %1$sd is used for plural.
#. TRANS: %1$d is the maximum number of characters, %2$d is the number of submitted characters.
-#: lib/command.php:488
#, fuzzy, php-format
msgid "Message too long - maximum is %1$d character, you sent %2$d."
msgid_plural "Message too long - maximum is %1$d characters, you sent %2$d."
@@ -8032,27 +6588,27 @@ msgstr[3] "هذه طويلة جدًا. أطول حجم للإشعار %d حرف
msgstr[4] "هذه طويلة جدًا. أطول حجم للإشعار %d حرفًا."
msgstr[5] "هذه طويلة جدًا. أطول حجم للإشعار %d حرفًا."
+#. TRANS: Error text shown when trying to send a direct message to a user without a mutual subscription (each user must be subscribed to the other).
+msgid "You can't send a message to this user."
+msgstr "لا يمكنك إرسال رسائل إلى هذا المستخدم."
+
#. TRANS: Error text shown sending a direct message fails with an unknown reason.
-#: lib/command.php:516
#, fuzzy
msgid "Error sending direct message."
msgstr "أنت ممنوع من إرسال رسائل مباشرة."
#. TRANS: Message given having repeated a notice from another user.
#. TRANS: %s is the name of the user for which the notice was repeated.
-#: lib/command.php:553
#, fuzzy, php-format
msgid "Notice from %s repeated."
msgstr "الإشعار من %s مكرر"
#. TRANS: Error text shown when repeating a notice fails with an unknown reason.
-#: lib/command.php:556
msgid "Error repeating notice."
msgstr "خطأ تكرار الإشعار."
#. TRANS: Message given if content of a notice for a reply is too long. %1$d is used for plural.
#. TRANS: %1$d is the maximum number of characters, %2$d is the number of submitted characters.
-#: lib/command.php:591
#, fuzzy, php-format
msgid "Notice too long - maximum is %1$d character, you sent %2$d."
msgid_plural "Notice too long - maximum is %1$d characters, you sent %2$d."
@@ -8065,100 +6621,83 @@ msgstr[5] "هذه طويلة جدًا. أطول حجم للإشعار %d حرف
#. TRANS: Text shown having sent a reply to a notice successfully.
#. TRANS: %s is the nickname of the user of the notice the reply was sent to.
-#: lib/command.php:604
#, fuzzy, php-format
msgid "Reply to %s sent."
msgstr "رُد على رسالة %s"
#. TRANS: Error text shown when a reply to a notice fails with an unknown reason.
-#: lib/command.php:607
msgid "Error saving notice."
msgstr "خطأ أثناء حفظ الإشعار."
#. TRANS: Error text shown when no username was provided when issuing a subscribe command.
-#: lib/command.php:654
msgid "Specify the name of the user to subscribe to."
msgstr ""
#. TRANS: Command exception text shown when trying to subscribe to an OMB profile using the subscribe command.
-#: lib/command.php:663
msgid "Can't subscribe to OMB profiles by command."
msgstr ""
#. TRANS: Text shown after having subscribed to another user successfully.
#. TRANS: %s is the name of the user the subscription was requested for.
-#: lib/command.php:671
#, php-format
msgid "Subscribed to %s."
msgstr ""
#. TRANS: Error text shown when no username was provided when issuing an unsubscribe command.
#. TRANS: Error text shown when no username was provided when issuing the command.
-#: lib/command.php:692 lib/command.php:803
msgid "Specify the name of the user to unsubscribe from."
msgstr ""
#. TRANS: Text shown after having unsubscribed from another user successfully.
#. TRANS: %s is the name of the user the unsubscription was requested for.
-#: lib/command.php:703
#, php-format
msgid "Unsubscribed from %s."
msgstr ""
#. TRANS: Error text shown when issuing the command "off" with a setting which has not yet been implemented.
#. TRANS: Error text shown when issuing the command "on" with a setting which has not yet been implemented.
-#: lib/command.php:723 lib/command.php:749
msgid "Command not yet implemented."
msgstr "الأمر لم يُجهزّ بعد."
#. TRANS: Text shown when issuing the command "off" successfully.
-#: lib/command.php:727
msgid "Notification off."
msgstr "الإشعار مُطفأ."
#. TRANS: Error text shown when the command "off" fails for an unknown reason.
-#: lib/command.php:730
msgid "Can't turn off notification."
msgstr "تعذّر إطفاء الإشعارات."
#. TRANS: Text shown when issuing the command "on" successfully.
-#: lib/command.php:753
msgid "Notification on."
msgstr "الإشعار يعمل."
#. TRANS: Error text shown when the command "on" fails for an unknown reason.
-#: lib/command.php:756
msgid "Can't turn on notification."
msgstr "تعذّر تشغيل الإشعار."
#. TRANS: Error text shown when issuing the login command while login is disabled.
-#: lib/command.php:770
msgid "Login command is disabled."
msgstr ""
#. TRANS: Text shown after issuing the login command successfully.
#. TRANS: %s is a logon link..
-#: lib/command.php:783
#, php-format
msgid "This link is useable only once and is valid for only 2 minutes: %s."
msgstr ""
#. TRANS: Text shown after issuing the lose command successfully (stop another user from following the current user).
#. TRANS: %s is the name of the user the unsubscription was requested for.
-#: lib/command.php:812
#, php-format
msgid "Unsubscribed %s."
msgstr ""
#. TRANS: Text shown after requesting other users a user is subscribed to without having any subscriptions.
-#: lib/command.php:830
msgid "You are not subscribed to anyone."
msgstr "لست مُشتركًا بأي أحد."
#. TRANS: Text shown after requesting other users a user is subscribed to.
#. TRANS: This message supports plural forms. This message is followed by a
#. TRANS: hard coded space and a comma separated list of subscribed users.
-#: lib/command.php:835
msgid "You are subscribed to this person:"
msgid_plural "You are subscribed to these people:"
msgstr[0] "لست مشتركًا بأحد."
@@ -8170,14 +6709,12 @@ msgstr[5] ""
#. TRANS: Text shown after requesting other users that are subscribed to a user
#. TRANS: (followers) without having any subscribers.
-#: lib/command.php:857
msgid "No one is subscribed to you."
msgstr "لا أحد مشترك بك."
#. TRANS: Text shown after requesting other users that are subscribed to a user (followers).
#. TRANS: This message supports plural forms. This message is followed by a
#. TRANS: hard coded space and a comma separated list of subscribing users.
-#: lib/command.php:862
msgid "This person is subscribed to you:"
msgid_plural "These people are subscribed to you:"
msgstr[0] "لا أحد مشترك بك."
@@ -8189,14 +6726,12 @@ msgstr[5] ""
#. TRANS: Text shown after requesting groups a user is subscribed to without having
#. TRANS: any group subscriptions.
-#: lib/command.php:884
msgid "You are not a member of any groups."
msgstr "لست عضوًا في أي مجموعة."
#. TRANS: Text shown after requesting groups a user is subscribed to.
#. TRANS: This message supports plural forms. This message is followed by a
#. TRANS: hard coded space and a comma separated list of subscribed groups.
-#: lib/command.php:889
msgid "You are a member of this group:"
msgid_plural "You are a member of these groups:"
msgstr[0] "لست عضوًا في أي مجموعة."
@@ -8206,232 +6741,268 @@ msgstr[3] "أنت عضو في هذه المجموعات:"
msgstr[4] ""
msgstr[5] ""
-#. TRANS: Help text for commands. Do not translate the command names themselves; they are fixed strings.
-#: lib/command.php:904
-msgid ""
-"Commands:\n"
-"on - turn on notifications\n"
-"off - turn off notifications\n"
-"help - show this help\n"
-"follow - subscribe to user\n"
-"groups - lists the groups you have joined\n"
-"subscriptions - list the people you follow\n"
-"subscribers - list the people that follow you\n"
-"leave - unsubscribe from user\n"
-"d - direct message to user\n"
-"get - get last notice from user\n"
-"whois - get profile info on user\n"
-"lose - force user to stop following you\n"
-"fav - add user's last notice as a 'fave'\n"
-"fav # - add notice with the given id as a 'fave'\n"
-"repeat # - repeat a notice with a given id\n"
-"repeat - repeat the last notice from user\n"
-"reply # - reply to notice with a given id\n"
-"reply - reply to the last notice from user\n"
-"join - join group\n"
-"login - Get a link to login to the web interface\n"
-"drop - leave group\n"
-"stats - get your stats\n"
-"stop - same as 'off'\n"
-"quit - same as 'off'\n"
-"sub - same as 'follow'\n"
-"unsub - same as 'leave'\n"
-"last - same as 'get'\n"
-"on