ChristophDorn schrieb:
soon. My main focus was re-usable low-level components that any application
can benefit from. One major requirement was that you can use these
components within any other framework without conflict. I am hoping that the
components will start to establish some standards for PHP like there is for
Java so we can start interchanging components between different php
projects/frameworks with a standard interface and no conflicts.
That's a sound concern. I am definitely interested in seeing your Framework.
I think the goal for us should be to develop libraries that can drive a
qooxdoo interface (create qooxdoo javascript with php and load data into
qooxdoo) and can call application logic triggered by qooxdoo events. The
Yes, but again I think that using XML to describe the widgets should be the aim. This would also mean that you can exchange GUI templates with other backends implemented in other languages. I cannot sing the praise enough of TAL which is used in the Zope server and has implementations in other languages. It enables me to build dynamic widgets on the server side which are very transparent.

See, for example, the xml code for a chat system I implemented in my application (chatWindow.xml). It continously requests data from a script (chatWindow.inc.php). Or see the macro that builds a GUI from data (macros.tal) which is used in a user management GUI (user_management.xml).

I don't find it so appealing to recreate the qooxdoo javascript API on the serverside and having to instantiate classes, add children widgets, add the object to the client document, etc. It's all very informationally redundant and unnessessarily verbose. The XML syntax just frees you from all of that, and also will be much easier to maintain when the javascript API changes.

libraries should be self-contained so anyone can incorporate them into any
project like horde or mambo. This way we can have many people use them. The
libraries should be structured in a way where they will also easily fit into
a more comprehensive toolset (second phase) to facilitate all standard
taskes when building comprehensive pure qooxdoo applications like you listed
below (I think initially people will use qooxdoo to enhance their apps and
may not necessarily make it their core platform. To enable this it will be
important to only include required qooxdoo components into a javascript file
so it loads faster and enables developer to offer a more powerful tool for a
feature if the user's browser supports it).
That is an excellent idea.
Its hard for people to change frameworks if they already like one that does
what they need. Lets give them the tools and libraries to drive qooxdoo from
their preferred framework?
Yes you are right. A framework should be unobstrusive. On the other hand, if everything comes out-of-the-box and allows you to just throw in some data describing the widgets and some code delivering the data in raw form (without the need to think about transport, validation, security etc.) - this might be come very handy for developers (at least for me, I would love to have it!).
The framework I have been developing is very comprehensive and runs several
commercial websites. I think it would be a perfect fit to also drive qooxdoo
from PHP. Short from just dumping the framework code on the net (and not
having the documentation and time to support) I have been thinking about how
to best release it so it will be valuable and gain traction fast. The key is
small components that already fit and work with many other frameworks which
over time will grow into a comprehensive toolset which can be used by itself
which would then represent the entire framework. Make different people
responsible for different components so we can keep up with documentation,
examples and support.
I would certainly join this project given it can do what I need or if I can quickly add what I need.
What do you think? Whats your background and main experience with PHP? Do
you use it in your job?
Well, I am an amateur (being a political scientist). I use PHP (4.x) because that's the first thing I learned (I have no time to learn anything else - javascript being the exception).

Christian
<?xml version="1.0" encoding="utf-8"?>
<qx:widgets xmlns:qx="http://qooxdoo.sourceforge.net";>

    <tal:block define="widget   string:bg_chatWindow"/>
    <qx:window  id="bg_chatWindow"
                left="100" top="100"
                width="600" height="400"
		        allowMinimize="false" showMinimize="false"
                caption="Message Window">
    
    	<qx:atom 
		id="bg_chatWindow_display"
		left="0" bottom="30" width="70%" height="90%"
		text="loading..."
		verticalBlockAlign="top"
		backgroundColor="white"
		overflow="auto"
		border="QxBorder.presets.inset"/>
    
    	<qx:atom 
		id="bg_chatWindow_users_online"
		right="0" bottom="30" width="28%" height="90%"
		text="loading..."
		verticalBlockAlign="top"
		backgroundColor="white"
		overflow="auto"
		border="QxBorder.presets.inset"/>
    
        <qx:textField 
            id="bg_chatWindow_textField"
            bottom="0" left="0" right="65" height="22"
            border="QxBorder.presets.inset">
            <qx:eventListener type='keypress' args='event'>
            if ( event.getKeyCode() == 13 ){
                ${widget}.getMessages(true);
            }
            </qx:eventListener>  
        </qx:textField>

        <qx:button
            bottom="0" right="0" height="22" width="60"
            icon="icons/16/ok.png" 
            iconPosition="left"
            text="Send">
		<qx:eventListener type='click' >
            ${widget}.getMessages(true);
			${widget}_textField.setFocused(true);
		</qx:eventListener>  
	</qx:button>
		
	<qx:script>
                        
            // current widget
            ${widget}.close();

            // getters and setters
            ${widget}.getTextField = function()
            {
                return bg_chatWindow_textField;
            };
            
            ${widget}.getInput = function()
            {
                return this.getTextField().getElement().value;
            };

            ${widget}.setInput = function(text)
            {
                this.getTextField().getElement().value = text;
            }; 
	 
             ${widget}.display = function ( text )
             {
                bg_chatWindow_display.setText ( text );
                bg_chatWindow_display.setScrollTop ( 100000 );
             }
        </qx:script> 
        <qx:script>
            
            ${widget}._msgRead = [];
            
            // data transfer
            ${widget}.getMessages = function ( send )
            {
                user = bg.getUserName();
                if ( ! user ) return;

                var textInput = ""; 
                
                if ( send ) {
                    textInput = this.getInput();
                    this.setInput("");
                } 
             
                dojo.io.bind({
                    url: bg.url + "/data/chatWindow",
                    content: {  msgRead : bg_chatWindow._msgRead[user] || 0,
                                textInput : textInput,
                                sendText : bg_chatWindow.getVisible(),
                                user: user},
                    method : "get",            
                    handler: function(type, response, evt){
                        bg.endRequest();
                        try{
                            data = eval ( response );
                        } catch (e) {
                            alert( response );
                            return false;
                        }
						
						// forced logout?
						
						if ( data.action == "logout" ) {
							bg.logout();
							return;
						}
						
                        if ( data.users ) {
                            ${widget}_users_online.setText ( data.users );
                        }
                        
                        // new text for message window?
                        if ( data.text ) {
                            ${widget}.display ( data.text );
                        }
                        
                        // should window be forced open?
                        if ( data.openWindow ) bg_chatWindow.open();
                        
                        // actions if window is open or closed
                        if ( ${widget}.getVisible() ) {
                            
                            // mark all messages as read
                            ${widget}._msgRead[user] = data.msgId; 
                            ${widget}._unreadMsg = 0;
                            
                        } else {
                            
                            // display alert, but only once
                            if ( data.unread ) {
                                if ( ${widget}._unreadMsg != data.unread ){
                                        // show message only once
                                        bg.alert (  data.unread  + " new message(s)", 5, "email" );
                                        ${widget}._unreadMsg = data.unread;
                                }
                            }
                        } 
                         
                        ${widget}.setText ( data.unread );
                    },
                    mimetype: "text/plain"
                });                                
            };
            window.setTimeout(function(){${widget}_textField.setFocused(true);},1000);
        </qx:script> 
    </qx:window>
</qx:widgets>
request['textInput'] ); $msgRead = (int) $this->request['msgRead']; $user = either ( $this->request['user'], $this->getUserName() ); $sendText = $this->request['sendText']; $userName = $this->getUserFullName(); $config = &$this->getConfig(); // logout when user has been logged out or session expired if ( ( ! $config->get( "users.$user.online" ) or $GLOBALS['ZOPHE_LOG_OUT']==true or $this->getUserRole() == "anonymous" ) and $this->getUserRole() != "superuser") { return array ( action => "logout" ); } // load message array if ( ! is_array ( $messages = @file ( $message_log ) ) ) $messages = array () ; // add message or execute command if ( $textInput ){ if ( $this->checkUserRole ( "manager" ) ) { switch ( $textInput ) { case "RESET": $messages = array(); $msgRead = 0; $config->delete ("chat"); $this->setSessionVar ( "chat_lastMsgId", 0 ); $textInput = "Resetting Message Log"; break; } } // write message $messages [] = "
$userName" . " (" . date("d.m.Y H:i") . "):
" . $textInput; $msgfile = str_replace ("\n\n", "\n", trim ( implode ("\n", $messages ) ) ); file_put_contents ( $message_log, $msgfile ) ; } // find out last message read in previous session if ( ! is_numeric ( $lastMsgId = $this->getSessionVar ("chat_lastMsgId") ) ) { $lastMsgId = either ( $config->get ("chat.$user.currentMsgId") , 0 ); $this->setSessionVar ( "chat_lastMsgId", $lastMsgId ); } // detemine number of unread messages $msgId = count ( $messages); $lastRead = max ( $lastMsgId, $msgRead ); $numUnreadMsg = ( $msgId - $lastRead ) ; // if message log has been reset, reset message count in client if ( $numUnreadMsg < 0 ) { $numUnreadMsg = $msgId; $lastRead = $msgId; } $config->set ( "chat.$user.currentMsgId", $msgId ); // display only messages since last logout and only if requested and if there has been a change $text = stripslashes ( implode ( "\n", array_slice ( $messages, $lastMsgId ) ) ); $text = nl2br ( wordwrap ( str_replace ("\n\n", "\n", $text) , 100 ) ) ; $openWindow = strstr ( $text, "WICHTIG:" ) ? true : false ; if ( $msgRead > 0 and ( $numUnreadMsg == 0 or $sendText=="false" ) ) { // don't send anything $text = ""; } // show users online $users_online =""; if ( $sendText == "true" ){ $users_online = "Users online:
"; foreach ( $config->get( "users" ) as $user ) if ( $user['online'] ) $users_online .= "
" . $user['name']; if ( $users_online == $this->getSessionVar ( "users_online" ) ) { $users_online = ""; } else { $this->setSessionVar ( "users_online", $users_online ); } } return array ( msgId => $msgId, unread => $numUnreadMsg, text => $text, openWindow => $openWindow, lastMsgId => $lastMsgId, msgRead => $msgRead, users => $users_online, action => $action ); ?>
<?xml version="1.0" encoding="utf-8"?>
<metal:block xmlns:qx="http://qooxdoo.sourceforge.net";
		  xmlns:tal="http:/phptal.sourceforge.net"
		  xmlns:metal="http://qooxdoo.sourceforge.net";>
    <metal:block define-macro="config_menu">
            <qx:fieldSet 
            left="0" right="0" top="30" bottom="0" >
            
            <tal:block repeat="name fieldnames">
                <tal:block define=" field 	    fields/${name};
                                    type    	field/type;
                                    label       php: __(name);"/>
                <qx:widget 
                    id="${name}_container"
                    left="0" right="0"
                    top="${top}" height="auto" >
                    <qx:atom 
                        left="0" width="100"
                        text="${label}"/>
                    <qx:textField 
                        tal:condition="php: type eq 'string'"
                        id="bg_${name}"
                        left="105" right="0">
                        <qx:eventListener type="changeText">
                            ${widget}.changeValue('${name}', event.getNewValue());
                        </qx:eventListener>
                        <qx:eventListener type="setValue">
                            this.setText ( event.getNewValue() );
                        </qx:eventListener>
                    </qx:textField>
                    <qx:passwordField 
                        tal:condition="php: type eq 'password'"
                        id="bg_${name}"
                        left="105" right="0">
                        <qx:eventListener type="changeText">
                            ${widget}.changeValue('${name}', event.getNewValue());
                        </qx:eventListener>
                        <qx:eventListener type="focus">
                            this.selectAll();
                        </qx:eventListener>
                    </qx:passwordField>                        
                    <qx:textArea
                        tal:condition="php: type eq 'lines'"
                        id="bg_${name}"
                        left="105" right="0" 
                        tal:attributes="height php: ${field/lines} * 20">			
                         <tal:block define="top php: top + ((${field/lines}-1) * 20)"/>
                         <qx:eventListener type="changeText">
                            ${widget}.changeValue('${name}', event.getNewValue());
                        </qx:eventListener>
                        <qx:eventListener type="setValue">
                            this.setText ( event.getNewValue() );
                        </qx:eventListener>
                    </qx:textArea>                 
                    <qx:comboBox 
                        tal:condition="php: type eq 'select'"
						tal:attributes="editable php: choose ( ${field/editable} eq 'true', 'true', 'false')"
                        id="bg_${name}"
                        left="105" right="0" top="0">
                        <qx:listItem 
							tal:condition="php: ${field/editable} ne 'true'"
                            tal:attributes="text php: __('[Please select]')" 
                            value=""/>
                        <tal:block define="options php: config.convertValues( ${field/options} );"/>
                        <qx:listItem 
                            tal:condition="php: is_array(options) "
                            tal:repeat="option options"
                            tal:attributes="text    php: choose ( is_array (option), ${option/text},  option ); 
                                            value   php: choose ( is_array (option), ${option/value}, option )"/>
                        <qx:eventListener type="changeSelected">
                            ${widget}.changeValue( '${name}', event.getNewValue().getValue() );
                        </qx:eventListener>
                        <qx:eventListener type="setValue">
                            var value = event.getNewValue();
                            this.setSelected ( value ? this.getList().findValueExact( value ) : this.getList().getFirstChild() );
                        </qx:eventListener>
                    </qx:comboBox>
                    <qx:checkBox
                        tal:condition="php: type eq 'checkbox'"
                        left="105" top="0"
                        id="bg_${name}">
                        <qx:eventListener type="changeChecked">
                            ${widget}.changeValue('${name}', event.getNewValue() );
                        </qx:eventListener>
                        <qx:eventListener type="setValue">
                            this.setChecked ( event.getNewValue() );
                        </qx:eventListener>                        
                    </qx:checkBox>
                    <qx:widget
                        tal:condition="php: type eq 'bitflags'"
                        id="bg_${name}"
                        border="QxBorder.presets.thinInset"
                        left="105" right="0" bottom="0"
                        paddingLeft="2" paddingRight="2"
                        paddingTop="2" paddingBottom="2">
                        <tal:block define="line     php:0; 
                                            flags   php:config.convertValues( ${field/options} );"/>
                        <tal:block repeat="flag flags">
                            <qx:checkBox
                                id="bg_${name}_${flag}"
                                left="0" top="${line}">
                                <qx:eventListener type="changeChecked">
                                    ${widget}.changeBitFlag('${name}', ${repeat/flag/index}, this.getChecked() );
                                </qx:eventListener>
                            </qx:checkBox>
                            <qx:atom 
                                 tal:attributes="text php: __('${name}_${flag}')"
                                left="20" right="0"
                                top="${line}"/>
                            <tal:block define="line php: line + 20; top php: top + 20;"/>
                        </tal:block>
                        <tal:block define="top php: top - 20"/>
                        <qx:eventListener type="setValue">
                            var value = event.getNewValue();
                            var flag, flags = ${widget}._fields[field].options;
                            var bit = 1;
                            while ( flag = flags.shift() ) {
                                var cbn = 'bg_' + field + "_" + flag;
                                var fcb = window[ cbn ];
                                if ( fcb ) {
                                    fcb.setChecked ( value ba bit );
                                }
                                bit*=2;
                            }
                        </qx:eventListener>
                    </qx:widget>
                    <qx:widget
                        tal:condition="php: type eq 'radiobutton'"
                        id="bg_${name}"
                        border="QxBorder.presets.thinInset"
                        left="105" right="0" bottom="0"
                        paddingLeft="2" paddingRight="2"
                        paddingTop="2" paddingBottom="2">
                        <tal:block define=" line     php:0; 
                                            options   php:config.convertValues( ${field/options} );"/>
                        <qx:radioButton
                            repeat="option options"
                            id="bg_${name}_${option}"
                            group="${name}"
                            left="0" top="${line}"
                            tal:attributes="text php: __(option)">
                            <qx:eventListener type="changeChecked">
                                ${widget}.changeValue( '${name}', "${option}" );
                            </qx:eventListener>
                            <tal:block define="line php: line + 20; top php: top + 20;"/>
                        </qx:radioButton>
                        <qx:eventListener type="setValue">
                            window["bg_${name}_" + event.getNewValue()].setChecked(true);
                        </qx:eventListener>                        
                        <tal:block define="top php: top - 20"/>
                    </qx:widget>
                </qx:widget>
                <qx:widget 
                    tal:condition="php: type eq 'buttons'"
                    top="${top}" left="105" right="0" bottom="0">
                    <tal:block define="width    php: floor(100/count(${field/options}));
                                       actions  field/options"/>
                    <qx:button
                        id="bg_${action}"
                        tal:repeat="action actions"
                        tal:attributes="text    php: __(${action});
                                        top     php: 0;
                                        left    php: ( width * ${repeat/action/index} ) . '%';
                                        width   php: ( width - 1 ) . '%' ;"
                        onclick="${widget}.handleAction('${name}','${action}')"/>
                </qx:widget>
				<qx:list 
					id="bg_${name}"
					tal:condition="php: type eq 'list'"
					top="${top}" left="105" right="0" 
					tal:attributes="height php: ${field/rows} * 20">
					<tal:block define="top php: top + ((${field/rows}-1) * 20)"/>
					<tal:block define="options php: config.convertValues( ${field/options} );"/>
					<qx:listItem 
						tal:condition="php: is_array(options) "
						tal:repeat="option options"
						tal:attributes="text    php: choose ( is_array (option), ${option/text},  option ); 
										value   php: choose ( is_array (option), ${option/value}, option )"/>
					<qx:eventListener type="click">
						${widget}.changeValue( '${name}', bg_${name}.getSelectedItem().getValue() );
					</qx:eventListener>
					<qx:eventListener type="setValue">
						var value = event.getNewValue();
						this.setSelected ( value ? this.findValueExact( value ) : this.getFirstChild() );
					</qx:eventListener>
					<qx:eventListener type="setOptions">
						this.setOptions(event);
					</qx:eventListener>
					<qx:script>
						this.moveListItemUp = function () {
							var manager = this.getManager();
							var item 	= manager.getSelectedItem();
						}
						
						this.setOptions = function(e) {
							var options = e.getNewValue();
							this.removeAll();
							for ( var i=0; i lt options.length; i++){
								this.add ( new QxListItem ( options[i].text,null,options[i].value ) );
							}
						}
					</qx:script>
				</qx:list>
				<tal:block define="top php: top + 25"/>
            </tal:block>
        </qx:fieldSet>
    </metal:block>
</metal:block>
<?xml version="1.0" encoding="utf-8"?>
<qx:widgets xmlns:qx="http://qooxdoo.sourceforge.net";>
    
    <tal:block define=" widget      string:bg_user_management; 
                        config      this/getConfig;
                        configBase  php: 'field_definition.users';
                        fieldnames  php: config.get('${configBase}.field_order');
                        fields      php: config.get('${configBase}.fields');
                        top         php: 0;"/>
  
    <qx:window
        id="${widget}"
        left="100"  top="100" 
        allowMinimize="false" showMinimize="false" 
        allowMaximize="false" showMaximize="false" 
        height="auto" width ="400">
        
        <qx:atom left="0" right="0" top="0" height="30"
            tal:attributes="text php: __('manage_user_label')" />
            
        <metal:block use-macro="${this/object_path}/macros.tal/config_menu"/>
        
        <qx:button text="Close" 
            top="5" right="5"  height="22"
            onClick="${widget}.close()" />
  
    </qx:window>
     
    
    <qx:script>
    
        ${widget}.populateMenu = function ( data )
        {
            // turn off server update mechanism
            this._preventUpdate = true;
            
            // populate dialogue
            for ( var i=0; i lt this._fieldnames.length; i++) {
                var field = this._fieldnames[i];
                //if ( field == "user_id" ) continue;
                var target = window['bg_' + field ];
                if ( ! target ) continue;
                var value = data ? ( data[field] || data[field.slice(5)] ) : null;
                target.dispatchEvent( new QxDataEvent ( "setValue", value) );
            }
            
            // turn update back on
            this._preventUpdate = false;
			
			// set default locale if none is selected
			if ( ! bg_user_locale.getSelected().getValue() ) {
				bg_user_locale.setSelected ( bg_user_locale.getList().findValueExact ( bg.locale ) );
			}
        };
    </qx:script>	    
    
    <tal:block define="passwords_no_match   php: __('Passwords do not match');
                       update_password      php: __('Do you want to update your password to:')"/>
    <qx:script>	
    
        /**
         * changes a single configuration value on the server
         *
         **/ 
        ${widget}.changeValue = function ( field, value )
        {
            if ( this._preventUpdate ) return; // don't save values just retrieved!
            _this = this;
            if ( field == "user_id" ) {
                if ( ! value ) return;
                this._user_id = value;
                bg.getConfigData ( "users." + value, function(data){
                    if ( typeof data != "object" )
                        data = {};
                    data.user_id = _this._user_id;
                    _this.populateMenu ( data );
                });
                return;
            } else if ( field == "user_password_repeat" ) {
                var password = bg_user_password.getText();
                if ( ! password ) return;
                if ( password la password != value ){
                    alert ( "${passwords_no_match}");
                    bg_user_password.setText("");
                    bg_user_password_repeat.setText("");
                    return;
                } else {
                    field = "user_password";
                    value = hex_md5 ( password );
                    if ( ! confirm ( "${update_password}" + password ) )
                        return;
                }
            } else if ( field == "user_password" ) {
                return;
            } else if ( field == "user_locale" la this._user_id == bg.getUserName() ) {
				bg.setLocale ( value );
            }
            
            // save to server
            if ( this._user_id ) {
                //alert ( ["users." + this._user_id + "." + field.slice(5), value] );
                bg.setConfigData ( "users." + this._user_id + "." + field.slice(5), value );
            } 
        };
     
        /**
         * changes a bitflag in a configuration value on the server
         *
         **/
         
        ${widget}.changeBitFlag = function (field, bit, value)
        {
            bit = Math.pow (2,bit);
            var newValue, oldValue = bg.foldertree.getCurrentFolderObj().getData()[field];
            //alert ( "Old value " + oldValue + ", setting bit " + bit + " to " + value );
            if ( value ) {
                newValue = oldValue | bit;
            } else {
                newValue = oldValue ^ bit;
            }
            this.changeValue ( field, newValue );
        };
    </qx:script>
    <tal:block define=" new_user_id     php: __('Enter id of new user');
                        user_id_exists  php: __('User id exists.');
                        really_delete_user php: __('Do you really want to delete user')"/>
    <qx:script>
        ${widget}.handleAction = function ( buttonGroup, action )
        {
            switch ( action ) {
            
                case "add_user":
                    var newUser = prompt ( "${new_user_id}" );
                    if ( ! newUser ) return;
                    if ( bg_user_id.getList().findValue( newUser ) ) {
                        alert ("${user_id_exists}");
                        return;
                    } 
                    bg_user_id.getList().add ( new QxListItem ( newUser, false, newUser ) );
                    bg_user_id.dispatchEvent ( new QxDataEvent ("setValue", newUser ) );
                    break;
            
                case "delete_user":
                    var userId  = this._user_id;
                    var _this   = this;
                    if ( ! confirm ( "${really_delete_user} " + userId ) ) return;
                    bg.deleteConfigData ( "users." + userId, function(){
                        var l = bg_user_id.getList()
                        l.remove( l.findValue( userId ) );
                        bg_user_id.setSelected ( l.getFirstChild() );
                    });
                    break;
            }
        }
    </qx:script>

    <qx:script>	
    
        // initialization

        ${widget}._fieldnames = ['<tal:block replace="php: implode('\',\'', fieldnames )"/>'];

		dojo.event.connect ( ${widget}, "open", function() {
			${widget}.changeValue("user_id", bg.getUserName() );
            bg_add_user.setEnabled ( bg.checkUserRole("superuser") );
            bg_delete_user.setEnabled ( bg.checkUserRole("sueruser") );
			bg_user_id.setEnabled( bg.checkUserRole('superuser') );
            bg_user_role.setEnabled( bg.checkUserRole('superuser') );
		});
		
        bg.getConfigData ( "${configBase}.fields", function(data){
            ${widget}._fields = data;
            ${widget}.open();
        });

    </qx:script>	   
    
</qx:widgets>

Reply via email to