From: Neil B. <n3...@us...> - 2006-01-29 03:29:08
|
Update of /cvsroot/jedit/plugins/TypoScript/typoscript In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv18159/TypoScript/typoscript Added Files: RemoteCallWorker.java T3PageNode.java T3Site.java T3TreeRenderer.java TypoScriptPlugin.java TypoScriptPluginOptions.java TypoScriptSiteBrowser.java TypoScriptVFS.java Log Message: Initial Commit, version 0.1.0 A plugin using XML-RPC to provide a VFS for editing TypoScript configuration "templates" within the TYPO3 content management system (typo3.org) --- NEW FILE: T3PageNode.java --- /*************************************************************** * Copyright notice * * (c) 2005,2006 Neil Bertram (ne...@ta...) * All rights reserved * * This plugin is part of the Typo3 project. The Typo3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * A copy is found in the textfile GPL.txt * * * This plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the source! ***************************************************************/ /** * $Id: T3PageNode.java,v 1.1 2006/01/29 03:28:55 n31l Exp $ * * This object represents a page on a remote TYPO3 site, and is also * the source of data for the JTree in TypoScriptSiteBrowser. * * @author Neil Bertram <ne...@ta...> */ package typoscript; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import org.apache.xmlrpc.XmlRpcException; import org.gjt.sp.jedit.Buffer; import org.gjt.sp.jedit.View; import org.gjt.sp.jedit.jEdit; /** * Represents a page node in the sitebrowser tree, but also contains the data linking a page * to its templates */ public class T3PageNode extends DefaultMutableTreeNode { private static final long serialVersionUID = 1L; private String title; private int uid; private int type; private boolean hasTemplate; private int setupLines; private int constantsLines; private int templateUID; private boolean loaded; private T3Site belongsTo; protected T3PageNodePopupMenu popupMenu; /** * This constructor is used for generating site root nodes which will only load data * upon being opened * @param site parent site */ public T3PageNode(T3Site site) { super(); parent = null; title = site.getName(); type = -1; // root node uid = 0; hasTemplate = false; templateUID = -1; // Add a "Loading" node inside it, removed once data arrives add(new DefaultMutableTreeNode()); loaded = false; belongsTo = site; popupMenu = null; // doesn't have one } /** * The constructor for a normal page node, constructed from the * XML-RPC result * * @param data xmlrpc data for this node * @param site parent site */ public T3PageNode(Hashtable data, T3Site site) { super(); title = (String)data.get("title"); uid = ((Integer)data.get("uid")).intValue(); type = ((Integer)data.get("doktype")).intValue(); hasTemplate = ((Boolean)data.get("hastemplate")).booleanValue(); if (!hasTemplate) { setupLines = -1; constantsLines = -1; templateUID = -1; } else { Hashtable templateInfo = (Hashtable)data.get("templateinfo"); setupLines = ((Integer)templateInfo.get("setuplines")).intValue(); constantsLines = ((Integer)templateInfo.get("constantslines")).intValue(); templateUID = ((Integer)templateInfo.get("templateuid")).intValue(); } // Look for children Vector ourChildren = (Vector)data.get("children"); Iterator it = ourChildren.iterator(); while (it.hasNext()) { Hashtable childNode = (Hashtable)it.next(); this.add(new T3PageNode(childNode, site)); } loaded = true; belongsTo = site; if (hasTemplate) { popupMenu = new T3PageNodePopupMenu(); } else { popupMenu = null; } } /** * Only ever called on a site root node, this will convert the node * into the root of a real site tree by fetching the data over XML-RPC * * @param treeModel the model representing the tree to populate */ public void loadTree(DefaultTreeModel treeModel) { new TreeFetchWorker(treeModel, belongsTo, this).start(); loaded = true; } public String toString() { return title; } public int getConstantsLines() { return constantsLines; } public boolean hasTemplate() { return hasTemplate; } public int getSetupLines() { return setupLines; } public int getTemplateUID() { return templateUID; } public String getTitle() { return title; } public int getType() { return type; } public int getUid() { return uid; } public boolean isLoaded() { return loaded; } public T3Site getSite() { return belongsTo; } /** * Opens a page into a jedit buffer * * @param tree the sitetree that the selection was made in (to calculate path) * @param type either constants or setup * @param view the view to load the buffer into */ public void open(JTree tree, String type, View view) { TreePath selectPath = tree.getSelectionPath(); T3PageNode selected = (T3PageNode)selectPath.getLastPathComponent(); String path = "typoscript:" + selected.getSite().getName() + "::"; Object[] pathComponents = selectPath.getPath(); for (int i = 2; i < pathComponents.length - 1; i++) { T3PageNode node = (T3PageNode)pathComponents[i]; path += node.getTitle() + "/"; } T3PageNode node = (T3PageNode)pathComponents[pathComponents.length - 1]; path += node.getTitle() + "." + node.getUid() + "." + node.getTemplateUID() + "." + type + ".ts"; jEdit.openFile(view, path); } } /** * This class processes the arrival of data in a new thread so as not to hold up the interface */ class TreeFetchWorker extends Thread { DefaultTreeModel treeModel; T3Site curSite; T3PageNode curNode; public TreeFetchWorker(DefaultTreeModel model, T3Site site, T3PageNode node) { treeModel = model; curSite = site; curNode = node; } public void run() { curNode.removeAllChildren(); curNode.add(new DefaultMutableTreeNode("Retrieving site tree...")); treeModel.nodeStructureChanged(curNode); Hashtable data = null; try { data = curSite.getWorker().getPageTree(); } catch (Exception e) { curNode.removeAllChildren(); if (e instanceof XmlRpcException) { XmlRpcException xe = (XmlRpcException) e; if (xe.code == RemoteCallWorker.JEDITVFS_ERROR_AUTHFAIL) { curNode.add(new DefaultMutableTreeNode("Authentication failed - check settings")); } else { curNode.add(new DefaultMutableTreeNode("Unexpected error: " + e.getMessage())); } } else if (e instanceof IOException) { curNode.add(new DefaultMutableTreeNode("I/O error - Server down?")); } treeModel.nodeStructureChanged(curNode); e.printStackTrace(); return; } curNode.removeAllChildren(); // Add all children of the data we loaded (which is the root node of this site) Vector ourChildren = (Vector)data.get("children"); Iterator it = ourChildren.iterator(); while (it.hasNext()) { Hashtable childNode = (Hashtable)it.next(); curNode.add(new T3PageNode(childNode, curSite)); } treeModel.nodeStructureChanged(curNode); } } /** * Represents the popup menu when a template is right clicked * Intended to be JDK1.4 compatible, 1.5 has a new way to attach popup menus in a cleaner manner * * Only ever instantiated on nodes with templates */ class T3PageNodePopupMenu extends JPopupMenu { private JTree siteTree; private View bufferView; public T3PageNodePopupMenu() { JMenuItem mnuOpenConstants = new JMenuItem("Open constants"); mnuOpenConstants.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { T3PageNode selected = (T3PageNode)siteTree.getLastSelectedPathComponent(); selected.open(siteTree, "constants", bufferView); } }); JMenuItem mnuOpenSetup = new JMenuItem("Open setup"); mnuOpenSetup.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { T3PageNode selected = (T3PageNode)siteTree.getLastSelectedPathComponent(); selected.open(siteTree, "setup", bufferView); } }); this.add(mnuOpenSetup); this.add(mnuOpenConstants); } /** * Sets the calling tree and view then invokes the menu display */ public void show(Component invoker, int x, int y, JTree tree, View view) { bufferView = view; siteTree = tree; super.show(invoker, x, y); } } --- NEW FILE: RemoteCallWorker.java --- /*************************************************************** * Copyright notice * * (c) 2005,2006 Neil Bertram (ne...@ta...) * All rights reserved * * This plugin is part of the Typo3 project. The Typo3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * A copy is found in the textfile GPL.txt * * * This plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the source! ***************************************************************/ /** * $Id: RemoteCallWorker.java,v 1.1 2006/01/29 03:28:55 n31l Exp $ * * This handles the communication with the TYPO3 server. * This is accomplished through XML-RPC calls (using Apache's * XML-RPC library) to the jeditvfs plugin on the TYPO3 side. * * @author Neil Bertram <ne...@ta...> */ package typoscript; import java.io.IOException; import java.util.Hashtable; import java.util.Vector; import org.apache.xmlrpc.XmlRpcClient; import org.apache.xmlrpc.XmlRpcException; import org.gjt.sp.util.Log; /** * This class handles the gruntwork of communicating with the TYPO3 plugin over XML-RPC * */ public class RemoteCallWorker { public static final short JEDITVFS_ERROR_AUTHFAIL = 0; public static final short JEDITVFS_ERROR_UNKNOWNMETHOD = 1; public static final short JEDITVFS_ERROR_NOSUCHTEMPLATE = 2; private T3Site site; private XmlRpcClient xmlrpc; public RemoteCallWorker(T3Site parent) { site = parent; xmlrpc = new XmlRpcClient(site.getUrlFull()); xmlrpc.setBasicAuthentication(site.getUsername(), site.getPassword()); } public Hashtable getPageTree() throws XmlRpcException, IOException { Hashtable result = null; result = (Hashtable)remoteCall("vfs.getList", null); return result; } public String getSiteTitle() throws IOException, XmlRpcException { String result = null; result = (String)remoteCall("vfs.getSiteTitle", null); return result; } public String getConstants(Integer templateuid) throws IOException, XmlRpcException { String result = null; Vector params = new Vector(1); params.add(templateuid); result = (String)remoteCall("vfs.getConstants", params); return result; } public String getSetup(Integer templateuid) throws IOException, XmlRpcException { String result = null; Vector params = new Vector(1); params.add(templateuid); result = (String)remoteCall("vfs.getSetup", params); return result; } public void putConstants(Integer templateuid, String constants) throws IOException, XmlRpcException { Vector params = new Vector(3); params.add(templateuid); params.add(constants); Boolean clearCache = new Boolean(site.clearCacheOnSave()); params.add(clearCache); remoteCall("vfs.putConstants", params); } public void putSetup(Integer templateuid, String setup) throws IOException, XmlRpcException { Vector params = new Vector(3); params.add(templateuid); params.add(setup); Boolean clearCache = new Boolean(site.clearCacheOnSave()); params.add(clearCache); remoteCall("vfs.putSetup", params); } private Object remoteCall(String methodName, Vector params) throws IOException, XmlRpcException { Log.log(Log.DEBUG, this, "Sending remote call " + methodName + " params " + params); if (params == null) params = new Vector(0); Object result = xmlrpc.execute(methodName, params); if (result == null) throw new XmlRpcException(-1, "No result - Server plugin missing?"); if (result instanceof XmlRpcException) throw (XmlRpcException)result; // bug in xmlrpc? Log.log(Log.DEBUG, this, "Result: " + result); return result; } } --- NEW FILE: TypoScriptPlugin.java --- /*************************************************************** * Copyright notice * * (c) 2005,2006 Neil Bertram (ne...@ta...) * All rights reserved * * This plugin is part of the Typo3 project. The Typo3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * A copy is found in the textfile GPL.txt * * * This plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the source! ***************************************************************/ /** * $Id: TypoScriptPlugin.java,v 1.1 2006/01/29 03:28:55 n31l Exp $ * * The main plugin class for the TypoScript jEdit plugin * This handles loading and saving of configuration, and * handling of EditBus events that may require us to reload * the site browser dockable. * * @author Neil Bertram <ne...@ta...> */ package typoscript; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.util.Vector; import javax.swing.JOptionPane; import org.gjt.sp.jedit.Buffer; import org.gjt.sp.jedit.EBMessage; import org.gjt.sp.jedit.EBPlugin; import org.gjt.sp.jedit.MiscUtilities; import org.gjt.sp.jedit.Mode; import org.gjt.sp.jedit.View; import org.gjt.sp.jedit.jEdit; import org.gjt.sp.jedit.io.VFS; import org.gjt.sp.jedit.msg.BufferUpdate; import org.gjt.sp.jedit.msg.PropertiesChanged; import org.gjt.sp.util.Log; /** * Main plugin class, handles configuration serialisation to the file in $HOME/typoscriptplugin/sitesconfig.obj * Also checks for EditBus messages that may require us to reload any open site browsers */ public class TypoScriptPlugin extends EBPlugin { public static final String NAME = "typoscript"; public static final String MENU = "typoscript.menu"; public static final String PROPERTY_PREFIX = "plugin.typoscript.TypoScriptPlugin."; public static final String OPTION_PREFIX = "options.typoscript"; protected static TypoScriptPlugin instance; protected static Vector siteConfig; public TypoScriptPlugin() { super(); instance = this; } public void start() { super.start(); loadConfiguration(); if (siteConfig == null) { Log.log(Log.ERROR, this, "Configuration null??"); siteConfig = new Vector(); } } public void loadConfiguration() { // Attempt to load the configuration in from the user's config file String configPath = MiscUtilities.constructPath(jEdit.getSettingsDirectory(), "typoscriptplugin" + File.separatorChar + "sitesconfig.obj"); File configFile = new File(configPath); if (!configFile.exists()) { // Check to see if the "typoscriptplugin" directory exists if (!configFile.getParentFile().exists()) { Log.log(Log.NOTICE, this, "Couldn't find configuration directory, creating directory"); // Create it if (!configFile.getParentFile().mkdir()) { Log.log(Log.ERROR, this, "Couldn't create configuration directory in " + configFile.getParentFile().getPath()); return; } } // By now the directory either already exists, or has been created, create the file Log.log(Log.NOTICE, this, "Creating configuration file"); try { configFile.createNewFile(); } catch (IOException e) { Log.log(Log.ERROR, this, "Failed to create configuration file at " + configPath + ", an IOException occured:" + e.toString()); } // Create an empty configuration vector and commit it to the new configuration file siteConfig = new Vector(); saveConfiguration(); Log.log(Log.NOTICE, this, "Configuration file created"); } else { // File exists, de-serialise it try { FileInputStream fIn = new FileInputStream(configPath); ObjectInputStream objIn = new ObjectInputStream(fIn); siteConfig = (Vector)objIn.readObject(); } catch (FileNotFoundException e) { // This technically can't happen... Well unless you're *very* quick to delete the file e.printStackTrace(); } catch (IOException e) { Log.log(Log.ERROR, this, "An IOException occured while deserialising the configuration:" + e.toString()); } catch (ClassNotFoundException e) { // Can this happen? We know what a vector is... Log.log(Log.ERROR, this, "Deserialise error (classnotfound)"); e.printStackTrace(); } } } public static void saveConfiguration() { String configPath = MiscUtilities.constructPath(jEdit.getSettingsDirectory(), "typoscriptplugin" + File.separatorChar + "sitesconfig.obj"); try { FileOutputStream fOut = new FileOutputStream(configPath); ObjectOutputStream objOut = new ObjectOutputStream(fOut); objOut.writeObject(siteConfig); } catch (FileNotFoundException e) { Log.log(Log.ERROR, TypoScriptPlugin.instance, "Couldn't serialialise configuration because the output file at " + configPath + " doesn't exist!"); } catch (IOException e) { Log.log(Log.ERROR, TypoScriptPlugin.instance, "An IOException occured while serialising the configuration to a file:" + e.toString()); } } /** * Handles EditBus messages that we care about * We are interested in options saving (we ping the browser to reload, if it's up, * and a buffer update so we can force the TypoScript editmode * @param message The message from EditBus * @credit Ollie Rutherfurd for his MoinMoin plugin */ public void handleMessage(EBMessage message) { if (message instanceof PropertiesChanged) { View[] openViews = jEdit.getViews(); for (int i = 0; i < openViews.length; i++) { TypoScriptSiteBrowser browser = (TypoScriptSiteBrowser)openViews[i].getDockableWindowManager().getDockable("typoscriptsitebrowser"); if (browser != null) { browser.checkForPossibleSitesUpdate(); } } } if (message instanceof BufferUpdate) { BufferUpdate bu = (BufferUpdate)message; if (bu.getWhat() == BufferUpdate.LOADED) { Buffer buf = bu.getBuffer(); VFS vfs = buf.getVFS(); if (vfs instanceof TypoScriptVFS) { Mode mode = jEdit.getMode("typoscript"); if (mode != null) { buf.setMode(mode); } } } } } } --- NEW FILE: T3TreeRenderer.java --- /*************************************************************** * Copyright notice * * (c) 2005,2006 Neil Bertram (ne...@ta...) * All rights reserved * * This plugin is part of the Typo3 project. The Typo3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * A copy is found in the textfile GPL.txt * * * This plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the source! ***************************************************************/ /** * $Id: T3TreeRenderer.java,v 1.1 2006/01/29 03:28:55 n31l Exp $ * * This renderer represents page nodes in the TypoScriptSiteBrowser * dockable's JTree. It sets appropriate icons (loaded from the icons * directory) and sets template titles bold. * * @author Neil Bertram <ne...@ta...> */ package typoscript; import java.awt.Component; import java.awt.Font; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JTree; import javax.swing.tree.DefaultTreeCellRenderer; /** * Represents the visual appearance of nodes in the site browser tree * eg. template nodes are shown in bold, and icons represent certain "doktypes" * * Icons courtesy of TYPO3 crystal skin and Crystal SVG from KDE icon set */ public class T3TreeRenderer extends DefaultTreeCellRenderer { private Icon pageIcon; private Icon sysfolderIcon; private Icon shortcutIcon; private Icon rootIcon; private Icon unknownIcon; private Icon loadingIcon; public T3TreeRenderer() { super(); pageIcon = new ImageIcon(TypoScriptPlugin.class.getResource("/typoscript/icons/page.png")); sysfolderIcon = new ImageIcon(TypoScriptPlugin.class.getResource("/typoscript/icons/sysfolder.png")); shortcutIcon = new ImageIcon(TypoScriptPlugin.class.getResource("/typoscript/icons/shortcut.png")); rootIcon = new ImageIcon(TypoScriptPlugin.class.getResource("/typoscript/icons/root.png")); unknownIcon = new ImageIcon(TypoScriptPlugin.class.getResource("/typoscript/icons/unknown.png")); loadingIcon = new ImageIcon(TypoScriptPlugin.class.getResource("/typoscript/icons/wait.png")); } public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); // If this is the (invisible) root node or one of the "Tree loading" nodes, just set the clock icon if (!(value instanceof T3PageNode)) { setIcon(loadingIcon); return this; } T3PageNode pageNode = (T3PageNode)value; if (pageNode.hasTemplate()) { Font boldFont = new Font("Default", Font.BOLD, 12); this.setFont(boldFont); } else { Font normalFont = new Font("Default", 0, 12); this.setFont(normalFont); } // For any page that's not a root node, set the tooltip to its UID // for a root, display its URL if (pageNode.getType() != -1) { setToolTipText("Page UID " + String.valueOf(pageNode.getUid())); } else { try { setToolTipText(pageNode.getSite().getUrlBase().toString()); } catch (Exception e) { // Ignore } } // Set the icon switch(pageNode.getType()) { case -1: setIcon(rootIcon); break; // Root of a site (globe icon) case 1: setIcon(pageIcon); break; // a standard page case 4: setIcon(shortcutIcon); break; // a shortcut to a page case 254: setIcon(sysfolderIcon); break;// a sysfolder default: setIcon(unknownIcon); break; // something I haven't bothered to find an icon for (lots of types) } return this; } } --- NEW FILE: TypoScriptSiteBrowser.java --- /*************************************************************** * Copyright notice * * (c) 2005,2006 Neil Bertram (ne...@ta...) * All rights reserved * * This plugin is part of the Typo3 project. The Typo3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * A copy is found in the textfile GPL.txt * * * This plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the source! ***************************************************************/ /** * $Id: TypoScriptSiteBrowser.java,v 1.1 2006/01/29 03:28:55 n31l Exp $ * * This is the dockable site browser used for opening a remote * TYPO3 template * * @author Neil Bertram <ne...@ta...> */ package typoscript; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Iterator; import java.util.Vector; import javax.swing.JButton; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.ToolTipManager; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import org.gjt.sp.jedit.View; import org.gjt.sp.jedit.jEdit; import org.gjt.sp.jedit.gui.DefaultFocusComponent; /** * The dockable site browser panel, displays a tree of sites which dynamically * load page tree on opening and allows the opening of templates through a context menu * or buttons at the bottom of the panel */ public class TypoScriptSiteBrowser extends JPanel implements DefaultFocusComponent { private JScrollPane siteTreePane; private JTree siteTree; protected DefaultMutableTreeNode treeRootNode; protected DefaultTreeModel siteTreeModel; protected JButton btnOpenConstants; protected JButton btnOpenSetup; protected View view; protected Vector localSitesConfig; private JPanel siteLoadPane; public TypoScriptSiteBrowser(View ourView, String position) { super(new BorderLayout()); view = ourView; treeRootNode = new DefaultMutableTreeNode("ROOT"); // not visible localSitesConfig = (Vector)TypoScriptPlugin.siteConfig.clone(); Iterator iter = localSitesConfig.iterator(); while (iter.hasNext()) { treeRootNode.add(new T3PageNode((T3Site)iter.next())); } siteTreeModel = new DefaultTreeModel(treeRootNode); siteTree = new JTree(siteTreeModel); siteTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); ToolTipManager.sharedInstance().registerComponent(siteTree); siteTree.setCellRenderer(new T3TreeRenderer()); siteTree.setRootVisible(false); siteTree.addTreeExpansionListener(new TreeExpansionListener() { public void treeExpanded(TreeExpansionEvent e) { T3PageNode opened = (T3PageNode)e.getPath().getLastPathComponent(); if (e.getPath().getPathCount() == 2) { // is root page opened.loadTree(siteTreeModel); } } public void treeCollapsed(TreeExpansionEvent e) { ; // ignore } }); siteTree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { if (siteTree.getLastSelectedPathComponent() instanceof T3PageNode) { T3PageNode selected = (T3PageNode)siteTree.getLastSelectedPathComponent(); if (selected.hasTemplate()) { // Enable the open buttons btnOpenConstants.setEnabled(true); btnOpenSetup.setEnabled(true); return; } } btnOpenConstants.setEnabled(false); btnOpenSetup.setEnabled(false); } }); siteTree.addMouseListener(new MouseAdapter() { // Some systems trigger popups on mousedown, others mouseup (see MouseEvent.isPopupTrigger()) public void mousePressed(MouseEvent e) { this.mouseReleased(e); // cheating! } public void mouseReleased(MouseEvent e) { if (e.isPopupTrigger()) { int row = siteTree.getRowForLocation(e.getX(), e.getY()); siteTree.setSelectionRow(row); T3PageNode curNode = (T3PageNode)siteTree.getLastSelectedPathComponent(); if (curNode.popupMenu != null) { curNode.popupMenu.show(siteTree, e.getX(), e.getY(), siteTree, view); } } } }); siteTree.setShowsRootHandles(false); siteTreePane = new JScrollPane(siteTree); this.add(siteTreePane, BorderLayout.CENTER); siteLoadPane = new JPanel(); btnOpenConstants = new JButton("Constants"); btnOpenSetup = new JButton("Setup"); btnOpenConstants.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { T3PageNode selected = (T3PageNode)siteTree.getLastSelectedPathComponent(); selected.open(siteTree, "constants", view); } }); btnOpenSetup.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { T3PageNode selected = (T3PageNode)siteTree.getLastSelectedPathComponent(); selected.open(siteTree, "setup", view); } }); btnOpenConstants.setEnabled(false); btnOpenSetup.setEnabled(false); siteLoadPane.add(btnOpenConstants); siteLoadPane.add(btnOpenSetup); this.add(siteTreePane, BorderLayout.CENTER); this.add(siteLoadPane, BorderLayout.PAGE_END); } /** * Called when someone focuses the browser panel */ public void focusOnDefaultComponent() { ; } /** * Called by the main plugin class to inform us of an options-save event on the EditBus */ public void checkForPossibleSitesUpdate() { if (!localSitesConfig.equals(TypoScriptPlugin.siteConfig)) { // The list has changed. Update localSitesConfig = (Vector)TypoScriptPlugin.siteConfig.clone(); Iterator iter = localSitesConfig.iterator(); treeRootNode.removeAllChildren(); while (iter.hasNext()) { treeRootNode.add(new T3PageNode((T3Site)iter.next())); } siteTreeModel.nodeStructureChanged(treeRootNode); } } } --- NEW FILE: T3Site.java --- /*************************************************************** * Copyright notice * * (c) 2005,2006 Neil Bertram (ne...@ta...) * All rights reserved * * This plugin is part of the Typo3 project. The Typo3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * A copy is found in the textfile GPL.txt * * * This plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the source! ***************************************************************/ /** * $Id: T3Site.java,v 1.1 2006/01/29 03:28:55 n31l Exp $ * * This object represents a remote site on a TYPO3 installation. * It contains the necessary URL and authentication information to * access site trees and template records. * These objects are serialiased within a Vector to save configuration * @see TypoScriptPlugin * * @author Neil Bertram <ne...@ta...> */ package typoscript; import java.io.Serializable; import java.net.MalformedURLException; import java.net.URL; /** * Represents a T3 site in a serialisable form for saving to the configuration file in $HOME/typoscriptplugin/sitesconfig.obj */ public class T3Site implements Serializable, Comparable { private String name; private URL urlBase; private URL urlFull; private String username; private String password; private boolean clearCacheOnSave; transient private RemoteCallWorker worker; private static final long serialVersionUID = 1L; /** * Constructor used for deserialising and for creating a temporary site in the Add Site form */ public T3Site() { name = ""; try { urlBase = new URL("http://www.example.com/siteroot/"); urlFull = null; } catch (MalformedURLException e) { ; // can't happen } username = ""; password = ""; clearCacheOnSave = true; //default worker = null; } /** * Standard constructor - use this one to create a new site * * @param newURLBase The URL base (URL to the site root) * @param newURLFull The Full URL (URL to the responder page) * @param user TYPO3 Administrator username * @param pass TYPO3 Administrator password * @param clearCache Whether to clear the TYPO3 page cache on each template save */ public T3Site(URL newURLBase, URL newURLFull, String user, String pass, boolean clearCache) { name = "UNKNOWN"; // will be fetched soon urlBase = newURLBase; urlFull = newURLFull; username = user; password = pass; clearCacheOnSave = clearCache; worker = new RemoteCallWorker(this); } public boolean clearCacheOnSave() { return clearCacheOnSave; } public void setClearCacheOnSave(boolean clearCacheOnSave) { this.clearCacheOnSave = clearCacheOnSave; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public URL getUrlBase() { return urlBase; } public void setUrlFull(URL url) { this.urlFull = url; } public URL getUrlFull() { return urlFull; } public void setUrlBase(URL url) { this.urlBase = url; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getName() { return name; } public void setName(String newName) { name = newName; } public RemoteCallWorker getWorker() { // If the worker isn't currently initialised (after deserialisation) we initialise it now if (worker == null) { worker = new RemoteCallWorker(this); } return worker; } public String toString() { return name; } // For sorting by name public int compareTo(Object other) { return this.toString().compareTo(other.toString()); } } --- NEW FILE: TypoScriptVFS.java --- /*************************************************************** * Copyright notice * * (c) 2005,2006 Neil Bertram (ne...@ta...) * All rights reserved * * This plugin is part of the Typo3 project. The Typo3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * A copy is found in the textfile GPL.txt * * * This plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the source! ***************************************************************/ /** * $Id: TypoScriptVFS.java,v 1.1 2006/01/29 03:28:55 n31l Exp $ * * This is the TypoScript over XML-RPC VFS for jEdit * * @author Neil Bertram <ne...@ta...> * @credit Ollie Rutherfurd <ol...@ru...> for inspiration */ package typoscript; import java.awt.Component; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.util.HashMap; import java.util.Iterator; import java.util.StringTokenizer; import java.util.Vector; import org.apache.xmlrpc.XmlRpcException; import org.gjt.sp.jedit.Buffer; import org.gjt.sp.jedit.io.VFS; import org.gjt.sp.util.Log; /** * Implementation of the TypoScript VFS protocol * Requires the jeditvfs TYPO3 extension to be available on the server. * * URLs for this VFS are of the form typoscript:Site Name::Path/To/Template.pageuid.templateuid.type.ts * type is either "constants" or "setup" * * @author Neil Bertram <ne...@ta...> * @credit Ollie Rutherfurd <ol...@ru...> for inspiration from his MoinMoin plugin * */ public class TypoScriptVFS extends VFS { private static HashMap pendingStreams; /** * Constructs a new TypoScriptVFS instance, which has only READ and WRITE capabilities, not save-as etc */ public TypoScriptVFS() { super("typoscript", READ_CAP | WRITE_CAP); } /** * jEdit calls this from the AWT thread when it wants to establish a VFS session * for a particular path. We don't need to keep any persistent data, so we just * return a dummy here * @param path The VFS path * @param comp The component of the parent window * @return dummy session (empty object) */ public Object createVFSSession(String path, Component comp) { pendingStreams = new HashMap(); // We don't need jEdit to keep session objects for us, return a "dummy" as per docs return new Object(); } /** * Gets the filename part of a path (Template.pageuid.templateuid.type.ts) * @param path is the path provided by jEdit (typoscript:sitename::treepath) */ public String getFileName(String path) { String last = ""; if (path.indexOf("/") != -1) { // this line used to read path.contains(), but has been changed to indexOf for JDK1.4 compliance // There's more than just the base site, strip back to the last / StringTokenizer st = new StringTokenizer(path, "/"); while (st.hasMoreTokens()) last = st.nextToken(); } else { // The template is in the root, return the part after the :: StringTokenizer st = new StringTokenizer(path, "::"); while (st.hasMoreTokens()) last = st.nextToken(); } return last; } public char getFileSeparator() { return '/'; } public String getTwoStageSaveName(String path) { return null; // disable 2-stage save } public InputStream _createInputStream(Object session, String path, boolean ignoreErrors, Component comp) throws IOException, FileNotFoundException, MalformedURLException { if (path.endsWith(".marks")) throw new FileNotFoundException(); Log.log(Log.DEBUG, this, "CREATE INPUT STREAM " + path); Object[] siteInfo = decodePath(path); T3Site site = (T3Site)siteInfo[0]; Integer uid = (Integer)siteInfo[1]; String type = (String)siteInfo[2]; // Perform the XMLRPC request String templateContents = ""; try { if (type.equals("constants")) { templateContents = site.getWorker().getConstants(uid); } else if (type.equals("setup")) { templateContents = site.getWorker().getSetup(uid); } } catch (XmlRpcException e) { switch(e.code) { case RemoteCallWorker.JEDITVFS_ERROR_AUTHFAIL: throw new IOException("Authentication with TYPO3 failed - Check you authentication settings in plugin options"); case RemoteCallWorker.JEDITVFS_ERROR_NOSUCHTEMPLATE: throw new FileNotFoundException(e.getMessage()); default: throw new IOException("Unexpected error while communicating with TYPO3: " + e.getMessage()); } } return new ByteArrayInputStream(templateContents.getBytes("UTF-8")); } /** * Creates an output stream for jEdit to save into. jEdit is actually saving into a String, which we commit on _saveCompleted */ public OutputStream _createOutputStream(Object session, String path, Component comp) throws IOException, FileNotFoundException, MalformedURLException { Log.log(Log.DEBUG, this, "CREATE OUTPUT STREAM"); ByteArrayOutputStream out = new ByteArrayOutputStream(); pendingStreams.put(path, out); return out; } /** * Write the contents of the output stream out to the server * @param Session The VFS session (ignored) * @param buffer The buffer * @param path The path the buffer was saved to - this is the path we <i>will</i> save to though * @throws IOException upon an exception from the underlying XML-RPC interface * @throws FileNotFoundException upon the remote template being deleted * @throws MalformedURLException in the case of a URL not containing all the elements we require (site, template UID, type) */ public void _saveComplete(Object Session, Buffer buffer, String path, Component comp) throws IOException, FileNotFoundException, MalformedURLException { Log.log(Log.DEBUG, this, "SAVE COMPLETE, WRITING"); Object[] siteInfo = decodePath(path); T3Site site = (T3Site)siteInfo[0]; Integer uid = (Integer)siteInfo[1]; String type = (String)siteInfo[2]; ByteArrayOutputStream outStream = (ByteArrayOutputStream)pendingStreams.remove(path); try { if (type.equals("constants")) { site.getWorker().putConstants(uid, outStream.toString("UTF-8")); } else if (type.equals("setup")) { site.getWorker().putSetup(uid, outStream.toString("UTF-8")); } } catch (XmlRpcException e) { Log.log(Log.ERROR, this, "Exception during save " + e); switch(e.code) { case RemoteCallWorker.JEDITVFS_ERROR_AUTHFAIL: throw new IOException("Authentication with TYPO3 failed - Check you authentication settings in plugin options"); case RemoteCallWorker.JEDITVFS_ERROR_NOSUCHTEMPLATE: throw new FileNotFoundException(e.getMessage()); default: throw new IOException("Unexpected error while communicating with TYPO3: " + e.getMessage()); } } } /** * This takes a path of the form Site name::PageName/OtherPageName/OtherOtherPageName.pageuid.templateuid.type.ts and finds * the particular site, template UID and request type * @param path The path to the template, as provided by jEdit * @return A 3-element Object array containing (in this order) the T3Site object, template UID, and type (constants/setup) * @throws FileNotFoundException If the site doesn't exist in the plugin's list of configured sites */ private Object[] decodePath(String path) throws FileNotFoundException, MalformedURLException { path = path.replaceFirst("typoscript:", ""); // First part of URL is the site StringTokenizer siteTokeniser = new StringTokenizer(path, "::"); if (!siteTokeniser.hasMoreTokens()) { throw new MalformedURLException("No site component found in TypoScript URL"); } String siteName = siteTokeniser.nextToken(); Log.log(Log.DEBUG, this, "LOCATE SITE " + siteName); Iterator iter = TypoScriptPlugin.siteConfig.iterator(); while (iter.hasNext()) { T3Site test = (T3Site)iter.next(); Log.log(Log.DEBUG, this, "CHECKING " + test.getName()); if (test.getName().equals(siteName)) { // Found it! Now extract the UID StringTokenizer uidTokeniser = new StringTokenizer(path, "."); Vector matches = new Vector(); while (uidTokeniser.hasMoreTokens()) { matches.add(uidTokeniser.nextToken()); } Log.log(Log.DEBUG, this, "Components " + matches); // The last 3 elements of matches will be uid, type and ts, so take size - 3 for uid and size -2 for type Integer uid = new Integer((String)matches.get(matches.size() - 3)); String type = (String)matches.get(matches.size() - 2); Log.log(Log.DEBUG, this, "UID is " + uid + "\nType is " + type); // Construct the return structure Object[] ret = new Object[3]; ret[0] = test; ret[1] = uid; ret[2] = type; return ret; } } // If we reach here, the site couldn't be found, this is a FileNotFoundException throw new FileNotFoundException("Unable to locate TYPO3 site for path: " + path); } } --- NEW FILE: TypoScriptPluginOptions.java --- /*************************************************************** * Copyright notice * * (c) 2005,2006 Neil Bertram (ne...@ta...) * All rights reserved * * This plugin is part of the Typo3 project. The Typo3 project is * free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * The GNU General Public License can be found at * http://www.gnu.org/copyleft/gpl.html. * A copy is found in the textfile GPL.txt * * * This plugin is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * This copyright notice MUST APPEAR in all copies of the source! ***************************************************************/ /** * $Id: TypoScriptPluginOptions.java,v 1.1 2006/01/29 03:28:55 n31l Exp $ * * This file contains the entire options dialog source for the * "Plugin Options" section. It allows adding, editing and removing * of TYPO3 sites, as well as verification of credentials etc. * * @author Neil Bertram <ne...@ta...> */ package typoscript; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; import java.util.Iterator; import java.util.Vector; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.methods.HeadMethod; import org.apache.xmlrpc.XmlRpcException; import org.gjt.sp.jedit.AbstractOptionPane; import org.gjt.sp.jedit.GUIUtilities; import org.gjt.sp.jedit.jEdit; import org.gjt.sp.jedit.gui.EnhancedDialog; import org.gjt.sp.jedit.gui.RolloverButton; /** * The options dialog in the Plugin Option panel */ public class TypoScriptPluginOptions extends AbstractOptionPane { private JList siteList; private JButton add; private JButton edit; private JButton remove; protected Vector localSitesConfig; protected DefaultListModel listModel; public TypoScriptPluginOptions() { super(TypoScriptPlugin.NAME); } protected void _init() { // Take a copy of the current sites config localSitesConfig = (Vector)TypoScriptPlugin.siteConfig.clone(); listModel = new DefaultListModel(); Iterator iter = localSitesConfig.iterator(); while (iter.hasNext()) { listModel.addElement(iter.next()); } setLayout(new BorderLayout()); add(BorderLayout.NORTH, new JLabel("TYPO3 Sites")); JPanel sites = new JPanel(new BorderLayout()); siteList = new JList(listModel); siteList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); siteList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent e) { updateButtons(); } }); siteList.addMouseListener(new MouseListener() { public void mouseClicked(MouseEvent e) { // Edit if doubleclick if (e.getClickCount() == 2) { new AddEditSiteDialog(TypoScriptPluginOptions.this, (T3Site)localSitesConfig.get(siteList.getSelectedIndex())); } } public void mouseEntered(MouseEvent e) {;} public void mousePressed(MouseEvent e) {;} public void mouseReleased(MouseEvent e) {;} public void mouseExited(MouseEvent e) {;} }); JScrollPane scrollPane = new JScrollPane(siteList); sites.add(scrollPane); this.add(sites); JPanel buttons = new JPanel(); buttons.setLayout(new BoxLayout(buttons,BoxLayout.X_AXIS)); buttons.setBorder(new EmptyBorder(6,0,0,0)); add = new RolloverButton(GUIUtilities.loadIcon("Plus.png")); add.setToolTipText("Add Site"); add.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // Open the add/edit dialog in add mode new AddEditSiteDialog(TypoScriptPluginOptions.this, null); } }); buttons.add(add); remove = new RolloverButton(GUIUtilities.loadIcon("Minus.png")); remove.setToolTipText("Remove Site"); remove.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { localSitesConfig.remove(siteList.getSelectedIndex()); listModel.removeElementAt(siteList.getSelectedIndex()); } }); buttons.add(remove); edit = new RolloverButton(GUIUtilities.loadIcon("ButtonProperties.png")); edit.setToolTipText("Edit Site"); edit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // Open add/edit dialog in edit mode new AddEditSiteDialog(TypoScriptPluginOptions.this, (T3Site)localSitesConfig.get(siteList.getSelectedIndex())); } }); buttons.add(edit); buttons.add(Box.createGlue()); updateButtons(); add(BorderLayout.SOUTH,buttons); } protected void _save() { // Copy the configuration back into the global store TypoScriptPlugin.siteConfig = localSitesConfig; // Save it back to disk TypoScriptPlugin.saveConfiguration(); } /** * Checks whether any buttons should be disabled/enabled * based on the selection (or lack thereof) in the list */ private void updateButtons() { int index = siteList.getSelectedIndex(); edit.setEnabled(index != -1 ? true : false); remove.setEnabled(index != -1 ? true : false); } } /** * The dialog for adding or editing a site */ class AddEditSiteDialog extends EnhancedDialog { private T3Site curSite; private TypoScriptPluginOptions parent; private T3Site origSite; private boolean isNew; protected JTextField txtURL; protected JTextField txtUser; protected JPasswordField txtPass; protected JCheckBox chkClearCache; protected JButton btnSave; protected JButton btnCancel; protected JButton btnTest; /** * Construcs the dialog to add or edit a site * If a site object is passed in, we are editing * otherwise a null site means new * * @param parent A reference to the option pane that opened this dialog * @param site A T3Site object to edit, or null to create a new one */ public AddEditSiteDialog(Component ourParent, T3Site site) { super(JOptionPane.getFrameForComponent(ourParent), (site == null) ? "New Site" : "Edit Site", true); parent = (TypoScriptPluginOptions)ourParent; if (site == null) { curSite = new T3Site(); // temporary isNew = true; origSite = null; } else { curSite = site; isNew = false; origSite = site; } JPanel formPanel = new JPanel(new GridBagLayout()); JLabel lblURL = new JLabel("Site root URL"); JLabel lblUser = new JLabel("Backend Login"); JLabel lblPass = new JLabel("Password"); txtURL = new JTextField(curSite.getUrlBase().toString(), 20); txtUser = new JTextField(curSite.getUsername(), 20); txtPass = new JPasswordField(curSite.getPassword(), 20); chkClearCache = new JCheckBox("Clear TYPO3 cache on template save", curSite.clearCacheOnSave()); // defaults to true btnSave = new JButton("Save"); btnSave.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ok(); } }); btnCancel = new JButton("Cancel"); btnCancel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { cancel(); } }); btnTest = new JButton("Test settings"); btnTest.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { test(true); } }); formPanel.add(lblURL, GridBagHelper.getConstraint(0, 0, 1, 1, "right")); formPanel.add(txtURL, GridBagHelper.getConstraintWithInsets(1, 0, 1, 1, "left", 1, 5, 1, 0)); formPanel.add(lblUser, GridBagHelper.getConstraint(0, 1, 1, 1, "right")); formPanel.add(txtUser, GridBagHelper.getConstraintWithInsets(1, 1, 1, 1, "left", 1, 5, 1, 0)); formPanel.add(lblPass, GridBagHelper.getConstraint(0, 2, 1, 1, "right")); formPanel.add(txtPass, GridBagHelper.getConstraintWithInsets(1, 2, 1, 1, "left", 1, 5, 3, 0)); formPanel.add(chkClearCache, GridBagHelper.getConstraint(0, 3, 2, 1, "centre")); JPanel buttons = new JPanel(); buttons.add(btnSave); buttons.add(btnCancel); buttons.add(btnTest); formPanel.add(buttons, GridBagHelper.getConstraint(0, 5, 2, 1, "centre")); this.getContentPane().add(formPanel); this.pack(); this.setLocationRelativeTo(GUIUtilities.getParentDialog(parent)); this.setVisible(true); txtURL.requestFocus(); } public void cancel() { dispose(); } public void ok() { if (!test(false)) return; // test() will report any errors // Commit curSite if (isNew) { // Append parent.localSitesConfig.add(curSite); Collections.sort(parent.localSitesConfig); parent.listModel.addElement(curSite); } else { // Replace the old version by inserting the new one into the same index int index = parent.localSitesConfig.indexOf(origSite); parent.localSitesConfig.remove(origSite); parent.listModel.removeElement(origSite); parent.localSitesConfig.insertElementAt(curSite, index); parent.listModel.add(index, curSite); } dispose(); } /** * Checks to ensure all fields are filled out and that login credentials check out * * @param reportOK Whether to report on success (false if this is a passive check before adding) * @return true if everything checks out */ private boolean test(boolean reportOK) { URL urlFull = null; URL urlBase = null; if (txtURL.getText().length() == 0) { JOptionPane.showMessageDialog(this, "You must specify a URL", "URL Field Blank", JOptionPane.ERROR_MESSAGE); return false; } else { try { String typeNum = jEdit.getProperty(TypoScriptPlugin.PROPERTY_PREFIX + "typenum"); urlFull = new URL(txtURL.getText() + "index.php?type=" + typeNum); urlBase = new URL(txtURL.getText()); } catch(MalformedURLException e) { JOptionPane.showMessageDialog(this, "The URL you entered was not valid.\nPlease ensure it starts with http:// or https:// and contains no spaces or other characters that would normally be disallowed in a URL", "Invalid URL", JOptionPane.ERROR_MESSAGE); return false; } if (!txtURL.getText().endsWith("/")) { JOptionPane.showMessageDialog(this, "The URL you entered did not end with a '/' character. This is required.\nIf you would normally login at http://typo3.example.com/typo3/, then you need to enter http://typo3.example.com/", "Invalid URL", JOptionPane.ERROR_MESSAGE); return false; } } if (txtUser.getText().length() == 0) { JOptionPane.showMessageDialog(this, "You must specify a username, even if you have some alternative authentication scheme (make one up)", "Username Field Blank", JOptionPane.ERROR_MESSAGE); return false; } if (txtPass.getPassword().length == 0) { JOptionPane.showMessageDialog(this, "You must specify a password", "Password Field Blank", JOptionPane.ERROR_MESSAGE); return false; } // First stage checks out. Disable the buttons and check the provided information disableButtons(); this.pack(); // resize window try { HttpClient client = new HttpClient(); HeadMethod head = new HeadMethod(urlFull.toString()); client.executeMethod(head); //head.execute() if (head.getStatusCode() != HttpStatus.SC_OK) { JOptionPane.showMessageDialog(this, "Received a non-200 response code from the server: " + head.getStatusCode() + "(" + head.getStatusText() + ")\nPlease ensure there are no rewrite rules or redirects in the way.\nI tried URL: " + urlFull.toString(), "Non-200 response", JOptionPane.ERROR_MESSAGE); resetButtonState(); return false; } if (head.getResponseHeader("X-jeditvfs-present") == null) { JOptionPane.showMessageDialog(this, "The jEdit VFS extension doesn't appear to be installed on the remote site.\nPlease install the 'jeditvfs' plugin from the TYPO3 extension repository and try again.\nIt's also possible the URL didn't point to the right place.\nYou shoud be pointing to the root of the FRONTEND of your site.\nI tried: " + urlFull.toString(), "jeditvfs extension not located", JOptionPane.ERROR_MESSAGE); resetButtonState(); return false; } } catch (IOException e) { JOptionPane.showMessageDialog(this, "An I/O error occured while testing the site.\nPlease check your internet connection. Details below:\n" + e.toString(), "IOException", JOptionPane.ERROR_MESSAGE); resetButtonState(); return false; } // That worked. Now we need to construct a real T3Site object and perform an authentication check (and get the site name in the process) curSite = new T3Site(urlBase, urlFull, txtUser.getText(), new String(txtPass.getPassword()), chkClearCache.isSelected()); try { curSite.setName(curSite.getWorker().getSiteTitle()); } catch (IOException e) { JOptionPane.showMessageDialog(this, "An I/O error occured while testing the site.\nPlease check your internet connection. Details below:\n" + e.toString(), "IOException", JOptionPane.ERROR_MESSAGE); resetButtonState(); return false; } catch (XmlRpcException e) { // Probably an authentication failure. if (e.code == RemoteCallWorker.JEDITVFS_ERROR_AUTHFAIL) { JOptionPane.showMessageDialog(this, "Authentication failed.\nPlease make sure you typed your username and password correctly.\nNote also that you require an admin-level backend account to use this system.\nIf you get this failure and are using an exotic authentication scheme, please contact the developer of this plugin to report a bug.\nServer said: " + e.getMessage(), "Backend authentication failed", JOptionPane.ERROR_MESSAGE); } else { JOptionPane.showMessageDialog(this, "The server returned an unexpected response in reply to an authentication test\nServer said: " + e.getMessage(), "Unexpected XML-RPC exception", JOptionPane.ERROR_MESSAGE); } resetButtonState(); return false; } if (reportOK) { JOptionPane.showMessageDialog(this, "All OK", "All tests passed", JOptionPane.INFORMATION_MESSAGE); } resetButtonState(); return true; } private void disableButtons() { btnSave.setEnabled(false); btnTest.setEnabled(false); } private void resetButtonState() { btnSave.setEnabled(true); btnTest.setEnabled(true); } } /** * This class returns GridBagConstraints, optionally with inset padding. It speeds up the use of * the GridBag layout manager! * * (borrowed from an earlier project of mine) * @author Neil Bertram <ne...@ta...> * */ class GridBagHelper { /** * This method is STATIC and will give you a GridBagConstraints with the required attributes * @param gridx Horizontal grid position of the element * @param gridy Vertical grid position of the element * @param gridwidth How many cells the element should span * @param gridheight How many rows the element should span * @param position either left, centre, right, top-left, top-right, bottom-left, bottom-right * @return a GridBagConstraints object with the desired attributes. */ public static GridBagConstraints getConstraint(int gridx, int gridy, int gridwidth, int gridheight, String position) { GridBagConstraints tempGbc = new GridBagConstraints(); tempGbc.gridx = gridx; tempGbc.gridy = gridy; tempGbc.gridwidth = gridwidth; tempGbc.gridheight = gridheight;... [truncated message content] |