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.
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.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
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.
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!).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?
I would certainly join this project given it can do what I need or if I can quickly add what I need.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.
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).What do you think? Whats your background and main experience with PHP? Do you use it in your job?
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>
