From 56d545d175793ad6ffc45d16e02e87097769a38b Mon Sep 17 00:00:00 2001
From: jensp Example: The common usage for a Link component is
+ * illustrated in the code fragment below:
+ *
+ * The target of the link above will be rendered in HTML as:
+ * href="path/to/target/?foo=1"
+ * If either the link text or the URL needs to be changed for a link
+ * within a locked page, a {@link PrintListener} should be used.
+ *
+ * @version $Id$
+ */
+public class Link extends BaseLink {
+
+ private static final Logger s_log = Logger.getLogger(ParameterMap.class);
+
+ private static final String FRAME_TARGET_ATTR = "target";
+
+ private URL m_webURL = null;
+ private ParameterMap m_params = new ParameterMap();
+
+ /**
+ * The value for the XML type attribute for a plain link.
+ */
+ protected final String TYPE_LINK = "link";
+
+ /**
+ * Passing this value to {@link #setTargetFrame setTargetFrame} will
+ * create a link that opens a new browser window whenever it is clicked.
+ * This constructor is a common one for a Link component, as it allows
+ * for the Link text and the target URL to be set at the same time during
+ * construction. Starting with release 5.2, this method prefixes the
+ * passed-in The vast majority of CCM UI code expects to link through the
+ * dispatcher. Code that does not should use the
+ * Constructs a Link using a
+ * All the variables set with this method are appended to the
+ * query string in the URL that is output for this Get the "target" attribute of the link, which determines
+ * which browser frame will load the new page when this link is
+ * clicked. Set the "target" attribute of the link, which determines
+ * which browser frame will load the new page when this link is
+ * clicked. ListPanels can be ordered or unordered. Generates a DOM fragment:
+ *
+ * Page p = new Page("Link Example");
+ * Link link = new Link(new Label(GlobalizedMessage),"path/to/target/");
+ * link.setVar("foo","1");
+ * p.add(link);
+ *
+ *
+ * url with the
+ * webapp context path or the dispatcher servlet path.
+ *
+ * The vast majority of CCM UI code expects to link through
+ * the dispatcher. Code that does not should use the
+ * Link constructor taking a URL.
+ * @see #Link(String,URL)
+ */
+ public Link(Component child, String url) {
+ super(child, url);
+ init();
+ }
+
+ /**
+ * Constructors with PrintListener parameters allow for a
+ * {@link PrintListener} to be set for the Link, without the need
+ * to make a separate call to the addPrintListener method.
+ * PrintListeners are a convenient way to alter underlying Link
+ * attributes such as Link text or target URL within a locked page
+ * on a per request basis.
+ *
+ * @param child
+ * @param l
+ */
+ public Link(Component child, PrintListener l) {
+ super(child, l);
+
+ init();
+ }
+
+
+ /**
+ * Constructors with PrintListener parameters allow for a
+ * {@link PrintListener} to be set for the Link, without the need to make a
+ * separate call to the addPrintListener method. PrintListeners
+ * are a convenient way to alter underlying Link attributes such as Link
+ * text or target URL within a locked page on a per request basis.
+ *
+ * @deprecated refactor to use Link(Component,PrintListener) to provide a
+ * globalized label for the link.
+ */
+ public Link(String label, PrintListener l) {
+ super(label,l);
+
+ init();
+ }
+
+
+ /**
+ * Constructors with PrintListener parameters allow for a
+ * {@link PrintListener} to be set for the Link, without the need to make a
+ * separate call to the addPrintListener method. PrintListeners
+ * are a convenient way to alter underlying Link attributes such as Link
+ * text or target URL within a locked page on a per request basis.
+ *
+ * @param listener PrintListener, may be used to change either the Display
+ * text or the url within a locked page.
+ */
+ public Link(PrintListener listener) {
+ super(listener);
+
+ init();
+ }
+
+ /**
+ * url with the path to the CCM dispatcher.
+ * Code using this constructor should not prefix url
+ * with the webapp context path or the dispatcher servlet
+ * path.Link constructor taking a URL.URL. When this constructor is
+ * used, the method {@link #setVar(String,String)} and its deprecated
+ * equivalent have no effect on the resulting hyperlink. Instead, use the
+ * ParameterMap argument to URL.String of label text
+ * @param url a URL for the link's target
+ * @deprecated refactor to use Link(Component,URL) to provide a
+ * globalized label for the link.
+ */
+ public Link(String label, URL url) {
+ super(label, url.toString());
+
+ init();
+
+ m_webURL = url;
+ }
+
+ @Override
+ public Object clone() throws CloneNotSupportedException {
+ Link result = (Link) super.clone();
+
+ result.m_params = (ParameterMap) m_params.clone();
+
+ return result;
+ }
+
+ /**
+ * Sets a query variable and its value. Overwrites any values that may
+ * have been set previously under the specified name.
+ * Link.
+ *
+ * @param name the name of the query
+ * @param value the value for the query
+ * @pre name != null
+ */
+ public void setVar(String name, String value) {
+ Assert.isUnlocked(this);
+
+ m_params.setParameter(name, value);
+ }
+
+// No longer used anywhere in the code base
+// /**
+// * Set a query variable and its value
+// * @deprecated use {@link #setVar setVar}
+// */
+// public void addURLVars(String name, String value) {
+// setVar(name, value);
+// }
+
+ /**
+ *
+ * @return
+ * may be this method should be deprecated as well as addURLVars?
+ */
+ public String getURLVarString() {
+ return m_params.toString();
+ }
+
+ /**
+ * ListPanel, progressing from top to bottom.
+ *
+ * true is an ordered (numbered) list;
+ * false is an unordered (bulleted) list
+ *
+ */
+ public ListPanel(boolean ordered) {
+ m_ordered = ordered;
+ }
+
+ /**
+ * Adds child components as a subtree under list-item nodes.
+ *
+ * <bebop:listPanel>
+ * <bebop:cell> ... cell contents </bebop:cell>
+ * <bebop:cell> ... cell contents </bebop:cell>
+ * ...
+ * </bebop:list>
The PageFactory provides a framework for
+ * instantiating instances of the Bebop Page class in a
+ * project- and application-independant manner.
The traditional approach to writing bebop applications is to + * subclass the com.arsdigita.bebop.Page, adding various components to + * provide navigation & layout and writing custom XSL for styling + * purposes.
+ * + *The problem of this approach is that when multiple applications + * are combined to form a complete site, is it difficult to produce an + * integrated navigation infrastructure & uniform styling across all + * pages. In addition, by placing application specific functionality + * in a subclass of Page, the ability to reuse & embed applications + * within each other is hampered since Page objects cannot be + * nested.
+ * + *The PageFactory addresses the following situations
+ * + *The point of interaction for PageFactory is typically in the
+ * application's Dispatcher class. Rather than invoking the Page
+ * constructor (ie new Page(title)), applications call
+ * PageFactory.buildPage(title, "appname") This method
+ * will return an instance of Page with the currently configured
+ * navigation components added.
The two compulsory arguments to the buildPage
+ * method are the name of the application (ie, 'forum', 'cms'), and
+ * the page title (either as a String or Label object). The
+ * application name is used as key in both the enterprise.init file
+ * configuration & the XSL templates. There is optionally a third
+ * string id parameter which provides a unique identifier
+ * for the page within the application. If the page class is a
+ * subclass of ApplicationPage this will be used to set the XML
+ * id attribute.
Consider the following example (based loosely on the Simple + * Survey application):
+ * + *
+ * package com.arsdigita.simplesurvey.dispatcher;
+ *
+ * import com.arsdigita.simplesurvey.ui.IndexPanel;
+ * import com.arsdigita.simplesurvey.ui.AdminPanel;
+ * import com.arsdigita.simplesurvey.ui.SurveySelectionModel;
+ * import com.arsdigita.bebop.BigDecimalParameter;
+ * import com.arsdigita.bebop.page.BebopMapDispatcher;
+ *
+ * public class Dispatcher extends BebopMapDispatcher {
+ *
+ * public Dispatcher() {
+ * Page index = buildIndexPage();
+ * Page admin = buildAdminPage();
+ *
+ *
+ * addPage("index.jsp",index);
+ * addPage("", index);
+ * addPage("admin/index.jsp",index);
+ * addPage("admin", index);
+ * }
+ *
+ * private Page buildIndexPage() {
+ * SurveySelectionModel survey =
+ * new SurveySelectionModel(new BigDecimalParameter("survey"));
+ *
+ * Page page = PageFactory.buildPage("simplesurvey",
+ * "Simple Survey",
+ * "index");
+ * page.add(IndexPanel(survey));
+ * page.addGlobalStateParam(survey.getStateParameter());
+ * page.lock();
+ * return page;
+ * }
+ *
+ * private Page buildAdminPage() {
+ * SurveySelectionModel survey =
+ * new SurveySelectionModel(new BigDecimalParameter("survey"));
+ *
+ * Page page = PageFactory.buildPage("simplesurvey",
+ * "Simple Survey Administration",
+ * "admin");
+ * page.add(AdminPanel(survey));
+ * page.addGlobalStateParam(survey.getStateParameter());
+ * page.lock();
+ * return page;
+ * }
+ *
+ * }
+ *
+ *
+ * The process of updating existing applications to make use of the + * PageFactory varies according to the complexity of the application + * in question.
+ * + *In the simplest case where an application has not subclassed the
+ * Page class, then it is just a case of replacing calls to new
+ * Page(title) with PageFactory.buildPage("appname",
+ * title).
When an application has subclassed Page, then the initial
+ * approach is to change the subclass in question so that it derives
+ * from SimpleContainer instead of Page. Any calls to the
+ * addGlobalStateParam or addRequestListener
+ * methods can be moved from the constructor into the
+ * register method where there will be direct access to
+ * the Page object.
The com.arsdigita.bebop.base_page system property
+ * may be used to specify the full name of a subclass of Page which
+ * provides the constructor signature detailed in the
+ * setPageClass method. If omitted it defaults to
+ * BasePage.
The com.arsdigita.ui package provides an
+ * alternative subclass called SimplePage which supports
+ * specification.
Based on the sample code distributed with the JavaMail 1.2 API. + * + * @author Ron Henderson + * @version $Id$ + */ + +public class ByteArrayDataSource implements DataSource { + + /** + * Holds the data for this DataSource + */ + + private byte[] m_data; + + /** + * MIME type of the data + */ + + private String m_type; + + /** + * Name of the data (optional) + */ + + private String m_name; + + /** + * Creates a data source from an input stream. + * + * @param is the InputStream to read from + * @param type the MIME type of the data + * @param name the name of the data + */ + + public ByteArrayDataSource (InputStream is, + String type, + String name) + { + m_type = type; + m_name = name; + + try { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + int ch; + + while ((ch = is.read()) != -1) { + os.write(ch); + } + + m_data = os.toByteArray(); + } catch (IOException ioex) { + // do something + } + } + + /** + * Creates a data source from a byte array. + * + * @param data the byte array containing the data + * @param type the MIME type of the data + * @param name the name of the data + */ + + public ByteArrayDataSource (byte[] data, + String type, + String name) + { + m_data = data; + m_type = type; + m_name = name; + } + + /** + * Creates a data source from a String, assuming the data only + * contains ASCII characters (iso-8859-1). + * + * @param data the String containing the data + * @param type the MIME type of the data + * @param name the name of the data + */ + + public ByteArrayDataSource (String data, + String type, + String name) + { + this(data,type,name,"iso-8859-1"); + } + + /** + * Creates a data source from a String using a specified character + * set. + * + * @param data the String containing the data + * @param type the MIME type of the data + * @param name the name of the data + * @param charset the encoding used for the String + */ + + public ByteArrayDataSource (String data, + String type, + String name, + String charset) + { + m_type = type; + m_name = name; + + try { + m_data = data.getBytes(charset); + } catch (UnsupportedEncodingException uex) { + // do something + } + } + + /** + * Returns an input stream for the data. + */ + + public InputStream getInputStream() throws IOException { + if (m_data == null) { + throw new IOException("no data"); + } + return new ByteArrayInputStream(m_data); + } + + /** + * Required by the interface, but not available. + */ + + public OutputStream getOutputStream() throws IOException { + throw new IOException("not implemented"); + } + + /** + * Returns the MIME type of the content. + */ + + public String getContentType() { + return m_type; + } + + /** + * Returns the name of the content. + */ + + public String getName() { + return m_name; + } +} diff --git a/ccm-core/src/main/java/com/arsdigita/mail/Mail.java b/ccm-core/src/main/java/com/arsdigita/mail/Mail.java new file mode 100644 index 000000000..9ed4cd898 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/mail/Mail.java @@ -0,0 +1,1062 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.mail; + +import com.arsdigita.util.MessageType; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Properties; +import java.util.StringTokenizer; +import javax.activation.DataHandler; +import javax.activation.FileDataSource; +import javax.activation.URLDataSource; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.SendFailedException; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMultipart; +import javax.mail.internet.MimeUtility; +import org.apache.log4j.Logger; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +/** + * Represents a email message with optional attachments. This class + * is a wrapper for the JavaMail API that makes it easier for + * application developers to create and send email. For simple plain + * text message, there is a static convenience method that does not + * require the construction of an explicit Mail object: + * + *
+ * Mail.send(to, from, subject, body) + *+ * + *
For more complex messages, the API provides methods to set all + * standard mail headers, attach other pieces of content, and finally + * invoke the transport process to deliver the message. + * + * @author Ron Henderson + * @version $Id$ + */ +public class Mail implements MessageType { + + /** + * Used for logging. + */ + private static final Logger s_log = + Logger.getLogger(Mail.class); + private static MailConfig s_config; + + public static MailConfig getConfig() { + if (s_config == null) { + s_config = new MailConfig(); + s_config.load("ccm-core/mail.properties"); + s_config.require("javamail.properties"); + } + return s_config; + } + private static final InternetAddress[] EMPTY_ADDRESS_LIST = + new InternetAddress[0]; + /** + * Table of message headers. + */ + private Hashtable m_headers; + /** + * Email addresses the message is being sent to. + */ + private InternetAddress[] m_to; + private InternetAddress[] m_filteredTo = EMPTY_ADDRESS_LIST; + private InternetAddress[] m_invalidTo = EMPTY_ADDRESS_LIST; + private static Set s_invalidDomains = new HashSet(); + static { + s_log.debug("Static initalizer starting..."); + s_invalidDomains.add("example.com"); + s_log.debug("Static initalizer finished."); + } + /** + * Email address the message is being sent from. + */ + private InternetAddress m_from; + /** + * Email address used for replies to this message. + */ + private InternetAddress[] m_replyTo; + /** + * Email addresses that the message is being carbon-copied to. + */ + private InternetAddress[] m_cc; + /** + * Email addresses that the message is being blind carbon-copied to. + */ + private InternetAddress[] m_bcc; + /** + * Message subject. + */ + private String m_subject; + /** + * Message body (can be text or HTML). + */ + private String m_body; + /** + * Message body alternate (if the body is HTML) + */ + private String m_alternate; + /** + * Encoding specification for m_body and m_alternate (optional). + * Default value (null) implies "us-ascii" encoding. + */ + private String m_encoding; + /** + * Message attachments (optional) + */ + private MimeMultipart m_attachments; + /** + * Unique identifier for each mail send out. + */ + private String m_messageID; + /** + * Session object used to send mail. + */ + private static Session s_session; + /** + * SMTP host to connect to. Only used to override the default for + * testing purposes. + */ + private static String s_host; + /** + * SMTP port to connect to. Only used to override the default for + * testing purposes. + */ + private static String s_port; + // Constants used by Mail + final static String CONTENT_TYPE = "Content-Type"; + final static String CONTENT_ID = "Content-ID"; + final static String MIXED = "mixed"; + final static String ALTERNATIVE = "alternative"; + /** + * Disposition of "inline" + */ + public final static String INLINE = javax.mail.Part.INLINE; + /** + * Disposition of "attachment" + */ + public final static String ATTACHMENT = javax.mail.Part.ATTACHMENT; + + /** + * Default constructor. Must use the setTo, setSubject (and so on) + * methods to create a valid mail message. + */ + public Mail() { + this(null, null, null, null); + } + + /** + * Constructor used to specify to, from, and subject. + * + * @param to one or more of addresses to send the message to + * @param from the address the message is being sent from + * @param subject the subject for the message + */ + public Mail(String to, + String from, + String subject) { + this(to, from, subject, null); + } + + /** + * Constructor used to specify to, from, subject, and body. + * + * @param to one or more of addresses to send the message to + * @param from the address the message is being sent from + * @param subject the subject for the message + * @param body the plain text body of the message + */ + public Mail(String to, + String from, + String subject, + String body) { + m_to = (to == null ? EMPTY_ADDRESS_LIST : parseAddressField(to)); + filterRecipients(); + m_from = (from == null ? null : parseAddress(from)); + m_subject = subject; + setBody(body); + } + + /** + * Constructor used to specify to, from, subject, body, and + * encoding. + * + * @param to one or more of addresses to send the message to + * @param from the address the message is being sent from + * @param subject the subject for the message + * @param body is plain text body of the message + * @param enc the encoding of the body + */ + public Mail(String to, + String from, + String subject, + String body, + String enc) { + this(to, from, subject, body); + setEncoding(enc); + } + + /** + * A convenience method to send a simple plain-text message. + * + * @param to one or more of addresses to send the message to + * @param from the address the message is being sent from + * @param subject the subject for the message + * @param body the plain text body of the message + */ + public static void send(String to, + String from, + String subject, + String body) + throws MessagingException, + SendFailedException { + Mail msg = new Mail(to, from, subject, body); + msg.send(); + } + + /** + * Sends the message. + */ + public void send() + throws MessagingException, + SendFailedException { + Transport transport = getSession().getTransport(); + transport.connect(); + send(transport); + transport.close(); + } + + /** + * Sends the message using a given Transport object (package-level + * access). This method is used when sending multiple messages at + * once with a single connection to the mail server. + * + * @throws SendFailedException on any kind of MessagingException, + * also such returned from the server. Applications might try + * to catch this and re-schedule sending the mail. + */ + void send(Transport transport) + throws MessagingException, + SendFailedException { + Message msg = null; + if (m_filteredTo.length > 0) { + msg = getMessage(); + + try { + transport.sendMessage(msg, msg.getAllRecipients()); + } catch (MessagingException mex) { + + // Close the transport agent and rethrow error for + // detailed message. + + transport.close(); + + throw new SendFailedException("send failed: ", mex); + } + } + + // Write a copy of the message into the log file + + if (getConfig().isDebug()) { + if (msg != null) { + try { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + msg.writeTo(os); + s_log.debug("message sent:\n" + os.toString() + + "\n-- EOT --"); + } catch (IOException ex) { + s_log.error("unable to log message"); + } + } else { + s_log.debug("no message sent. No valid recipients:\n"); + } + } else { + s_log.info("message sent to <" + Arrays.asList(m_filteredTo) + + "> from <" + m_from + "> subject <" + m_subject + ">"); + s_log.info("messages filtered for <" + Arrays.asList(m_invalidTo) + + "> from <" + m_from + "> subject <" + m_subject + ">"); + } + } + + /** + * Sets the email address that the message is being sent + * to. + * + * @param to one or more addresses to send the message to + */ + public void setTo(String to) { + m_to = parseAddressField(to); + filterRecipients(); + } + + /** + * Sets the email address that the message is being sent + * from. + * + * @param from the address the message is sent from + */ + public void setFrom(String from) { + m_from = parseAddress(from); + } + + /** + * Sets the subject of the message. + * + * @param subject the subject of the message + */ + public void setSubject(String subject) { + m_subject = subject; + } + + /** + * Sets the replyTo address. + * + * @param replyTo the address to use for replies + */ + public void setReplyTo(String replyTo) { + m_replyTo = parseAddressField(replyTo); + } + + /** + * Sets the Message ID + * + * @param messageID unique identifier for each email. + */ + public void setMessageID(String messageID) { + m_messageID = messageID; + } + + /** + * Sets the mail's MIME headers. + * + * @param headers a String containing MIME headers + */ + public void setHeaders(String headers) { + m_headers = parseHeaderField(headers); + } + + /** + * Adds a header (name, value) pair. + * + * @param name the header element name + * @param value the header element value + */ + public void addHeader(String name, String value) { + if (m_headers == null) { + m_headers = new Hashtable(); + } + + m_headers.put(name, value); + } + + /** + * Sets the email address that is being carbon-copied. + * @param cc the email address for a carbon copy + */ + public void setCc(String cc) { + m_cc = parseAddressField(cc); + } + + /** + * Sets the email address that is being blind carbon-copied. + * @param bcc the email address for a blind carbon copy + */ + public void setBcc(String bcc) { + m_bcc = parseAddressField(bcc); + } + + /** + * Sets the body of the email to a simple plain text message. + * @param body the body of the message in plain text + */ + public void setBody(String body) { + m_body = body; + } + + /** + * Sets the body of the email to an HTML encoded message with a + * plain text alternative. + * + * @param body the body of the message in HTML + * @param alt the alternate message body in plain text + */ + public void setBody(String body, String alt) { + m_body = body; + m_alternate = alt; + } + + /** + * Sets the character encoding. Valid encodings include "us-ascii", + * "iso-8859-1" for w-Europe, "iso-8859-2" for e-Europe, and so on. + * + * @param enc the requested encoding + */ + public void setEncoding(String enc) { + m_encoding = enc; + } + + /** + * Returns the character encoding that is being used. The default + * is "us-ascii". + * + * @return the string value of the character encoding being used + */ + public String getEncoding() { + return m_encoding; + } + + /** + * Adds an attachment to a message. This method is private but + * is invoked by all of the other attach methods once they've + * constructed an appropraite MimeBodyPart to attach. + * + * @param part the message part to attach + */ + private void attach(MimeBodyPart part) + throws MessagingException { + if (m_attachments == null) { + m_attachments = new MimeMultipart(); + } + m_attachments.addBodyPart(part); + } + + /** + * Adds an attachment with a specified name and description to a + * message by fetching its content from a URL. Sets the + * disposition to ATTACHMENT. + * + * @param url the URL to retreieve the content from + * @param name the name of the attachment + * @param description a description of the attachment + */ + public void attach(URL url, + String name, + String description) + throws MessagingException { + attach(url, name, description, Mail.ATTACHMENT); + } + + /** + * Adds an attachment with a specified name, description and + * disposition to a message by fetching its content from a URL. + * + * @param url the URL to retreieve the content from + * @param name the name of the attachment + * @param description a description of the attachment + * @param disposition Mail.ATTACHMENT or Mail.INLINE + */ + public void attach(URL url, + String name, + String description, + String disposition) + throws MessagingException { + MimeBodyPart part = new MimeBodyPart(); + attach(part); + + DataHandler dh = new DataHandler(new URLDataSource(url)); + + part.setDataHandler(dh); + part.setFileName(name); + part.setDescription(description); + part.setDisposition(disposition); + } + + /** + * Adds an attachment with a specified name and description to a + * message by fetching its content from a local file. Sets the + * disposition to ATTACHMENT. + * + * @param path the file path to retreieve the content from + * @param name the name of the attachment + * @param description a description of the attachment + */ + public void attach(File path, + String name, + String description) + throws MessagingException { + attach(path, name, description, ATTACHMENT); + } + + /** + * Adds an attachment with a specified name, description and + * disposition to a message by fetching its content from a + * local file. + * + * @param path the file path to retreieve the content from + * @param name the name of the attachment + * @param description a description of the attachment + * @param disposition Mail.ATTACHMENT or Mail.INLINE + */ + public void attach(File path, + String name, + String description, + String disposition) + throws MessagingException { + MimeBodyPart part = new MimeBodyPart(); + attach(part); + + DataHandler dh = new DataHandler(new FileDataSource(path)); + + part.setDataHandler(dh); + part.setFileName(name); + part.setDescription(description); + part.setDisposition(disposition); + } + + /** + * Attaches a byte array to a message. Sets the MIME type and + * name of the attachment, and initializes its disposition to + * ATTACHMENT. + * + * @param data the content of the attachment + * @param type the MIME type of the attachment + * @param name the name of the attachment + */ + public void attach(byte[] data, + String type, + String name) + throws MessagingException { + attach(data, type, name, null, ATTACHMENT); + } + + /** + * Attaches a byte array to a message. Sets the MIME type, name, + * description and disposition of the attachment. + * + * @param data the content of the attachment + * @param type the MIME type of the attachment + * @param name the name of the attachment + * @param description a description of the attachment + * @param disposition Mail.ATTACHMENT or Mail.INLINE + * @throws javax.mail.MessagingException + */ + public void attach(byte[] data, + String type, + String name, + String description, + String disposition) + throws MessagingException { + ByteArrayDataSource ds = new ByteArrayDataSource(data, type, name); + attach(ds, description, disposition); + } + + /** + * Attaches a String to a message. Sets the MIME type and name of + * the attachment, and initializes the disposition to ATTACHMENT. + * + * @param data the content of the attachment + * @param type the MIME type of the attachment + * @param name the name of the attachment + */ + public void attach(String data, + String type, + String name) + throws MessagingException { + attach(data, type, name, null, ATTACHMENT); + } + + /** + * Attaches a String to a message. Sets the MIME type, name, + * description and disposition of the attachment. + * + * @param data the content of the attachment + * @param type the MIME type of the attachment + * @param name the name of the attachment + * @param description a description of the attachment + * @param disposition Mail.ATTACHMENT or Mail.INLINE + */ + public void attach(String data, + String type, + String name, + String description, + String disposition) + throws MessagingException { + ByteArrayDataSource ds = new ByteArrayDataSource(data, type, name); + attach(ds, description, disposition); + } + + /** + * Attaches the content from a ByteArrayInputStream to a message. + * Sets the MIME type and name of the attachment, and initializes + * the disposition to ATTACHMENT. + * + * @param is the input stream to read from. + * @param type the MIME type of the attachment + * @param name the name of the attachment + */ + public void attach(ByteArrayInputStream is, + String type, + String name) + throws MessagingException { + attach(is, type, name, null, ATTACHMENT); + } + + /** + * Attaches the content from a ByteArrayInputStream to a message. + * Sets the MIME type, name, description and disposition of the + * attachment. + * + * @param is the input stream to read from. + * @param type the MIME type of the attachment + * @param name the name of the attachment + * @param description a description of the attachment + * @param disposition Mail.ATTACHMENT or Mail.INLINE + */ + public void attach(ByteArrayInputStream is, + String type, + String name, + String description, + String disposition) + throws MessagingException { + ByteArrayDataSource ds = new ByteArrayDataSource(is, type, name); + attach(ds, description, disposition); + } + + /** + * Attaches the content from a ByteArrayDataSource to a + * message. This is used internally by various other methods that + * take higher-level object types as input. The MIME type and + * name are determined directly from the dataSource. + * + * @param dataSource the data source to read from + * @param description a description of the attachment + * @param disposition Mail.ATTACHMENT or Mail.INLINE + */ + protected void attach(ByteArrayDataSource dataSource, + String description, + String disposition) + throws MessagingException { + MimeBodyPart part = new MimeBodyPart(); + attach(part); + + DataHandler dh = new DataHandler(dataSource); + + part.setDataHandler(dh); + part.setFileName(dataSource.getName()); + part.setDescription(description); + part.setDisposition(disposition); + } + + /** + * Attaches content to a message by supplying a DataHandler. All + * relevant parameters (MIME type, name, ...) are determined + * directly from the DataHandler. + * + * @param dh a DataHandler for some piece of content. + */ + public void attach(DataHandler dh) + throws MessagingException { + attach(dh, null, ATTACHMENT); + } + + /** + * Attaches content to a message by supplying a DataHandler. Sets + * the description and disposition of the content. + * + * @param dh the data source to read from + * @param description a description of the attachment + * @param disposition Mail.ATTACHMENT or Mail.INLINE + */ + public void attach(DataHandler dh, + String description, + String disposition) + throws MessagingException { + MimeBodyPart part = new MimeBodyPart(); + attach(part); + + part.setDataHandler(dh); + part.setFileName(dh.getName()); + part.setDescription(description); + part.setDisposition(disposition); + } + + /** + * Utility function that returns an appropriate Session object for + * sending mail. This uses the default properties from the Mail + * initializer and any of properties that can be overridden at + * the package level. + */ + static synchronized Session getSession() { + + if (s_session == null) { + + // Set up the properties + Properties props = new Properties(getConfig().getJavamail()); + + // Check for overrides of the server information + if (s_host != null) { + props.put("mail.smtp.host", s_host); + } + if (s_port != null) { + props.put("mail.smtp.port", s_port); + } + + // Set up the session + s_session = Session.getInstance(props, null); + s_session.setDebug(getConfig().isDebug()); + } + + return s_session; + } + + /** + * Utility function that returns the message part of the mail. + * Useful if you want to build a separate Transport process (for + * example, to queue a number of messages to send all at once rather + * than invoke the Mail.send() method for each instance.) + */ + private Message getMessage() + throws MessagingException { + // Create the message + MimeMessage msg = new MimeMessage(getSession()); + + msg.setFrom(m_from); + msg.setRecipients(Message.RecipientType.TO, m_filteredTo); + msg.setSentDate(new Date()); + + /** + * If no message ID is set then do not generate message-id header. + */ + if (m_messageID != null) { + msg.setMessageID("<" + m_messageID + ">"); + } + + if (m_cc != null) { + msg.setRecipients(Message.RecipientType.CC, m_cc); + } + + if (m_bcc != null) { + msg.setRecipients(Message.RecipientType.BCC, m_bcc); + } + + if (m_replyTo != null) { + msg.setReplyTo(m_replyTo); + } + + // Encode the subject + String enc_subj; + try { + enc_subj = MimeUtility.encodeText(m_subject, m_encoding, null); + } catch (UnsupportedEncodingException uee) { + s_log.warn("unable to encode subject: " + uee); + enc_subj = m_subject; + } + msg.setSubject(enc_subj); + + // Encode the MIME headers + if (m_headers != null) { + Enumeration e = m_headers.keys(); + while (e.hasMoreElements()) { + String name = (String) e.nextElement(); + String value = (String) m_headers.get(name); + String enc_v; + try { + enc_v = MimeUtility.encodeText(value, m_encoding, null); + } catch (UnsupportedEncodingException uee) { + s_log.warn("unable to encode header element: " + uee); + enc_v = value; + } + msg.addHeader(name, enc_v); + } + } + + // Return the Message object with it's content ready to go. + + return prepareMessageContent(msg); + + } + + /** + * Sets the host and port to connect to when sending + * mail. Package-level access (should only be used for testing). + * + * @param host the SMTP host to connect to + * @param port the port number on that host + */ + synchronized static void setSmtpServer(String host, String port) { + s_host = host; + s_port = port; + + // invalidate the current session object + s_session = null; + } + + /** + * Returns the SMTP mail host for debugging and account information. + * @return the SMTP mail host for debugging and account information. + */ + public synchronized static String getSmtpServer() { + return s_host; + } + + /** + * Writes the content of the message to the given output stream. + * Useful for debugging. + * + * @param os the output stream to write the message to + */ + public void writeTo(OutputStream os) + throws MessagingException { + try { + getMessage().writeTo(os); + } catch (IOException ex) { + s_log.error("writeTo output error", ex); + } + } + + /** + * Parses an address field. + */ + private static InternetAddress[] parseAddressField(String str) { + String[] addrList; + + if (str.indexOf(",") != -1) { + ArrayList a = new ArrayList(); + StringTokenizer st = new StringTokenizer(str, ",", false); + while (st.hasMoreTokens()) { + a.add(st.nextToken()); + } + + addrList = new String[a.size()]; + a.toArray(addrList); + } else { + addrList = new String[1]; + addrList[0] = str; + } + + return parseAddressList(addrList); + } + + /** + * Parses an address list. + */ + private static InternetAddress[] parseAddressList(String[] addrList) { + InternetAddress[] addrs = new InternetAddress[addrList.length]; + + for (int i = 0; i < addrList.length; i++) { + addrs[i] = parseAddress(addrList[i]); + } + return addrs; + } + + /** + * Parses an address. + */ + private static InternetAddress parseAddress(String str) { + String address = null; + String personal = null; + + InternetAddress addr = null; + + str = str.trim(); + + if (str.indexOf(" ") == -1) { + address = str; + } else { + int sp = str.lastIndexOf(" "); + personal = str.substring(0, sp); + address = str.substring(sp + 1); + } + + try { + addr = new InternetAddress(address, personal); + } catch (UnsupportedEncodingException e) { + s_log.error("unable to parse address: " + str); + } + + return addr; + } + + /** + * Parses a header field. + */ + private static Hashtable parseHeaderField(String str) { + String[] headerList; + + if (str.indexOf(",") != -1) { + ArrayList a = new ArrayList(); + StringTokenizer st = new StringTokenizer(str, ",", false); + while (st.hasMoreTokens()) { + a.add(st.nextToken()); + } + headerList = new String[a.size()]; + a.toArray(headerList); + } else { + headerList = new String[1]; + headerList[0] = str; + } + + return parseHeaderList(headerList); + } + + /** + * Parses a header list. + */ + private static Hashtable parseHeaderList(String[] headerList) { + Hashtable headers = new Hashtable(); + + for (int i = 0; i < headerList.length; i++) { + parseHeader(headerList[i], headers); + } + return headers; + } + + /** + * Parses a header. + */ + private static void parseHeader(String str, Hashtable headers) { + str = str.trim(); + + int sp = str.lastIndexOf(":"); + String name = str.substring(0, sp); + String value = (str.substring(sp + 1)).trim(); + + headers.put(name, value); + } + + /** + * Utility function to prepare the content of the message. + */ + private Message prepareMessageContent(MimeMessage msg) + throws MessagingException { + if (m_alternate == null && m_attachments == null) { + + // We have a plain-text message with no attachments. Use + // the Message.setText() method to initialize the content + // and leave the default MIME type alone. + msg.setText(m_body, m_encoding); + + } else { + + // For anything else the message will be a MIME multipart, + // with a subtype of of either "mixed" or "alternative" + // depending on whether we have attachments. + + String subtype = (m_attachments == null) ? ALTERNATIVE : MIXED; + + // Create a MIME multipart for the content. + + MimeMultipart mp = new MimeMultipart(subtype); + msg.setContent(mp); + + // Next we need to look at whether the message part of the + // content is going to be text/plain or text/html. + + MimeBodyPart part = new MimeBodyPart(); + + if (m_alternate == null) { + + // No alternate, so it must be text/plain with + // attachments. + + part.setText(m_body, m_encoding); + part.setHeader(CONTENT_TYPE, MessageType.TEXT_PLAIN); + mp.addBodyPart(part); + + } else { + + // We have an alternate form, so we supply the body as + // the first part and the alternate as the second. + // The overall MIME subtype is probably ALTERNATE + // (depending on whether we have attachments). + + part.setText(m_body, m_encoding); + part.setHeader(CONTENT_TYPE, MessageType.TEXT_HTML); + mp.addBodyPart(part); + + + part = new MimeBodyPart(); + part.setText(m_alternate, m_encoding); + part.setHeader(CONTENT_TYPE, MessageType.TEXT_PLAIN); + mp.addBodyPart(part); + + } + + // Do we have attachments? If so then the MIME subtype + // must be MIXED and and the attachments need to be + // transferred to the Message. + + if (m_attachments != null) { + + // Add attachments to the Message content. + + for (int i = 0; i < m_attachments.getCount(); i++) { + mp.addBodyPart(m_attachments.getBodyPart(i)); + } + } + } + + // Save changes to the message. This will update the MIME + // headers so the message is ready to send. + + msg.saveChanges(); + + return msg; + } + + /** + * + */ + private InternetAddress[] filterRecipients() { + ArrayList filtered = new ArrayList(); + ArrayList invalid = new ArrayList(); + for (int i = 0; i < m_to.length; i++) { + Iterator it = s_invalidDomains.iterator(); + boolean isValid = true; + while (it.hasNext()) { + if (m_to[i].getAddress().endsWith((String) it.next())) { + isValid = false; + break; + } + } + if (isValid) { + filtered.add(m_to[i]); + } else { + invalid.add(m_to[i]); + s_log.debug("filtering message to non-existent email address " + + m_to[i]); + } + } + m_filteredTo = (InternetAddress[]) filtered.toArray(new InternetAddress[filtered. + size()]); + m_invalidTo = (InternetAddress[]) invalid.toArray(new InternetAddress[invalid. + size()]); + return m_filteredTo; + } +} diff --git a/ccm-core/src/main/java/com/arsdigita/mail/MimeMessage.java b/ccm-core/src/main/java/com/arsdigita/mail/MimeMessage.java new file mode 100644 index 000000000..92f057603 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/mail/MimeMessage.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.mail; + +// JavaMail API +import javax.mail.Session; +import javax.mail.MessagingException; + +/** + * Extends the standard MimeMessage to supply the correct value for + * MessageID on all outbound email. + * + * @version $Id$ + */ + +final class MimeMessage extends javax.mail.internet.MimeMessage { + + // Constants + + private static final String MESSAGE_ID = "Message-ID"; + + private String m_messageID; + + /** + * Default constructor. + */ + + public MimeMessage (Session session) { + super(session); + } + + /** + * Called by the saveChanges() method to update the MIME headers. + */ + + protected void updateHeaders() + throws MessagingException + { + super.updateHeaders(); + + if (m_messageID != null) { + try { + setHeader(MESSAGE_ID, m_messageID); + } catch (MessagingException mex) { + // ignore + } + } + } + + public void setMessageID(String messageID) { + m_messageID = messageID; + } + +} diff --git a/ccm-core/src/main/java/com/arsdigita/templating/ApplicationOIDPatternGenerator.java b/ccm-core/src/main/java/com/arsdigita/templating/ApplicationOIDPatternGenerator.java index f0f8efd1e..833526d19 100755 --- a/ccm-core/src/main/java/com/arsdigita/templating/ApplicationOIDPatternGenerator.java +++ b/ccm-core/src/main/java/com/arsdigita/templating/ApplicationOIDPatternGenerator.java @@ -21,7 +21,7 @@ package com.arsdigita.templating; import com.arsdigita.web.Web; -import org.libreccm.web.Application; +import org.libreccm.web.CcmApplication; import javax.servlet.http.HttpServletRequest; @@ -48,7 +48,7 @@ public class ApplicationOIDPatternGenerator implements PatternGenerator { public String[] generateValues(String key, HttpServletRequest req) { - final Application application = Web.getWebContext().getApplication(); + final CcmApplication application = Web.getWebContext().getApplication(); if (application != null) { String[] oid = new String[1]; diff --git a/ccm-core/src/main/java/com/arsdigita/templating/ApplicationPatternGenerator.java b/ccm-core/src/main/java/com/arsdigita/templating/ApplicationPatternGenerator.java index 9d9613643..6204b570b 100755 --- a/ccm-core/src/main/java/com/arsdigita/templating/ApplicationPatternGenerator.java +++ b/ccm-core/src/main/java/com/arsdigita/templating/ApplicationPatternGenerator.java @@ -26,8 +26,7 @@ import com.arsdigita.web.Web; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; -import org.libreccm.web.Application; -import org.libreccm.web.ApplicationType; +import org.libreccm.web.CcmApplication; /** * Generates a set of pattern values based on the application key, eg @@ -41,7 +40,7 @@ public class ApplicationPatternGenerator implements PatternGenerator { private static final Logger s_log = Logger.getLogger(PatternGenerator.class); /** - * Implementation iof the Interface class. + * Implementation of the Interface class. * * @param key * @param req @@ -54,10 +53,9 @@ public class ApplicationPatternGenerator implements PatternGenerator { s_log.debug("Processing Application with key: " + key); - final Application app = Web.getWebContext().getApplication(); + final CcmApplication app = Web.getWebContext().getApplication(); if (app != null) { - String[] returnValue = {((ApplicationType) app.getResourceType()) - .getTitle()}; + String[] returnValue = {app.getApplicationType()}; s_log.debug("Found application >>" + returnValue + "<< in Application."); return returnValue; diff --git a/ccm-core/src/main/java/com/arsdigita/templating/URLPatternGenerator.java b/ccm-core/src/main/java/com/arsdigita/templating/URLPatternGenerator.java index a9af92173..c45462459 100755 --- a/ccm-core/src/main/java/com/arsdigita/templating/URLPatternGenerator.java +++ b/ccm-core/src/main/java/com/arsdigita/templating/URLPatternGenerator.java @@ -27,7 +27,7 @@ import com.arsdigita.web.Web; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; -import org.libreccm.web.Application; +import org.libreccm.web.CcmApplication; /** @@ -155,7 +155,7 @@ public class URLPatternGenerator implements PatternGenerator { private String getBasePath() { // retrieve the application of the request - Application app = Web.getWebContext().getApplication(); + CcmApplication app = Web.getWebContext().getApplication(); if (app == null) { return "/"; } else { diff --git a/ccm-core/src/main/java/com/arsdigita/templating/WebAppPatternGenerator.java b/ccm-core/src/main/java/com/arsdigita/templating/WebAppPatternGenerator.java index 0e12ae5f5..a1edeff9c 100755 --- a/ccm-core/src/main/java/com/arsdigita/templating/WebAppPatternGenerator.java +++ b/ccm-core/src/main/java/com/arsdigita/templating/WebAppPatternGenerator.java @@ -23,7 +23,7 @@ import com.arsdigita.web.Web; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; -import org.libreccm.web.Application; +import org.libreccm.web.CcmApplication; /** * Generates a set of patterns corresponding to the current web application @@ -52,7 +52,7 @@ public class WebAppPatternGenerator implements PatternGenerator { public String[] generateValues(String key, HttpServletRequest req) { - Application app = Web.getWebContext().getApplication(); + CcmApplication app = Web.getWebContext().getApplication(); String ctx = (app == null) ? null : ""; if (app == null || ctx == null || "".equals(ctx)) { diff --git a/ccm-core/src/main/java/com/arsdigita/ui/UI.java b/ccm-core/src/main/java/com/arsdigita/ui/UI.java new file mode 100644 index 000000000..97dd4bd20 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/ui/UI.java @@ -0,0 +1,233 @@ +/* + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +package com.arsdigita.ui; + +import com.arsdigita.ui.login.LoginServlet; +import javax.servlet.http.HttpServletRequest; + +import org.apache.log4j.Logger; + +/** + *
A central location for commonly used UI services and their accessories.
+ * + * + * @author pb + */ +public abstract class UI { + + /** Private loggin instance. */ + private static final Logger s_log = Logger.getLogger(UI.class); + + /** The UI XML namespace. */ + public static final String UI_XML_NS = "http://www.arsdigita.com/ui/1.0"; + + private static final UIConfig s_config = UIConfig.getConfig(); + + /** URL for systems public top level page (entry or start page). */ + private static final String s_rootPageURL = s_config.getRootPage(); + /** URL to page a user should be redirected to after login. */ + private static final String s_userRedirectURL = s_config.getUserRedirect(); + /** (Absolute) URL for workspace page. */ + private static final String s_workspaceURL = s_config.getWorkspace(); + + + /** + * Provides a handle to the UI config record. + * + * @return Instance of UIConfig + */ + public static UIConfig getConfig() { + return s_config; + } + + /** + * Provides an absolute URL (leading slash) into the system top-level page + * (entry page / start page). It is relative to document root without any + * constant prefix if there is one configured. + * + * Method is typically called by servlets and especially by JSP's. + * + * Currently just a wrapper script to getRootPageURL() because req is + * currently ignored. + * + * @param req HttpServletRequest, may be used to determin the context of + * the current thread (application), currently not used and + * introduced here for backwards compatibility + * + * @return URL for top-level page as String + */ + public static String getRootPageURL(HttpServletRequest req) { + return getRootPageURL(); + } + + /** + * Provides an absolute URL (leading slash) into the system top-level page + * (entry page / start page). It is relative to document root without any + * constant prefix if there is one configured. + * + * Method is typically called by servlets and especially by JSP's. + * + * @return URL for top-level page as String + */ + public static String getRootPageURL() { + return s_rootPageURL; + } + + /** + * Provides the absolute URL of a page, which redirects an incomming request + * based on some clients property, usually whether the user is logged in, + * either to a general public page or to a user (client) specific page. + * + * It is relative to document root including leading slash but without any + * constant prefix if there is one configured. + * + * It is typically used after login to redirect to an appropriate page, by + * default to /peremissions/. A site should configure an application + * to their specific purposes. e.g ccm-cms provides a page + * content-center/redirect.jsp which redirects a user to the content-center + * if logged in. + * + * @return full URL of a user redirect page, may be null + */ + public static String getUserRedirectURL() { + return s_userRedirectURL; + } + + /** + * Wrapper method for {@see getUserRedirectURL()} + * which redirects a user to the content-center if logged in. + * + * Method is specifically used by various redirect JSP's. + * + * @param req HttpServletRequest, may be used to determin the context of + * the current thread (application), currently not used and + * introduced here for backwards compatibility + * @return full URL of a user redirect page, may be null + */ + public static String getUserRedirectURL(HttpServletRequest req) { + return getUserRedirectURL(); + } + + /** + * Provides the absolute URL for the system workspace page. It is relative + * to document root including leading slash but without any constant prefix + * if there is one configured. + * + * It is used by the user redirection page (see above) as redirection target + * if no logged in user exists or no user specific page is configured. By + * default it is configured as "pvt/" as well. An installation usually + * defines a different page according to their specific purposes, e.g. + * portal or navigation. + * + * @return URL for workspace page as String + */ + public static String getWorkspaceURL() { + return s_workspaceURL; + } + + /** + * Provides the absolute URL for the system workspace page. + * It is relative to document root including leading slash but without any + * constant prefix if there is one configured. + * + * Method is typically called by servlets and especially by JSP's. + * + * Currently just a wrapper script to getWorkspaceURL() because req is + * currently ignored. + * + * @param req HttpServletRequest, may be used to determin the context of + * the current thread (application), currently not used and + * introduced here for backwards compatibility + * + * @return URL for workspace page as String + */ + // In old LegacyInitializer + // WORKSPACE_PAGE_KEY = page.kernel.workspace=pvt/ (mod- t0 portal/nav) + public static String getWorkspaceURL(HttpServletRequest req) { + return getWorkspaceURL(); + } + + + // //////////////////////////////////////////////////////////////////////// + // Various deprfecated methods, to be removed as soon as invoking code is + // refactored. + // //////////////////////////////////////////////////////////////////////// + + + /** + * Provides an absolute URL (leading slash) into the system login page. + * It is relative to document root without any constant prefix if there is + * one configured. + * + * @return URL for login page as String + * @deprecated use Login.getLoginPageURL()() instead + */ + public static String getLoginPageURL() { + return LoginServlet.LOGIN_PAGE_URL; + } + + /** + * Provides an absolute URL (leading slash) for a cookie explanation page. + * It is relative to document root without any constant prefix if there is + * one configured. + * + * @return url String for new user registration page as String + * @deprecated use LoginServlet.getCookiesExplainPageURL() instead + */ + public static String getCookiesExplainPageURL() { + return LoginServlet.getCookiesExplainPageURL(); + } + + /** + * Provides an absolute URL (leading slash) for a password recovery page. + * It is relative to document root without any constant prefix if there is + * one configured. + * + * @return url String for new user registration page as String + * @deprecated use LoginServlet.getRecoverPasswordPageURL() instead + */ + public static String getRecoverPasswordPageURL() { + return LoginServlet.getRecoverPasswordPageURL(); + } + + /** + * Provides an absolute URL (leading slash) to a user profile editing page. + * It is relative to document root without any constant prefix if there is + * one configured. + * + * @return url String for new user registration page as String + * @deprecated use LoginServlet.getEditUserProfilePageURL() instead + */ + public static String getEditUserProfilePageURL() { + return LoginServlet.getEditUserProfilePageURL(); + } + + /** + * Provides an absolute URL (leading slash) for the system logout page. It + * is relative to document root without any constant prefix if there is one + * configured. + * + * @return URL for logout page as String + * @deprecated use LoginServlet.getLogoutPageURL() instead + */ + public static String getLogoutPageURL() { + return LoginServlet.getLogoutPageURL(); + } + +} diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/ChangePasswordForm.java b/ccm-core/src/main/java/com/arsdigita/ui/login/ChangePasswordForm.java new file mode 100644 index 000000000..ee5e6bf75 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/ChangePasswordForm.java @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.ui.login; + +import com.arsdigita.bebop.BoxPanel; +import com.arsdigita.bebop.ColumnPanel; +import com.arsdigita.bebop.Container; +import com.arsdigita.bebop.Form; +import com.arsdigita.bebop.FormData; +import com.arsdigita.bebop.FormProcessException; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.event.FormProcessListener; +import com.arsdigita.bebop.event.FormValidationListener; +import com.arsdigita.bebop.form.Hidden; +import com.arsdigita.bebop.form.Password; +import com.arsdigita.bebop.form.Submit; +import com.arsdigita.bebop.parameters.NotNullValidationListener; +import com.arsdigita.bebop.parameters.StringParameter; +import com.arsdigita.bebop.parameters.URLParameter; +import com.arsdigita.kernel.KernelConfig; +import com.arsdigita.kernel.security.SecurityConfig; +import com.arsdigita.ui.UI; +import com.arsdigita.web.Web; +import com.arsdigita.web.URL; +import com.arsdigita.web.ReturnSignal; +import com.arsdigita.mail.Mail; +import com.arsdigita.util.UncheckedWrapperException; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.log4j.Logger; +import org.libreccm.cdi.utils.CdiLookupException; +import org.libreccm.cdi.utils.CdiUtil; +import org.libreccm.core.CcmSessionContext; +import org.libreccm.core.Subject; +import org.libreccm.core.User; +import org.libreccm.core.UserManager; +import org.libreccm.core.UserRepository; +import org.libreccm.core.authentication.LoginManager; + +import java.util.logging.Level; + +/** + * A Form that allows a user to change their password by entering their old + * password, a new password, and a confirmation of their new password. Requires + * that the user is logged in. Requires that new password differ from old and + * meet strong password requirements. If the user is recovering from a lost + * password (UserContext.isRecovering() is true), does not require or display + * the old password parameter and does not require that new password differ from + * old. Mails the user to notify of password change. Redirects user to workspace + * or return_url if set. + * + * @author Sameer Ajmani + * + */ +public class ChangePasswordForm extends Form + implements FormProcessListener, + FormValidationListener { + + private static final Logger s_log = Logger.getLogger( + ChangePasswordForm.class.getName()); + final static String CHANGE_PASSWORD_FORM_NAME = "change-password"; + final static String OLD_PASSWORD_PARAM_NAME = "old-password"; + final static String NEW_PASSWORD_PARAM_NAME = "new-password"; + final static String CONFIRM_PASSWORD_PARAM_NAME = "confirm-password"; + final static String RETURN_URL_PARAM_NAME + = LoginHelper.RETURN_URL_PARAM_NAME; + private final UserAuthenticationListener m_listener + = new UserAuthenticationListener(); + private Hidden m_returnURL; +// private Hidden m_recovery; + private Label m_oldPasswordLabel; + private Password m_oldPassword; + private Password m_newPassword; + private Password m_confirmPassword; + + public ChangePasswordForm() { + this(new BoxPanel()); + } + + @Override + public void register(final Page page) { + super.register(page); + page.addRequestListener(m_listener); + page.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent event) { + PageState state = event.getPageState(); + m_oldPasswordLabel.setVisible(state, true); + m_oldPassword.setVisible(state, true); + } + + }); + } + + public ChangePasswordForm(Container panel) { + super(CHANGE_PASSWORD_FORM_NAME, panel); + + setMethod(Form.POST); + addValidationListener(this); + addProcessListener(this); + + // save the recovery credential as a hidden parameter +// m_recovery = new Hidden(new StringParameter(RecoveryLoginModule.getParamName())); +// m_recovery.setPassIn(true); +// add(m_recovery); + // save the return URL as a hidden parameter + m_returnURL = new Hidden(new URLParameter(RETURN_URL_PARAM_NAME)); + m_returnURL.setPassIn(true); + add(m_returnURL); + + final CdiUtil cdiUtil = new CdiUtil(); + final CcmSessionContext sessionContext; + try { + sessionContext = cdiUtil.findBean(CcmSessionContext.class); + } catch (CdiLookupException ex) { + throw new UncheckedWrapperException(""); + } + final Subject subject = sessionContext.getCurrentSubject(); + if (subject != null && subject instanceof User) { + final User user = (User) subject; + final Label greeting = new Label( + LoginHelper.getMessage( + "login.changePasswortForm.greeting", + new Object[]{String.format("%s %s", + user.getName().getGivenName(), + user.getName().getFamilyName())})); + greeting.setFontWeight(Label.BOLD); + greeting.setClassAttr("greeting"); + add(greeting); + } + + add(new Label(LoginHelper.getMessage( + "login.changePasswortForm.introText"))); + + // old password + m_oldPasswordLabel = new Label(LoginHelper.getMessage( + "login.changePasswordForm.oldPasswordLabel")); + add(m_oldPasswordLabel); + m_oldPassword = new Password(OLD_PASSWORD_PARAM_NAME); + // don't use NotNullValidationListener because + // old password may be null during password recovery + add(m_oldPassword); + + // new password + Object[] params = new Object[]{PasswordValidationListener.MIN_LENGTH}; + add(new Label(LoginHelper.getMessage( + "login.changePasswordForm.newPasswordLabel", params))); + m_newPassword = new Password(NEW_PASSWORD_PARAM_NAME); + m_newPassword.addValidationListener(new PasswordValidationListener()); + add(m_newPassword); + + // confirm new password + add(new Label(LoginHelper.getMessage( + "login.changePasswordForm.confirmPasswordLabel"))); + m_confirmPassword = new Password(CONFIRM_PASSWORD_PARAM_NAME); + // don't use PasswordValidationListener to avoid duplicate errors + m_confirmPassword.addValidationListener(new NotNullValidationListener()); + add(m_confirmPassword); + + // submit + add(new Submit(LoginHelper.getMessage("login.changePasswordForm.submit")), + ColumnPanel.CENTER | ColumnPanel.FULL_WIDTH); + } + + @Override + public void validate(final FormSectionEvent event) + throws FormProcessException { + PageState state = event.getPageState(); + FormData data = event.getFormData(); + try { + // get user object + if (!m_listener.isLoggedIn(state)) { + // this error should never appear + data.addError(LoginHelper.localize( + "login.changePasswordForm.noUserError", + state.getRequest())); + return; + } + User user = m_listener.getUser(state); + + // get parameter values + String oldPassword = (String) m_oldPassword.getValue(state); + String newPassword = (String) m_newPassword.getValue(state); + String confirmPassword = (String) m_confirmPassword.getValue(state); + + // check old password unless recovering + try { + // The old password can never be null or contain leading or + // trailing slashes. + if (oldPassword == null + || !oldPassword.trim().equals(oldPassword)) { + data.addError(OLD_PASSWORD_PARAM_NAME, LoginHelper + .localize( + "login.changePasswordForm.badPasswordError", + state.getRequest())); + return; + } + + final CdiUtil cdiUtil = new CdiUtil(); + final UserManager userManager = cdiUtil.findBean( + UserManager.class); + if (!userManager.verifyPasswordForUser( + user, oldPassword)) { + data.addError(OLD_PASSWORD_PARAM_NAME, + LoginHelper.localize( + "login.changePasswordForm.badPasswordError", + state.getRequest())); + return; + } + } catch (CdiLookupException ex) { + throw new UncheckedWrapperException( + "Failed to lookup UserManager", ex); + } + + // check new password + if (newPassword.equals(oldPassword)) { + data.addError(NEW_PASSWORD_PARAM_NAME, LoginHelper.localize( + "login.changePasswordForm.mustDifferError", + state.getRequest())); + return; + } + if (!newPassword.equals(confirmPassword)) { + data.addError(CONFIRM_PASSWORD_PARAM_NAME, LoginHelper.localize( + "login.changePasswordForm.mustMatchError", + state.getRequest())); + return; + } + } finally { + if (!data.isValid()) { + // clear passwords from form data + m_oldPassword.setValue(state, ""); + m_newPassword.setValue(state, ""); + m_confirmPassword.setValue(state, ""); + } + } + } + + @Override + public void process(final FormSectionEvent event) + throws FormProcessException { + PageState state = event.getPageState(); + FormData data = event.getFormData(); + + // get user object + if (!m_listener.isLoggedIn(state)) { + // this error should never appear (checked in validate) + data.addError(LoginHelper.localize( + "login.changePasswordForm.noUserError", + state.getRequest())); + return; + } + User user = m_listener.getUser(state); + + // set new password + try { + final CdiUtil cdiUtil = new CdiUtil(); + final UserManager userManager = cdiUtil.findBean(UserManager.class); + final UserRepository userRepository = cdiUtil.findBean( + UserRepository.class); + + String newPassword = (String) m_newPassword.getValue(state); + userManager.updatePassword(user, newPassword); + userRepository.save(user); + + s_log.debug("committing password change"); + } catch (CdiLookupException ex) { + throw new UncheckedWrapperException( + "Failed to lookup UserManager or UserRepository", ex); + } + + // mail report to user + if (!user.getEmailAddresses().isEmpty()) { + + final HttpServletRequest req = state.getRequest(); + + final String to = user.getEmailAddresses().get(0).getAddress(); + final String from = SecurityConfig.getConfig() + .getAdminContactEmail(); + final String name = user.getName().getGivenName(); + final String subject = LoginHelper.localize( + "login.changePasswordForm.mailSubject", req); + final String body = LoginHelper.localize( + "login.changePasswordForm.mailBody", + new Object[]{name}, + req); + + // try to send the message, but don't throw the exception + // if it fails so that the password change is comitted + // anyway. + try { + Mail.send(to, from, subject, body); + } catch (javax.mail.MessagingException e) { + s_log.error("Could not notify user of password change", e); + } + } else { + s_log.debug("Could not notify user of password change: " + + "null email, user ID: " + + user.getSubjectId()); + } + + final HttpServletRequest req = state.getRequest(); + + final String path = UI.getWorkspaceURL(req); + + final URL fallback = URL.there(req, path); + + throw new ReturnSignal(req, fallback); + } + +} diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/DynamicLink.java b/ccm-core/src/main/java/com/arsdigita/ui/login/DynamicLink.java new file mode 100644 index 000000000..cb68b3bc1 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/DynamicLink.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.ui.login; + +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Link; +import com.arsdigita.bebop.event.PrintEvent; +import com.arsdigita.bebop.event.PrintListener; +// import com.arsdigita.kernel.security.LegacyInitializer; + +/** + * Package-private class that generates the URL for a link dynamically from + * the kernel page map. This class will be removed or changes when the page + * map is replaced by package parameters. + * + * 2011-02-04: API change (pboy) + * The page map is no retrieved from a set of parameters. The target is now a + * String representation of the absolut url (leading slash) relativ to + * document root. The target is now a targetUrl, no longer a targetKey. + * + * @author Sameer Ajmani + * @version $Id$ + */ +class DynamicLink extends Link { + + DynamicLink(final String labelKey, final String targetUrl) { + + super(new Label(LoginHelper.getMessage(labelKey)), + new PrintListener() { + public void prepare(PrintEvent e) { + Link link = (Link) e.getTarget(); + + // see {@link com.arsdigita.bebopLink#Link(String,URL)} + // Url is now expected without leading context wich is handled + // by the new dispatcher. Therefore the req. is not needed. + // anymore. + // String url = LegacyInitializer.getFullURL + // (targetKey, e.getPageState().getRequest()); + + link.setTarget(targetUrl); + } + }); + } +} diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/EmailInitListener.java b/ccm-core/src/main/java/com/arsdigita/ui/login/EmailInitListener.java new file mode 100644 index 000000000..4eb8a2aeb --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/EmailInitListener.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.ui.login; + +import com.arsdigita.web.Web; +import com.arsdigita.bebop.FormData; +import com.arsdigita.bebop.event.FormInitListener; +import com.arsdigita.bebop.event.FormSectionEvent; +import com.arsdigita.bebop.parameters.EmailParameter; + +import javax.mail.internet.AddressException; +import javax.mail.internet.InternetAddress; + +import org.apache.log4j.Logger; +import org.libreccm.core.CcmSessionContext; +import org.libreccm.core.User; + +/** + * Initializes the value of the given parameter to the current user's email + * address. If the user is not logged in or the email address is invalid, the + * parameter is not modified. + * + * @author Sameer Ajmani + * + */ +public class EmailInitListener implements FormInitListener { + + private static final Logger s_log = Logger.getLogger(EmailInitListener.class + .getName()); + + private EmailParameter m_param; + + public EmailInitListener(EmailParameter param) { + m_param = param; + } + + public void init(FormSectionEvent event) { + FormData data = event.getFormData(); + + s_log.debug("START"); + + final CcmSessionContext ctx = Web.getUserContext(); + + if (!ctx.isLoggedIn()) { + s_log.debug("FAILURE not logged in"); + return; + } + + User user = (User) ctx.getCurrentSubject(); + + if (user == null) { + s_log.debug("FAILURE no such user"); + return; + } + + if (user.getEmailAddresses().isEmpty() + || user.getEmailAddresses().get(0) == null) { + s_log.debug("FAILURE null primary email"); + return; + } + + if (user.getEmailAddresses().get(0).getAddress() == null + || user.getEmailAddresses().get(0).getAddress().isEmpty()) { + s_log.debug("FAILURE null email address"); + return; + } + + try { + InternetAddress addr = new InternetAddress(user.getEmailAddresses() + .get(0).getAddress()); + data.put(m_param.getName(), addr); + } catch (AddressException e) { + s_log.debug("FAILURE badly formed address"); + return; + } + + s_log.debug("SUCCESS"); + } + +} diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/LoginConstants.java b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginConstants.java new file mode 100644 index 000000000..2ceebd0a0 --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginConstants.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.ui.login; + +import com.arsdigita.globalization.GlobalizedMessage; + +/** + * Constants used by Login UI. + * + */ +public interface LoginConstants { + + public final static GlobalizedMessage SUBMIT = LoginHelper.getMessage( + "login.submit"); + public final static GlobalizedMessage PRIMARY_EMAIL = LoginHelper + .getMessage("login.primaryEmail"); + public final static GlobalizedMessage ADDITIONAL_EMAIL = LoginHelper + .getMessage("login.additionalEmail"); + public final static GlobalizedMessage SCREEN_NAME = LoginHelper.getMessage( + "login.screenName"); + public final static GlobalizedMessage FIRST_NAME = LoginHelper.getMessage( + "login.firstName"); + public final static GlobalizedMessage LAST_NAME = LoginHelper.getMessage( + "login.lastName"); + public final static GlobalizedMessage PASSWORD = LoginHelper.getMessage( + "login.password", new Object[]{ + PasswordValidationListener.MIN_LENGTH}); + public final static GlobalizedMessage PASSWORD_CONFIRMATION = LoginHelper + .getMessage("login.passwordConfirm"); + public final static GlobalizedMessage PASSWORD_QUESTION = LoginHelper + .getMessage("login.passwordQuestion"); + public final static GlobalizedMessage PASSWORD_ANSWER = LoginHelper + .getMessage("login.passwordAnswer"); + public final static GlobalizedMessage URL = LoginHelper.getMessage( + "login.url"); + public final static GlobalizedMessage BIO = LoginHelper.getMessage( + "login.bio"); + + public final static GlobalizedMessage ERROR_DUPLICATE_SN = LoginHelper + .getMessage("login.error.duplicateScreenName"); + public final static GlobalizedMessage ERROR_DUPLICATE_EMAIL = LoginHelper + .getMessage("login.error.duplicateEmail"); + public final static GlobalizedMessage ERROR_MISMATCH_PASSWORD = LoginHelper + .getMessage("login.error.mismatchPassword"); + public final static GlobalizedMessage ERROR_BAD_PASSWORD = LoginHelper + .getMessage("login.error.badPassword"); + + public final static GlobalizedMessage ERROR_LOGIN_FAIL = LoginHelper + .getMessage("login.error.loginFail"); + + public final static GlobalizedMessage ERROR_BAD_ANSWER = LoginHelper + .getMessage("login.error.badAnswer"); + public final static GlobalizedMessage ERROR_BAD_EMAIL = LoginHelper + .getMessage("login.error.badEmail"); + public final static GlobalizedMessage ERROR_BANNED_EMAIL = LoginHelper + .getMessage("login.error.bannedEmail"); + + public final static String FORM_EMAIL = "emailAddress"; + public final static String FORM_SCREEN_NAME = "screenName"; + + // Should not really be named email. Kept this way due to external tests + // depending on this value. + public final static String FORM_LOGIN = "email"; + + public final static String FORM_ADDITIONAL_EMAIL = "additional_email"; + public final static String FORM_FIRST_NAME = "firstname"; + public final static String FORM_LAST_NAME = "lastname"; + public final static String FORM_PASSWORD = "password"; + public final static String FORM_PASSWORD_CONFIRMATION + = "password_confirmation"; + public final static String FORM_PASSWORD_QUESTION = "question"; + public final static String FORM_PASSWORD_ANSWER = "answer"; + public final static String FORM_URL = "url"; + public final static String FORM_URL_DEFAULT = "http://"; + public final static String FORM_BIO = "biography"; + public final static String FORM_TIMESTAMP = "timestamp"; + public final static String FORM_PERSISTENT_LOGIN_P = "persistentCookieP"; + public final static String FORM_PERSISTENT_LOGIN_P_DEFAULT = "1"; + + public final static int TIMESTAMP_LIFETIME_SECS = 300; + public final static int MAX_NAME_LEN = 60; + +} diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/LoginGlobalizationUtil.java b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginGlobalizationUtil.java new file mode 100644 index 000000000..43a75c22c --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginGlobalizationUtil.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +package com.arsdigita.ui.login; + +import com.arsdigita.globalization.Globalized; +import com.arsdigita.globalization.GlobalizedMessage; + +/** + * Compilation of methods to simplify the handling of globalizing keys. + * Basically it adds the name of package's resource bundle files to the + * globalize methods and forwards to GlobalizedMessage, shortening the + * method invocation in the various application classes. + * + * @author tosmers + * @version $Revision$ $Date$ + */ +public class LoginGlobalizationUtil implements Globalized { + + /** Name of Java resource files to handle CMS's globalisation. */ + private static final String BUNDLE_NAME = "com.arsdigita.ui.admin.LoginResources"; + + /** + * Returns a globalized message using the package specific bundle, + * provided by BUNDLE_NAME. + * @param key + * @return + */ + public static GlobalizedMessage globalize(String key) { + return new GlobalizedMessage(key, BUNDLE_NAME); + } + + /** + * Returns a globalized message object, using the package specific bundle, + * as specified by BUNDLE_NAME. Also takes in an Object[] of arguments to + * interpolate into the retrieved message using the MessageFormat class. + * @param key + * @param args + * @return + */ + public static GlobalizedMessage globalize(String key, Object[] args) { + return new GlobalizedMessage(key, BUNDLE_NAME, args); + } + + /** + * Returns the name of the package specific resource bundle. + * + * @return Name of resource bundle as String + */ + public static String getBundleName() { + return BUNDLE_NAME; + } + +} \ No newline at end of file diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/LoginHelper.java b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginHelper.java new file mode 100644 index 000000000..e4763b26c --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginHelper.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.ui.login; + +import com.arsdigita.bebop.PageState; +import com.arsdigita.dispatcher.DispatcherHelper; +import com.arsdigita.globalization.GlobalizedMessage; +import com.arsdigita.web.ReturnSignal; +import java.io.IOException; +import org.apache.log4j.Logger; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * Provides helper functions for the login UI. + * + * @author Sameer Ajmani + * @version $Id$ + */ +public class LoginHelper { + + private static final Logger s_log = Logger.getLogger(LoginHelper.class); + + public static final String RETURN_URL_PARAM_NAME = "return_url"; + + /** + * Returns the name of the login UI resource bundle + * + * @return the name of the login UI resource bundle + **/ + static String getBundleBaseName() { + return "com.arsdigita.ui.login.LoginResources"; + } + + /** + * Returns a new GlobalizedMessage constructed with the given + * parameters and the login UI resource bundle. + * + * @return a new GlobalizedMessage + **/ + static GlobalizedMessage getMessage(String key, Object[] args) { + return new GlobalizedMessage(key, getBundleBaseName(), args); + } + + /** + * Returns a new GlobalizedMessage constructed with the given + * parameters and the login UI resource bundle. + * + * @return a new GlobalizedMessage + **/ + static GlobalizedMessage getMessage(String key) { + return new GlobalizedMessage(key, getBundleBaseName()); + } + + /** + * Constructs a new GlobalizedMessage with the given parameters and the + * login UI resource bundle, then localizes the message with the given + * request. + * + * @return the localized String + **/ + static String localize(String key, Object[] args, + HttpServletRequest request) { + return (String)getMessage(key, args).localize(request); + } + + /** + * Constructs a new GlobalizedMessage with the given parameters and the + * login UI resource bundle, then localizes the message with the given + * request. + * + * @return the localized String + **/ + static String localize(String key, HttpServletRequest request) { + return (String)getMessage(key).localize(request); + } + + /** + * Redirect the client to the URL stored in the return_url request + * parameter, or, if that parameter is not set, redirect to the given + * default URL. + * + * @param state the current page state + * @param def the default URL + **/ + public static void doReturn(PageState state, String def) { + throw new ReturnSignal(state.getRequest(), def); + } + + /** + * Redirect the client to the given URL unless the response has + * already been committed. Wrapper for {@link + * #sendRedirect(HttpServletRequest, HttpServletResponse, String)} + * that pulls out the request and response from the PageState. + * + * @throws IOException if the redirect fails. + **/ + public static void sendRedirect(PageState state, String url) + throws IOException { + sendRedirect(state.getRequest(), state.getResponse(), url); + } + + /** + * Redirect the client to the given URL unless the response has already + * been committed. Aborts further request processing. + * + * @throws IOException if the redirect fails. + **/ + public static void sendRedirect(HttpServletRequest request, + HttpServletResponse response, + String url) + throws IOException { + + if (!response.isCommitted()) { + s_log.debug("Redirecting to: "+url); + DispatcherHelper.sendRedirect(request, response, url); + response.flushBuffer(); + DispatcherHelper.abortRequest(); + } else { + s_log.debug("Redirect failed because " + +"response already committed"); + } + } +} \ No newline at end of file diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/LoginServlet.java b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginServlet.java new file mode 100644 index 000000000..22f61005c --- /dev/null +++ b/ccm-core/src/main/java/com/arsdigita/ui/login/LoginServlet.java @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2012 Peter Boy All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +package com.arsdigita.ui.login; + +import com.arsdigita.bebop.Component; +import com.arsdigita.bebop.ElementComponent; +import com.arsdigita.bebop.Label; +import com.arsdigita.bebop.Page; +import com.arsdigita.bebop.PageFactory; +import com.arsdigita.bebop.PageState; +import com.arsdigita.bebop.SimpleContainer; +import com.arsdigita.bebop.event.ActionEvent; +import com.arsdigita.bebop.event.ActionListener; +import com.arsdigita.bebop.page.BebopApplicationServlet; +import com.arsdigita.dispatcher.DispatcherConfig; +import com.arsdigita.dispatcher.DispatcherHelper; +import com.arsdigita.kernel.security.SecurityConfig; +import com.arsdigita.ui.UI; +import com.arsdigita.web.ReturnSignal; +import com.arsdigita.web.URL; + +import org.apache.log4j.Logger; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServletRequest; + +/** + * Login Application Servlet class, central entry point to create and process + * the Login application UI. + * + * It manages user registration page, new user page, user workspace, logout, and + * permissions admin pages. + * + * It just defines a mapping URL <-> various pages and uses the super class to + * actually server the pages. Additionally is provides service methods to expose + * various properties, especially the URL's of public subpages (e.g. logout) and + * initializes the creation of the UI. + * + * @author Peter BoyEmailInitListener.
+ *
+ * @author Crag Wolfe
+ * @version $Id$
+ */
+public class ScreenNameInitListener implements FormInitListener {
+
+ private static Logger s_log =
+ Logger.getLogger(ScreenNameInitListener.class.getName());
+ private StringParameter m_param;
+
+ /**
+ *
+ * @param param
+ */
+ public ScreenNameInitListener(StringParameter param) {
+ m_param = param;
+ }
+
+ /**
+ *
+ * @param event
+ */
+ public void init(FormSectionEvent event) {
+ PageState state = event.getPageState();
+ FormData data = event.getFormData();
+ s_log.debug("START");
+ final CcmSessionContext ctx = Web.getUserContext();
+ if (!ctx.isLoggedIn()) {
+ s_log.debug("FAILURE not logged in");
+ return;
+ }
+ final User user = (User) ctx.getCurrentSubject();
+ if (user.getScreenName() == null) {
+ s_log.debug("FAILURE null screen name");
+ return;
+ }
+ data.put(m_param.getName(), user.getScreenName());
+ s_log.debug("SUCCESS");
+ }
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/UserAuthenticationListener.java b/ccm-core/src/main/java/com/arsdigita/ui/login/UserAuthenticationListener.java
new file mode 100644
index 000000000..0c7b887d5
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/ui/login/UserAuthenticationListener.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.login;
+
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.event.RequestEvent;
+import com.arsdigita.bebop.event.RequestListener;
+import com.arsdigita.kernel.security.Util;
+
+import com.arsdigita.web.Web;
+import com.arsdigita.web.LoginSignal;
+import com.arsdigita.util.UncheckedWrapperException;
+
+import org.apache.log4j.Logger;
+import org.libreccm.cdi.utils.CdiLookupException;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.core.CcmSessionContext;
+import org.libreccm.core.User;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * A RequestListener that redirects the user to register if not logged in. The
+ * redirection URL includes a return_url parameter to send the user back to this
+ * page after logging in. Pages must not continue processing if this listener
+ * redirects the user, since the response has already been committed
+ * (isLoggedIn() returns false in this case). In a future version, this listener
+ * will abort processing of the request if the user is not logged in.
+ *
+ * @author Phong Nguyen
+ * @author Sameer Ajmani
+ * @version 1.0
+ * @version $Id: UserAuthenticationListener.java 287 2005-02-22 00:29:02Z
+ * sskracic $
+ */
+public class UserAuthenticationListener implements RequestListener {
+
+ private static final Logger s_log = Logger.getLogger(
+ UserAuthenticationListener.class);
+
+ /**
+ * If the user is logged in, returns the User object.
+ *
+ * @param state
+ *
+ * @return the User object for the logged in user
+ *
+ * @throws IllegalStateException if user is not logged in. Call isLoggedIn()
+ * to check for this case.
+ */
+ public User getUser(final PageState state) {
+ if (!isLoggedIn(state)) {
+ throw new IllegalStateException("User is not logged in");
+ }
+
+ // Note: aborts processing with an internal error if user not logged in!
+ // Not suiteable just to check log in status.
+ final CdiUtil cdiUtil = new CdiUtil();
+ try {
+ final CcmSessionContext context = cdiUtil.findBean(
+ CcmSessionContext.class);
+
+ return (User) context.getCurrentSubject();
+ } catch (CdiLookupException ex) {
+ throw new UncheckedWrapperException(
+ "Failed get get CcmSessionContext.", ex);
+ }
+ }
+
+ /**
+ * Determines whether the user is logged in.
+ *
+ * @param state
+ *
+ * @return true if the user is logged in
+ */
+ public boolean isLoggedIn(final PageState state) {
+ return Web.getUserContext().isLoggedIn();
+ }
+
+ /**
+ * Checks whether the user is logged in. If not, redirects the client to the
+ * login page.
+ *
+ * @param event
+ */
+ @Override
+ public void pageRequested(final RequestEvent event) {
+ PageState state = event.getPageState();
+
+ final CcmSessionContext sessionContext;
+ try {
+ final CdiUtil cdiUtil = new CdiUtil();
+ sessionContext = cdiUtil.findBean(
+ CcmSessionContext.class);
+ } catch (CdiLookupException ex) {
+ throw new UncheckedWrapperException(
+ "Failed to lookup CcmSessionContext", ex);
+ }
+ if (!sessionContext.isLoggedIn()) {
+ s_log.debug("User is not logged in");
+ redirectToLoginPage(state);
+ }
+ }
+
+ /**
+ * Redirects the client to the login page.
+ *
+ * @param state
+ */
+ private void redirectToLoginPage(final PageState state) {
+ HttpServletRequest req = state.getRequest();
+ String urlBase = Util.getSecurityHelper().getLoginURL(req);
+
+ // first make sure we're not already looking at the login
+ // page -- if we are, don't redirect!
+ if (urlBase.equals(Web.getWebContext().getRequestURL().getRequestURI())) {
+ s_log.debug("preventing cyclic redirect to: " + urlBase);
+ // return without redirect
+ return;
+ }
+
+ throw new LoginSignal(req);
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/UserEditForm.java b/ccm-core/src/main/java/com/arsdigita/ui/login/UserEditForm.java
new file mode 100644
index 000000000..c00878c91
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/ui/login/UserEditForm.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.login;
+
+// import com.arsdigita.kernel.security.LegacyInitializer;
+import com.arsdigita.ui.UI;
+import com.arsdigita.web.URL;
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.FormData;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.RequestLocal;
+import com.arsdigita.bebop.event.FormProcessListener;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.form.Hidden;
+import com.arsdigita.bebop.parameters.URLParameter;
+import com.arsdigita.util.UncheckedWrapperException;
+import com.arsdigita.web.ReturnSignal;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.log4j.Logger;
+import org.libreccm.cdi.utils.CdiLookupException;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.core.EmailAddress;
+import org.libreccm.core.PersonName;
+import org.libreccm.core.User;
+import org.libreccm.core.UserRepository;
+
+import java.util.logging.Level;
+
+/**
+ * Edits a user. If returnURL is passed in to the form, then redirects to that
+ * URL; otherwise redirects to the user workspace.
+ *
+ *
+ * @author Sameer Ajmani
+ *
+ * @version $Id$
+ *
+ *
+ */
+public class UserEditForm extends UserForm
+ implements FormProcessListener {
+
+// private static final Logger s_log = Logger.getLogger(UserEditForm.class);
+ private UserAuthenticationListener m_listener
+ = new UserAuthenticationListener();
+ private final Hidden m_returnURL;
+ private final RequestLocal m_user = new RequestLocal() {
+
+ @Override
+ public Object initialValue(final PageState ps) {
+ User result;
+ final long userId = m_listener.getUser(ps).getSubjectId();
+ final CdiUtil cdiUtil = new CdiUtil();
+ final UserRepository userRepository;
+ try {
+ userRepository = cdiUtil.findBean(UserRepository.class);
+ } catch (CdiLookupException ex) {
+ throw new UncheckedWrapperException(
+ "Failed to lookup UserRepository.", ex);
+ }
+
+ result = userRepository.findById(userId);
+
+ return result;
+ }
+
+ };
+
+ public UserEditForm() {
+ super("user-edit", new ColumnPanel(2), false);
+
+ addProcessListener(this);
+
+ // export return URL
+ m_returnURL = new Hidden(new URLParameter(
+ LoginHelper.RETURN_URL_PARAM_NAME));
+ m_returnURL.setPassIn(true);
+ add(m_returnURL);
+ }
+
+ @Override
+ public void register(final Page page) {
+ super.register(page);
+ page.addRequestListener(m_listener);
+ }
+
+ @Override
+ protected User getUser(final PageState state) {
+ return (User) m_user.get(state);
+ }
+
+ @Override
+ public void process(final FormSectionEvent event)
+ throws FormProcessException {
+ FormData data = event.getFormData();
+ PageState state = event.getPageState();
+
+ User user = getUser(state);
+
+ if (user == null) {
+ throw new UncheckedWrapperException(
+ "Failed to retrieve user from page state");
+ }
+
+ final PersonName name = user.getName();
+ name.setGivenName((String) m_firstName.getValue(state));
+ name.setFamilyName((String) m_lastName.getValue(state));
+
+ user.setScreenName((String) m_screenName.getValue(state));
+
+ final EmailAddress newAddress = new EmailAddress();
+ newAddress.setAddress(data.get(FORM_EMAIL).toString());
+ if (user.getEmailAddresses().isEmpty()) {
+ user.addEmailAddress(newAddress);
+ } else {
+ if (!user.getEmailAddresses().get(0).equals(newAddress)) {
+ user.getEmailAddresses().get(0).setAddress(newAddress.getAddress());
+ }
+ }
+
+ final CdiUtil cdiUtil = new CdiUtil();
+ final UserRepository userRepository;
+ try {
+ userRepository = cdiUtil.findBean(UserRepository.class);
+ } catch (CdiLookupException ex) {
+ throw new UncheckedWrapperException(
+ "Failed to lookup UserRepository", ex);
+ }
+
+ // redirect to workspace or return URL, if specified
+ final HttpServletRequest req = state.getRequest();
+
+// final String path = LegacyInitializer.getFullURL
+// (LegacyInitializer.WORKSPACE_PAGE_KEY, req);
+ final String path = UI.getWorkspaceURL();
+
+ final URL fallback = com.arsdigita.web.URL.there(req, path);
+
+ throw new ReturnSignal(req, fallback);
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/UserForm.java b/ccm-core/src/main/java/com/arsdigita/ui/login/UserForm.java
new file mode 100644
index 000000000..9ffab3f9d
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/ui/login/UserForm.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.login;
+
+import com.arsdigita.bebop.ColumnPanel;
+import com.arsdigita.bebop.Container;
+import com.arsdigita.bebop.Form;
+import com.arsdigita.bebop.FormData;
+import com.arsdigita.bebop.FormProcessException;
+import com.arsdigita.bebop.FormSection;
+import com.arsdigita.bebop.Label;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.event.FormInitListener;
+import com.arsdigita.bebop.event.FormSectionEvent;
+import com.arsdigita.bebop.event.FormValidationListener;
+import com.arsdigita.bebop.form.Password;
+import com.arsdigita.bebop.form.Submit;
+import com.arsdigita.bebop.form.TextField;
+import com.arsdigita.bebop.parameters.EmailParameter;
+import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
+import com.arsdigita.bebop.parameters.StringLengthValidationListener;
+import com.arsdigita.bebop.parameters.StringParameter;
+import com.arsdigita.kernel.KernelConfig;
+import com.arsdigita.util.UncheckedWrapperException;
+
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+
+import org.apache.log4j.Logger;
+import org.libreccm.cdi.utils.CdiLookupException;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.core.PersonName;
+import org.libreccm.core.User;
+import org.libreccm.core.UserRepository;
+
+/**
+ * Common code for user new / add / edit forms.
+ *
+ * @author Admin UI Team
+ * @version $Id$
+ *
+ */
+public abstract class UserForm extends Form
+ implements LoginConstants, FormInitListener, FormValidationListener {
+
+ private static final Logger s_log = Logger.getLogger(UserForm.class
+ .getName());
+
+ private boolean m_newUser;
+
+ protected TextField m_firstName;
+ protected TextField m_lastName;
+ protected TextField m_email;
+ protected TextField m_screenName;
+ //protected TextField m_additional;
+ protected Password m_password;
+ protected Password m_confirm;
+ protected TextField m_question;
+ protected TextField m_answer;
+
+ protected Label m_securitySectionHeader = new Label(LoginHelper
+ .getMessage("login.userNewForm.securitySectionHeader"), false);
+ protected Label m_securityBlurb = new Label(LoginHelper
+ .getMessage("login.userNewForm.securityBlurb"));
+ protected Label m_passwordBlurb = new Label(LoginHelper
+ .getMessage("login.userNewForm.passwordBlurb"));
+ protected Label m_passwordLabel = new Label(PASSWORD);
+ protected Label m_confirmationLabel = new Label(PASSWORD_CONFIRMATION);
+ protected Label m_questionBlurb = new Label(LoginHelper
+ .getMessage("login.userNewForm.questionBlurb"));
+ protected Label m_questionLabel = new Label(PASSWORD_QUESTION);
+ protected Label m_answerLabel = new Label(PASSWORD_ANSWER);
+ protected PasswordValidationListener m_passwordValidationListener
+ = new PasswordValidationListener();
+ protected NotEmptyValidationListener m_confirmationNotEmptyValidationListener
+ = new NotEmptyValidationListener();
+ protected Submit m_submit = new Submit(SUBMIT);
+ protected Label m_firstNameLabel = new Label(FIRST_NAME);
+ protected Label m_lastNameLabel = new Label(LAST_NAME);
+ protected Label m_urlLabel = new Label(URL);
+ protected Label m_screenNameLabel = new Label(SCREEN_NAME);
+ protected Label m_emailLabel = new Label(PRIMARY_EMAIL);
+
+ protected Container m_profilePart = new FormSection();
+ protected Container m_securityPart = new FormSection();
+ protected Container m_submitPart = new FormSection();
+
+ /**
+ * Create a UserForm with the given name and panel.
+ *
+ */
+ public UserForm(String name, Container panel, boolean newUser) {
+ super(name, panel);
+
+ m_newUser = newUser;
+
+ setMethod(Form.POST);
+ addInitListener(this);
+ addValidationListener(this);
+
+ if (m_newUser) {
+ m_profilePart.add(new Label(LoginHelper
+ .getMessage("login.userNewForm.aboutYouSectionHeader"),
+ false), ColumnPanel.FULL_WIDTH);
+ }
+
+ // SDM #163373: add length checking for first/last names. We do
+ // this with both maximum length parameters in the user/add form and
+ // with validation of the value that come in for processing.
+ m_firstName = new TextField(new StringParameter(FORM_FIRST_NAME));
+ m_firstName.setMaxLength(MAX_NAME_LEN);
+ m_firstName.setSize(20);
+ m_firstName.addValidationListener(new NotEmptyValidationListener());
+ m_firstName.addValidationListener(new StringLengthValidationListener(
+ MAX_NAME_LEN));
+
+ m_profilePart.add(m_firstNameLabel);
+ m_profilePart.add(m_firstName);
+
+ m_lastName = new TextField(new StringParameter(FORM_LAST_NAME));
+ m_lastName.setMaxLength(MAX_NAME_LEN);
+ m_lastName.setSize(25);
+ m_lastName.addValidationListener(new NotEmptyValidationListener());
+ m_lastName.addValidationListener(new StringLengthValidationListener(
+ MAX_NAME_LEN));
+
+ m_profilePart.add(m_lastNameLabel);
+ m_profilePart.add(m_lastName);
+
+ m_profilePart.add(m_screenNameLabel);
+ m_screenName = new TextField(new StringParameter(FORM_SCREEN_NAME));
+ m_screenName.addValidationListener(new NotEmptyValidationListener());
+ m_profilePart.add(m_screenName);
+
+ // Primary email address
+ m_email = new TextField(new EmailParameter(FORM_EMAIL));
+ m_email.addValidationListener(new NotEmptyValidationListener());
+
+ m_profilePart.add(m_emailLabel);
+ m_profilePart.add(m_email);
+
+ // TODO: support additional emails
+ // Additional email addresses
+ //m_additional = new TextField(new EmailParameter
+ // (FORM_ADDITIONAL_EMAIL));
+ //add(new Label(ADDITIONAL_EMAIL));
+ //add(m_additional);
+ // SDM #162740: disable user bio for now, as there
+ // is no support for User Bio in the kernel level.
+ // add(new Label(BIO));
+ // TextArea bioText = new TextArea(new StringParameter(FORM_BIO));
+ // bioText.setCols(50);
+ // bioText.setRows(10);
+ // add(bioText);
+ // add(new Label(""));
+ if (m_newUser) {
+ m_securityPart.add(new Label(LoginHelper
+ .getMessage("login.userNewForm.securitySectionHeader"),
+ false), ColumnPanel.FULL_WIDTH);
+
+ m_securityPart.add(new Label(LoginHelper
+ .getMessage("login.userNewForm.securityBlurb")),
+ ColumnPanel.FULL_WIDTH);
+
+ m_securityPart.add(new Label(LoginHelper
+ .getMessage("login.userNewForm.passwordBlurb")),
+ ColumnPanel.FULL_WIDTH);
+
+ // Password
+ m_password = new Password(new StringParameter(FORM_PASSWORD));
+ m_password.addValidationListener(new PasswordValidationListener());
+
+ m_securityPart.add(m_passwordLabel);
+ m_securityPart.add(m_password);
+
+ // Password confirmation
+ m_confirm = new Password(new StringParameter(
+ FORM_PASSWORD_CONFIRMATION));
+ m_confirm.addValidationListener(new NotEmptyValidationListener());
+
+ m_securityPart.add(m_confirmationLabel);
+ m_securityPart.add(m_confirm);
+
+ m_securityPart.add(new Label(LoginHelper
+ .getMessage("login.userNewForm.questionBlurb")),
+ ColumnPanel.FULL_WIDTH);
+
+ // Password question
+ m_question = new TextField(new StringParameter(
+ FORM_PASSWORD_QUESTION));
+ m_question.setSize(30);
+ m_question.addValidationListener(new NotEmptyValidationListener());
+
+ m_securityPart.add(m_questionLabel);
+ m_securityPart.add(m_question);
+
+ // Password answer
+ m_answer = new TextField(new StringParameter(FORM_PASSWORD_ANSWER));
+ m_answer.setSize(30);
+ m_answer.addValidationListener(new NotEmptyValidationListener());
+
+ m_securityPart.add(m_answerLabel);
+ m_securityPart.add(m_answer);
+ }
+
+ // Submit
+ m_submitPart.add(m_submit, ColumnPanel.CENTER | ColumnPanel.FULL_WIDTH);
+
+ add(m_profilePart);
+ add(m_securityPart);
+ add(m_submitPart);
+ }
+
+ /**
+ * Initializes this form with data from the user.
+ *
+ * @param event
+ *
+ * @throws com.arsdigita.bebop.FormProcessException
+ *
+ */
+ @Override
+ public void init(FormSectionEvent event)
+ throws FormProcessException {
+ PageState state = event.getPageState();
+
+ User user = getUser(state);
+ if (user == null) {
+ throw new FormProcessException(LoginGlobalizationUtil.globalize(
+ "login.userForm.couldnt_load_user"));
+ }
+
+ PersonName name = user.getName();
+ m_firstName.setValue(state, name.getGivenName());
+ m_lastName.setValue(state, name.getFamilyName());
+
+ InternetAddress address;
+ try {
+ address = new InternetAddress(user.getEmailAddresses().get(0)
+ .toString());
+ } catch (AddressException e) {
+ String[] errorMsg = new String[1];
+ errorMsg[0] = user.getEmailAddresses().get(0).toString();
+ throw new FormProcessException(
+ "Email address is bad: " + user.getEmailAddresses().get(0),
+ LoginHelper.getMessage("login.error.badEmail", errorMsg)
+ );
+ }
+
+ m_email.setValue(state, address);
+ m_screenName.setValue(state, user.getScreenName());
+
+ }
+
+ /**
+ * Gets the current user for initialising the form.
+ *
+ * @param state
+ * @return the current user, if the form should not be initialised with user
+ * data.
+ */
+ protected abstract User getUser(final PageState state);
+
+ /**
+ * Validates this form. Verifies that the password and password-confirm
+ * fields match. If not it adds an error to the password-confirm field. Also
+ * verifies that primary email address and screen name are unique among all
+ * users.
+ *
+ * @param event
+ * @throws com.arsdigita.bebop.FormProcessException
+ */
+ @Override
+ public void validate(final FormSectionEvent event)
+ throws FormProcessException {
+ PageState state = event.getPageState();
+ FormData data = event.getFormData();
+ try {
+ if (m_newUser) {
+ // Verify that password and confirmation match
+ String password = (String) m_password.getValue(state);
+ String confirm = (String) m_confirm.getValue(state);
+
+ if ((password != null) && (confirm != null)
+ && !password.equals(confirm)) {
+ data.addError(FORM_PASSWORD_CONFIRMATION,
+ ERROR_MISMATCH_PASSWORD);
+ }
+ }
+
+ String email = null;
+ if (m_email.getValue(state) != null) {
+ InternetAddress address = (InternetAddress) m_email
+ .getValue(state);
+ email = address.getAddress();
+ }
+
+ final String screenName = (String) m_screenName.getValue(state);
+
+ // If this query returns with any rows we have a duplicate
+ // screen name, email address, or both. Check the results and
+ // produce appropriate error messages.
+ final boolean checkPrimaryEmail = KernelConfig.getConfig()
+ .emailIsPrimaryIdentifier();
+
+ final UserRepository userRepo;
+ try {
+ final CdiUtil cdiUtil = new CdiUtil();
+ userRepo = cdiUtil.findBean(
+ UserRepository.class);
+ } catch (CdiLookupException ex) {
+ throw new FormProcessException(ex);
+ }
+
+ final User userByEmail = userRepo.findByEmailAddress(email);
+ if (userByEmail != null && checkPrimaryEmail) {
+ data.addError(FORM_EMAIL, ERROR_DUPLICATE_EMAIL);
+ }
+
+ final User userByScreenname = userRepo.findByScreenName(screenName);
+ if (userByScreenname != null) {
+ data.addError(FORM_SCREEN_NAME, ERROR_DUPLICATE_SN);
+ }
+
+ } finally {
+ // if the form has errors, clear the password fields so we don't
+ // send the passwords back over the network
+ if (m_newUser && !data.isValid()) {
+ m_password.setValue(state, "");
+ m_confirm.setValue(state, "");
+ }
+ }
+ }
+
+}
diff --git a/ccm-core/src/main/java/com/arsdigita/ui/login/UserInfo.java b/ccm-core/src/main/java/com/arsdigita/ui/login/UserInfo.java
new file mode 100644
index 000000000..8c61924f9
--- /dev/null
+++ b/ccm-core/src/main/java/com/arsdigita/ui/login/UserInfo.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+package com.arsdigita.ui.login;
+
+import com.arsdigita.bebop.ListPanel;
+import com.arsdigita.bebop.Page;
+import com.arsdigita.bebop.PageState;
+import com.arsdigita.bebop.SimpleComponent;
+import com.arsdigita.bebop.SimpleContainer;
+import com.arsdigita.dispatcher.DispatcherHelper;
+import com.arsdigita.util.UncheckedWrapperException;
+import com.arsdigita.web.URL;
+import com.arsdigita.xml.Element;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.libreccm.cdi.utils.CdiLookupException;
+import org.libreccm.cdi.utils.CdiUtil;
+import org.libreccm.core.User;
+import org.libreccm.web.ApplicationRepository;
+import org.libreccm.web.CcmApplication;
+
+
+/**
+ * A reusable Bebop component to display the primary attributes of the currently
+ * logged in user. Users can extend this class to provide information about
+ * another user by overriding the {@link
+ * UserInfo#register(Page)} and {@link UserInfo#getUser(PageState)} methods.
+ *
+ * @author Michael Bryzek
+ * @author Roger Hsueh
+ * @author Sameer Ajmani
+ * @author Peter Boy (refactored to eliminate old type kernel.Package* /
+ * SiteNode)
+ * @since 2001-06-01
+ * @version 1.0
+ * @version $Id$
+ */
+public class UserInfo extends SimpleContainer {
+
+ /**
+ * Logger instance for debugging support
+ */
+ private static final Logger s_log = Logger.getLogger(UserInfo.class
+ .getName());
+
+ /**
+ * Holds a list of content centers (Application instances) that exist on
+ * this installation. Usually there is only ONE installed, but obviously
+ * care is taken that one content-center may be installed per subsite.
+ */
+ private List
+ * A RedirectSignal that sends the client to the login UI.
+ * LoginSignal encodes the origin URL so that
+ * ReturnSignal can return the client to its starting point.
A signal that returns the client to a return URL encoded in the + * current URL, or if the return URL is not found, uses a fallback + * URL.
+ * + * @author Justin Ross <jross@redhat.com> + * @version $Id$ + */ +public class ReturnSignal extends RedirectSignal { + + private static final Logger s_log = Logger.getLogger(ReturnSignal.class); + private static final long serialVersionUID = -2923355745770322780L; + + public ReturnSignal(final HttpServletRequest sreq) { + super(getReturnURL(sreq), true); + } + + public ReturnSignal(final HttpServletRequest sreq, final String fallback) { + super(getReturnURL(sreq, fallback), true); + } + + public ReturnSignal(final HttpServletRequest sreq, final URL fallback) { + this(sreq, fallback.toString()); + } + + private static String getReturnURL(final HttpServletRequest sreq) { + s_log.debug("Fetching the return URL to redirect to"); + + final String returnURL = sreq.getParameter("return_url"); + + Assert.exists(returnURL, "String returnURL"); + + if (s_log.isDebugEnabled()) { + s_log.debug("Redirecting to URL '" + returnURL + "'"); + } + + return returnURL; + } + + private static String getReturnURL(final HttpServletRequest sreq, + final String fallback) { + s_log.debug("Fetching the return URL to redirect to"); + + Assert.exists(fallback, "String fallback"); + + final String returnURL = sreq.getParameter("return_url"); + + if (returnURL == null || returnURL.equals("")) { + if (s_log.isDebugEnabled()) { + s_log.debug("Cannot find the return URL parameter; " + + "using the fallback URL '" + fallback + "'"); + } + + return fallback; + } else { + if (s_log.isDebugEnabled()) { + s_log.debug("Redirecting to the value in the return URL " + + "parameter, '" + returnURL + "'"); + } + + return returnURL; + } + } +} diff --git a/ccm-core/src/main/java/com/arsdigita/web/URL.java b/ccm-core/src/main/java/com/arsdigita/web/URL.java index 69ee16911..3410f268a 100644 --- a/ccm-core/src/main/java/com/arsdigita/web/URL.java +++ b/ccm-core/src/main/java/com/arsdigita/web/URL.java @@ -28,7 +28,7 @@ import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; -import org.libreccm.web.Application; +import org.libreccm.web.CcmApplication; /** *@@ -778,7 +778,7 @@ public class URL { * indicating that the URL has no query string.
* * @param sreq the servlet request - * @param app theApplication to dispatch to
+ * @param app the CcmApplication to dispatch to
* @param pathInfo a String of extra path info for the
* application
* @param params a ParameterMap of parameters to use
@@ -787,7 +787,7 @@ public class URL {
* pathInfo
*/
public static final URL there(final HttpServletRequest sreq,
- final Application app,
+ final CcmApplication app,
final String pathInfo,
final ParameterMap params) {
if (Assert.isEnabled() && pathInfo != null) {
@@ -809,7 +809,7 @@ public class URL {
* specified application.
*
* @param sreq the servlet request
- * @param app the Application to dispatch to
+ * @param app the CcmApplication to dispatch to
* @param pathInfo a String of extra path info for the
* application
*
@@ -817,7 +817,7 @@ public class URL {
* pathInfo
*/
public static final URL there(final HttpServletRequest sreq,
- final Application app,
+ final CcmApplication app,
final String pathInfo) {
if (Assert.isEnabled() && pathInfo != null) {
Assert.isTrue(pathInfo.startsWith("/"),
@@ -868,7 +868,7 @@ public class URL {
* HttpServletRequest object as it will ignore any Host header
* given by the client.
*/
- public static final URL there(final Application app,
+ public static final URL there(final CcmApplication app,
final String pathInfo,
final ParameterMap params) {
return URL.there(app.getPrimaryUrl() + pathInfo, params);
@@ -877,7 +877,7 @@ public class URL {
public static final URL here(final HttpServletRequest sreq,
final String pathInfo,
final ParameterMap params) {
- final Application app = Web.getWebContext().getApplication();
+ final CcmApplication app = Web.getWebContext().getApplication();
Assert.exists(app, "Application app");
@@ -886,7 +886,7 @@ public class URL {
public static final URL here(final HttpServletRequest sreq,
final String pathInfo) {
- final Application app = Web.getWebContext().getApplication();
+ final CcmApplication app = Web.getWebContext().getApplication();
Assert.exists(app, "Application app");
diff --git a/ccm-core/src/main/java/com/arsdigita/web/WebContext.java b/ccm-core/src/main/java/com/arsdigita/web/WebContext.java
index c563df3e4..17b625f39 100644
--- a/ccm-core/src/main/java/com/arsdigita/web/WebContext.java
+++ b/ccm-core/src/main/java/com/arsdigita/web/WebContext.java
@@ -18,14 +18,14 @@
*/
package com.arsdigita.web;
-// import com.arsdigita.web.Application;
+// import com.arsdigita.web.CcmApplication;
import com.arsdigita.util.Assert;
import com.arsdigita.util.Record;
import org.apache.log4j.Logger;
import org.libreccm.core.CcmSessionContext;
import org.libreccm.core.User;
-import org.libreccm.web.Application;
+import org.libreccm.web.CcmApplication;
/**
*
@@ -51,7 +51,7 @@ public final class WebContext extends Record {
*/
private static final Logger s_log = Logger.getLogger(WebContext.class);
- private Application m_application = null;
+ private CcmApplication m_application = null;
private URL m_requestURL = null;
/**
@@ -90,7 +90,7 @@ public final class WebContext extends Record {
* @param app
* @param requestURL
*/
- final void init(final Application app, final URL requestURL) {
+ final void init(final CcmApplication app, final URL requestURL) {
setApplication(app);
setRequestURL(requestURL);
}
@@ -114,7 +114,7 @@ public final class WebContext extends Record {
*
* @return
*/
- public final Application getApplication() {
+ public final CcmApplication getApplication() {
return m_application;
}
@@ -122,7 +122,7 @@ public final class WebContext extends Record {
*
* @param app
*/
- final void setApplication(final Application app) {
+ final void setApplication(final CcmApplication app) {
m_application = app;
mutated("Application");
diff --git a/ccm-core/src/main/java/org/libreccm/categorization/Domain.java b/ccm-core/src/main/java/org/libreccm/categorization/Domain.java
index 7fb6242c8..341373198 100644
--- a/ccm-core/src/main/java/org/libreccm/categorization/Domain.java
+++ b/ccm-core/src/main/java/org/libreccm/categorization/Domain.java
@@ -29,7 +29,7 @@ import static org.libreccm.core.CoreConstants.*;
import org.libreccm.jpa.utils.UriConverter;
import org.libreccm.l10n.LocalizedString;
-import org.libreccm.web.Application;
+import org.libreccm.web.CcmApplication;
import java.io.Serializable;
import java.net.URI;
@@ -65,9 +65,9 @@ import javax.xml.bind.annotation.XmlRootElement;
* as well as the {@code CategoryPurpose} entity from the old
* {@code ccm-core module}.
*
- * A {@code Domain} can be mapped to multiple {@link Application}s. Normally
+ * A {@code Domain} can be mapped to multiple {@link CcmApplication}s. Normally
* This is used to make a {@code Domain} available in the application. The
- * {@link Application}s to which a {@code Domain} is mapped are called
+ * {@link CcmApplication}s to which a {@code Domain} is mapped are called
* owners of the domain.
*
* @author Jens Pelzetter
diff --git a/ccm-core/src/main/java/org/libreccm/categorization/DomainManager.java b/ccm-core/src/main/java/org/libreccm/categorization/DomainManager.java
index fb53861cd..740f2c72a 100644
--- a/ccm-core/src/main/java/org/libreccm/categorization/DomainManager.java
+++ b/ccm-core/src/main/java/org/libreccm/categorization/DomainManager.java
@@ -18,14 +18,14 @@
*/
package org.libreccm.categorization;
-import org.libreccm.web.Application;
+import org.libreccm.web.CcmApplication;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
/**
* Provides several methods when managing the relations between {@link Domain}s
- * and their owning {@link Application}s.
+ * and their owning {@link CcmApplication}s.
*
* @author Jens Pelzetter
*/
@@ -36,46 +36,46 @@ public class DomainManager {
private transient DomainRepository domainRepo;
/**
- * Adds a {@code Application} to the owners of a {@link Domain}. If the
- * provided {@code Application} is already an owner of the provided
+ * Adds a {@code CcmApplication} to the owners of a {@link Domain}. If the
+ * provided {@code CcmApplication} is already an owner of the provided
* {@code Domain} the method does nothing.
*
- * @param application The {@code Application} to add to the owners of the
+ * @param application The {@code CcmApplication} to add to the owners of the
* {@code Domain}.
* @param domain The {@code Domain} to which owners the
- * {@code Application is added}.
+ * {@code CcmApplication is added}.
*/
- public void addDomainOwner(final Application application,
+ public void addDomainOwner(final CcmApplication application,
final Domain domain) {
// TODO implement method
throw new UnsupportedOperationException();
}
/**
- * Removes a {@code Application} from the owners of a {@code Domain}. If the
- * provided {@code Application} is not an owner of the provided
+ * Removes a {@code CcmApplication} from the owners of a {@code Domain}. If the
+ * provided {@code CcmApplication} is not an owner of the provided
* {@code Domain} the method does nothing.
*
- * @param application The {@code Application} to remove from the owners of
+ * @param application The {@code CcmApplication} to remove from the owners of
* the provided {@code Domain}.
* @param domain The {@code Domain} from which owners the provided
- * {@code Application} should be removed.
+ * {@code CcmApplication} should be removed.
*/
- public void removeDomainOwner(final Application application,
+ public void removeDomainOwner(final CcmApplication application,
final Domain domain) {
// TODO implement method
throw new UnsupportedOperationException();
}
/**
- * Determines if a {@link Application} is an owner of {@link Domain}.
+ * Determines if a {@link CcmApplication} is an owner of {@link Domain}.
*
- * @param application The {@code Application} to test.
+ * @param application The {@code CcmApplication} to test.
* @param domain The {@code Domain} to test.
- * @return {@code true} if the provided {@code Application} is an owner
+ * @return {@code true} if the provided {@code CcmApplication} is an owner
* of the provided {@code Domain}, {@code false} otherwise.
*/
- public boolean isDomainOwner(final Application application,
+ public boolean isDomainOwner(final CcmApplication application,
final Domain domain) {
// TODO implement method
throw new UnsupportedOperationException();
diff --git a/ccm-core/src/main/java/org/libreccm/categorization/DomainOwnership.java b/ccm-core/src/main/java/org/libreccm/categorization/DomainOwnership.java
index c47fbaba2..50ff29455 100644
--- a/ccm-core/src/main/java/org/libreccm/categorization/DomainOwnership.java
+++ b/ccm-core/src/main/java/org/libreccm/categorization/DomainOwnership.java
@@ -33,7 +33,7 @@ import org.libreccm.core.CcmObject;
import static org.libreccm.core.CoreConstants.*;
-import org.libreccm.web.Application;
+import org.libreccm.web.CcmApplication;
/**
@@ -62,7 +62,7 @@ public class DomainOwnership implements Serializable {
* The {@link CcmObject} owning the {@link Domain}.
*/
@ManyToOne(optional = false)
- private Application owner;
+ private CcmApplication owner;
/**
* The {@link Domain} owned by the {@link CcmObject}.
@@ -98,11 +98,11 @@ public class DomainOwnership implements Serializable {
this.ownershipId = ownershipId;
}
- public Application getOwner() {
+ public CcmApplication getOwner() {
return owner;
}
- protected void setOwner(final Application owner) {
+ protected void setOwner(final CcmApplication owner) {
this.owner = owner;
}
diff --git a/ccm-core/src/main/java/org/libreccm/core/CcmCore.java b/ccm-core/src/main/java/org/libreccm/core/CcmCore.java
index 70380ab92..cedfb06a3 100644
--- a/ccm-core/src/main/java/org/libreccm/core/CcmCore.java
+++ b/ccm-core/src/main/java/org/libreccm/core/CcmCore.java
@@ -75,8 +75,7 @@ import javax.persistence.EntityManager;
org.libreccm.runtime.Initalizer.class,
org.libreccm.search.lucene.Document.class,
org.libreccm.search.lucene.Index.class,
- org.libreccm.web.Application.class,
- org.libreccm.web.ApplicationType.class,
+ org.libreccm.web.CcmApplication.class,
org.libreccm.web.Host.class,
org.libreccm.workflow.Task.class,
org.libreccm.workflow.UserTask.class,
diff --git a/ccm-core/src/main/java/org/libreccm/core/CcmSessionContext.java b/ccm-core/src/main/java/org/libreccm/core/CcmSessionContext.java
index f05d63764..899193800 100644
--- a/ccm-core/src/main/java/org/libreccm/core/CcmSessionContext.java
+++ b/ccm-core/src/main/java/org/libreccm/core/CcmSessionContext.java
@@ -23,7 +23,7 @@ import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
/**
- * This bean stores several datas about the current session, for example the
+ * This bean stores several data about the current session, for example the
* current party.
*
* @author Jens Pelzetter
diff --git a/ccm-core/src/main/java/org/libreccm/core/Resource.java b/ccm-core/src/main/java/org/libreccm/core/Resource.java
index 05d50bbce..9b743fa61 100644
--- a/ccm-core/src/main/java/org/libreccm/core/Resource.java
+++ b/ccm-core/src/main/java/org/libreccm/core/Resource.java
@@ -21,7 +21,7 @@ package org.libreccm.core;
import static org.libreccm.core.CoreConstants.*;
import org.libreccm.l10n.LocalizedString;
-import org.libreccm.web.Application;
+import org.libreccm.web.CcmApplication;
import java.io.Serializable;
import java.util.Collections;
@@ -43,7 +43,7 @@ import javax.persistence.TemporalType;
/**
* The {@code Resource} class is a base class for several other classes, for
- * example the {@link Application} class.
+ * example the {@link CcmApplication} class.
*
* Resources can be nested, a resource can have multiple child resources.
*
diff --git a/ccm-core/src/main/java/org/libreccm/core/ResourceType.java b/ccm-core/src/main/java/org/libreccm/core/ResourceType.java
index 5e9eaf5ab..d9b741c32 100644
--- a/ccm-core/src/main/java/org/libreccm/core/ResourceType.java
+++ b/ccm-core/src/main/java/org/libreccm/core/ResourceType.java
@@ -37,9 +37,18 @@ import javax.persistence.Table;
import org.libreccm.l10n.LocalizedString;
/**
- *
+ * This class is a port of the old {@code ResourceType} entity.
+ *
+ * @deprecated The real purpose of this class is not clear. Also the
+ * informations provided by the entities of this class are all quite static or
+ * can be interfered from the classes itself. In modern Java most if not all the
+ * informations provided by the entities of this class would be expressed as
+ * annotations. At the moment it is not clear of we can remove this class
+ * completely therefore it is still here but will maybe removed very soon.
+ *
* @author Jens Pelzetter
*/
+@Deprecated
@Entity
@Table(name = "RESOURCE_TYPES", schema = DB_SCHEMA)
@Inheritance(strategy = InheritanceType.JOINED)
diff --git a/ccm-core/src/main/java/org/libreccm/core/User.java b/ccm-core/src/main/java/org/libreccm/core/User.java
index 4a2a4267b..7c2acfd77 100644
--- a/ccm-core/src/main/java/org/libreccm/core/User.java
+++ b/ccm-core/src/main/java/org/libreccm/core/User.java
@@ -219,11 +219,11 @@ public class User extends Subject implements Serializable {
this.emailAddresses = eMailAddresses;
}
- protected void addEmailAddress(final EmailAddress emailAddress) {
+ public void addEmailAddress(final EmailAddress emailAddress) {
emailAddresses.add(emailAddress);
}
- protected void removeEmailAddress(final EmailAddress emailAddress) {
+ public void removeEmailAddress(final EmailAddress emailAddress) {
emailAddresses.remove(emailAddress);
}
diff --git a/ccm-core/src/main/java/org/libreccm/core/authentication/LoginManager.java b/ccm-core/src/main/java/org/libreccm/core/authentication/LoginManager.java
index d72a520a0..ae9464d01 100644
--- a/ccm-core/src/main/java/org/libreccm/core/authentication/LoginManager.java
+++ b/ccm-core/src/main/java/org/libreccm/core/authentication/LoginManager.java
@@ -84,6 +84,10 @@ public class LoginManager {
}
}
+ public void logout() {
+ sessionContext.setCurrentSubject(null);
+ }
+
private static class LoginCallbackHandler implements CallbackHandler {
private final transient String username;
diff --git a/ccm-core/src/main/java/org/libreccm/modules/Module.java b/ccm-core/src/main/java/org/libreccm/modules/Module.java
index c73394f1c..36cf3c9e5 100644
--- a/ccm-core/src/main/java/org/libreccm/modules/Module.java
+++ b/ccm-core/src/main/java/org/libreccm/modules/Module.java
@@ -18,6 +18,8 @@
*/
package org.libreccm.modules;
+import org.libreccm.web.ApplicationType;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -67,6 +69,14 @@ public @interface Module {
*/
RequiredModule[] requiredModules() default {};
+ /**
+ * ApplicationType types provided by the annotated module.
+ *
+ * @return An array containing the type descriptions for all application
+ * types provided by the annotated module.
+ */
+ ApplicationType[] applicationTypes() default {};
+
/**
* The JPA entities provided by the annotated module.
*
diff --git a/ccm-core/src/main/java/org/libreccm/web/ApplicationCreateException.java b/ccm-core/src/main/java/org/libreccm/web/ApplicationCreateException.java
new file mode 100644
index 000000000..7d0832e83
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/web/ApplicationCreateException.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+package org.libreccm.web;
+
+/**
+ *
+ * @author Jens Pelzetter
+ */
+public class ApplicationCreateException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new instance of ApplicationCreateException without detail message.
+ */
+ public ApplicationCreateException() {
+ super();
+ }
+
+
+ /**
+ * Constructs an instance of ApplicationCreateException with the specified detail message.
+ *
+ * @param msg The detail message.
+ */
+ public ApplicationCreateException(final String msg) {
+ super(msg);
+ }
+
+ /**
+ * Constructs an instance of ApplicationCreateException which wraps the
+ * specified exception.
+ *
+ * @param exception The exception to wrap.
+ */
+ public ApplicationCreateException(final Exception exception) {
+ super(exception);
+ }
+
+ /**
+ * Constructs an instance of ApplicationCreateException with the specified message which also wraps the
+ * specified exception.
+ *
+ * @param msg The detail message.
+ * @param exception The exception to wrap.
+ */
+ public ApplicationCreateException(final String msg, final Exception exception) {
+ super(msg, exception);
+ }
+}
diff --git a/ccm-core/src/main/java/org/libreccm/web/ApplicationCreator.java b/ccm-core/src/main/java/org/libreccm/web/ApplicationCreator.java
new file mode 100644
index 000000000..69b29b5d8
--- /dev/null
+++ b/ccm-core/src/main/java/org/libreccm/web/ApplicationCreator.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 LibreCCM Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+package org.libreccm.web;
+
+/**
+ *
+ * @author Jens Pelzetter
+ * @param About You
+login.userNewForm.securitySectionHeader=Security
+login.userNewForm.securityBlurb=We need a password from you to protect your identity as you contribute to the Q&A, discussion forums, and other community activities on this site.
+login.userNewForm.passwordBlurb=Choose a password that contains a mix of uppercase letters, lowercase letters, digits, and other symbols. If you forget your password, our server will help you change it to a new one.
+login.userNewForm.questionBlurb=We need a customized question and answer so we can make sure only you can change your password.
+login.changePasswordForm.oldPasswordLabel=Old Password:
+login.changePasswordForm.newPasswordLabel=New Password (at least {0} characters, no whitespace):
+login.changePasswordForm.confirmPasswordLabel=Confirm Password:
+login.changePasswordForm.submit=Submit
+login.changePasswordForm.noUserError=User is not logged in
+login.changePasswordForm.badPasswordError=Incorrect Password
+login.changePasswordForm.mustDifferError=New password must differ from old
+login.changePasswordForm.mustMatchError=New passwords must match
+login.changePasswordForm.mailSubject=Your password has been changed
+login.changePasswordForm.mailBody=Dear {0},\n\nYour password has been changed successfully.\nIf you did not intend to change your password,\nplease reply to this mail and report this problem.
+login.changePasswordPage.title=Change Password
+login.recoverPassword.mailSubject=Change your password
+login.recoverPassword.mailBody=Dear {0},\n\nClick on this URL or paste it into your browser to change your password:\n{1}
+login.recoverPasswordPage.title=Recover Password
+login.loginExpiredPage.title=Login Expired
+login.loginExpiredPage.before=The login page has expired. Please\u0020
+login.loginExpiredPage.link=login
+login.loginExpiredPage.after=\u0020again.
+login.passwordValidation.minLengthError=Password must be at least {0} characters long
+login.passwordValidation.whitespaceError=Password must not contain whitespace
+login.passwordValidation.minTypesError=Password must contain {0} of these 4 character types: uppercase letters, lowercase letters, digits, and other symbols
+login.submit=Submit
+login.primaryEmail=Email Address:
+login.additionalEmail=Additional Email:
+login.firstName=First (Given) Name:
+login.lastName=Last (Family) Name:
+login.password=Password (at least {0} characters, no whitespace):
+login.passwordConfirm=Confirm Password:
+login.passwordQuestion=Question:
+login.passwordAnswer=Answer:
+login.url=Personal Home Page URL:
+login.screenName=Screen Name:
+login.bio=Biography:
+login.error.duplicateScreenName=Some other user has this screen name
+login.error.duplicateEmail=Some other user has this email address
+login.error.mismatchPassword=New passwords must match
+login.error.badPassword=Incorrect password
+login.error.badAnswer=Incorrect answer
+login.error.badEmail=Unrecognized email address {0}
+login.error.bannedEmail=User cannot currently access system
+login.error.loginFail=Login failed
+login.changePasswortForm.greeting=Welcome {0}
+login.changePasswortForm.introText=To change your passwort please fill out this form.
+login.userForm.couldnt_load_user=Could not load User
+login.userLoginForm.couldnt_create_timestamp=Could not create timestamp
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/login/LoginResources_de.properties b/ccm-core/src/main/resources/com/arsdigita/ui/login/LoginResources_de.properties
new file mode 100644
index 000000000..691f2376b
--- /dev/null
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/login/LoginResources_de.properties
@@ -0,0 +1,66 @@
+login.userEditPage.title=Profil bearbeiten
+login.userEditPage.changePasswordLink=Passwort \u00e4ndern
+login.workspacePage.title=Ihr pers\u00f6nlicher Bereich
+login.explainCookiesPage.title=E-Mail Adresse und Passwort werden gesichert
+login.userInfo.logoutLink=Abmelden
+login.userInfo.editProfileLink=Profil bearbeiten
+login.userInfo.changePasswordLink=Passwort \u00e4ndern
+login.userRegistrationForm.title=Anmelden
+login.userRegistrationForm.email=Ihre E-Mail Adresse\:
+login.userRegistrationForm.screenName=Ihr Benutzername\:
+login.userRegistrationForm.password=Ihr Passwort\:
+login.userRegistrationForm.cookieOption=An dieses Login erinnern?
+login.userRegistrationForm.explainCookieLink=(Hilfe)
+login.userRegistrationForm.forgotPasswordLink=Passwort vergessen?
+login.userRegistrationForm.newUserRegister=Neue Benutzer registrieren sich hier
+login.userNewForm.title=Registrieren
+login.userNewForm.aboutYouSectionHeader=\u00dcber mich
+login.userNewForm.securitySectionHeader=Sicherheit
+login.userNewForm.securityBlurb=Wie ben\u00f6tigen ein Passwort, um Ihre Identit\u00e4t und Beitr\u00e4ge zu Foren und anderen Aktivit\u00e4ten zu sch\u00fctzen.
+login.userNewForm.passwordBlurb=W\u00e4hlen Sie ein Passwort aus Gru\u00dfbuchstaben, Kleinbuchstaben, Zahlen und anderen Zeichen. Wenn sie es vergessen, unterst\u00fctzt Sie der Server bei der Festlegung eines neuen.
+login.userNewForm.questionBlurb=Wir ben\u00f6tigen eine individuelle Frage und Antwort um sicher zu gehen, dass nur Sie das Passwort \u00e4ndern k\u00f6nnen.
+login.changePasswordForm.oldPasswordLabel=Altes Passwort\:
+login.changePasswordForm.newPasswordLabel=Neues Passwort (mindestens {0} Zeichen, keine Leertaste)\:
+login.changePasswordForm.confirmPasswordLabel=Passwort best\u00e4tigen\:
+login.changePasswordForm.submit=Ausf\u00fchren
+login.changePasswordForm.noUserError=Benutzer ist nicht angemeldet
+login.changePasswordForm.badPasswordError=Falsches Passwort
+login.changePasswordForm.mustDifferError=Das neue Passwort muss sich vom bisherigen unterscheiden
+login.changePasswordForm.mustMatchError=Die neuen Passw\u00f6rter m\u00fcssen gleich sein
+login.changePasswordForm.mailSubject=Ihr Passwort wurde ge\u00e4ndert.
+login.changePasswordForm.mailBody=Hallo {0},\n\nIhr Passwort wurde erfolgreich ge\u00e4ndert.\nWenn Sie Ihr Passwort nicht \u00e4ndern wollten,\nmelden Sie bitte diese Mail an uns.
+login.changePasswordPage.title=Passwort \u00c4nderung
+login.recoverPassword.mailSubject=\u00c4ndern Sie das Passwort
+login.recoverPassword.mailBody=Hallo {0},\n\nKlicken Sie auf diese URL oder kopieren Sie sie in Ihren Browser, um Ihr Passwort zu \u00e4ndern\:\n{1}
+login.recoverPasswordPage.title=Passwort holen
+login.loginExpiredPage.title=Anmeldung abgelaufen
+login.loginExpiredPage.before=Der Anmeldebildschirm ist nicht mehr g\u00fcltig
+login.loginExpiredPage.link=Anmeldung
+login.loginExpiredPage.after= erneut.
+login.passwordValidation.minLengthError=Passwort muss aus mindestens {0} Zeichen bestehen
+login.passwordValidation.whitespaceError=Das Passwort darf kein Leerzeichen enthalten
+login.passwordValidation.minTypesError=Das Passwort muss aus {0} dieser 4 Typen gestehen\: Gro\u00dfbuchstaben, Kleinbuchstaben, Ziffern, andere Zeichen
+login.submit=Ausf\u00fchren
+login.primaryEmail=E-Mail Adresse\:
+login.additionalEmail=Weitere E-Mail\:
+login.firstName=Vorname\:
+login.lastName=Nachname\:
+login.password=Passwort (mindestens {0} Zeichen, kein Leerzeichen)\:
+login.passwordConfirm=Passwort best\u00e4tigen\:
+login.passwordQuestion=Frage\:
+login.passwordAnswer=Antwort\:
+login.url=URL der pers\u00f6nlichen Homepage\:
+login.screenName=Benutzername\:
+login.bio=Biographie\:
+login.error.duplicateScreenName=Ein anderer Benutzer verwendet diesen Namen
+login.error.duplicateEmail=Ein anderer Nutzer verwendet diese E-Mail Adresse
+login.error.mismatchPassword=Die neuen Passw\u00f6rter m\u00fcssen gleich sein
+login.error.badPassword=Falsches Passwort
+login.error.badAnswer=Falsche Antwort
+login.error.badEmail=Ung\u00fcltige E-Mail Adresse: {0}
+login.error.bannedEmail=Benutzer kann zur Zeit das System nicht nutzen
+login.error.loginFail=Anmeldung nicht erfolgreich
+login.changePasswortForm.greeting=Willkommen {0}
+login.changePasswortForm.introText=Um ihr Passwort zu \u00e4ndern, f\u00fcllen sie bitte das folgende Formular aus.
+login.userForm.couldnt_load_user=User konnte nicht geladen werden
+login.userLoginForm.couldnt_create_timestamp=Konnte den Timestamp nicht erstellen
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/login/LoginResources_en.properties b/ccm-core/src/main/resources/com/arsdigita/ui/login/LoginResources_en.properties
new file mode 100644
index 000000000..d9978c8e4
--- /dev/null
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/login/LoginResources_en.properties
@@ -0,0 +1,66 @@
+login.userEditPage.title=Edit Profile
+login.userEditPage.changePasswordLink=Change password
+login.workspacePage.title=Your workspace
+login.explainCookiesPage.title=Saving email address and password
+login.userInfo.logoutLink=Log out
+login.userInfo.editProfileLink=Edit profile
+login.userInfo.changePasswordLink=Change password
+login.userRegistrationForm.title=Log in
+login.userRegistrationForm.email=Your email address:
+login.userRegistrationForm.screenName=Your screen name:
+login.userRegistrationForm.password=Your password:
+login.userRegistrationForm.cookieOption=Remember this login?
+login.userRegistrationForm.explainCookieLink=(help)
+login.userRegistrationForm.forgotPasswordLink=Forgot your password?
+login.userRegistrationForm.newUserRegister=New users register here
+login.userNewForm.title=Register
+login.userNewForm.aboutYouSectionHeader=About You
+login.userNewForm.securitySectionHeader=Security
+login.userNewForm.securityBlurb=We need a password from you to protect your identity as you contribute to the Q&A, discussion forums, and other community activities on this site.
+login.userNewForm.passwordBlurb=Choose a password that contains a mix of uppercase letters, lowercase letters, digits, and other symbols. If you forget your password, our server will help you change it to a new one.
+login.userNewForm.questionBlurb=We need a customized question and answer so we can make sure only you can change your password.
+login.changePasswordForm.oldPasswordLabel=Old Password:
+login.changePasswordForm.newPasswordLabel=New Password (at least {0} characters, no whitespace):
+login.changePasswordForm.confirmPasswordLabel=Confirm Password:
+login.changePasswordForm.submit=Submit
+login.changePasswordForm.noUserError=User is not logged in
+login.changePasswordForm.badPasswordError=Incorrect Password
+login.changePasswordForm.mustDifferError=New password must differ from old
+login.changePasswordForm.mustMatchError=New passwords must match
+login.changePasswordForm.mailSubject=Your password has been changed
+login.changePasswordForm.mailBody=Dear {0},\n\nYour password has been changed successfully.\nIf you did not intend to change your password,\nplease reply to this mail and report this problem.
+login.changePasswordPage.title=Change Password
+login.recoverPassword.mailSubject=Change your password
+login.recoverPassword.mailBody=Dear {0},\n\nClick on this URL or paste it into your browser to change your password:\n{1}
+login.recoverPasswordPage.title=Recover Password
+login.loginExpiredPage.title=Login Expired
+login.loginExpiredPage.before=The login page has expired. Please\u0020
+login.loginExpiredPage.link=login
+login.loginExpiredPage.after=\u0020again.
+login.passwordValidation.minLengthError=Password must be at least {0} characters long
+login.passwordValidation.whitespaceError=Password must not contain whitespace
+login.passwordValidation.minTypesError=Password must contain {0} of these 4 character types: uppercase letters, lowercase letters, digits, and other symbols
+login.submit=Submit
+login.primaryEmail=Email Address:
+login.additionalEmail=Additional Email:
+login.firstName=First (Given) Name:
+login.lastName=Last (Family) Name:
+login.password=Password (at least {0} characters, no whitespace):
+login.passwordConfirm=Confirm Password:
+login.passwordQuestion=Question:
+login.passwordAnswer=Answer:
+login.url=Personal Home Page URL:
+login.screenName=Screen Name:
+login.bio=Biography:
+login.error.duplicateScreenName=Some other user has this screen name
+login.error.duplicateEmail=Some other user has this email address
+login.error.mismatchPassword=New passwords must match
+login.error.badPassword=Incorrect password
+login.error.badAnswer=Incorrect answer
+login.error.badEmail=Unrecognized email address {0}
+login.error.bannedEmail=User cannot currently access system
+login.error.loginFail=Login failed
+login.changePasswortForm.greeting=Welcome {0}
+login.changePasswortForm.introText=To change your passwort please fill out this form.
+login.userForm.couldnt_load_user=Could not load User
+login.userLoginForm.couldnt_create_timestamp=Could not create timestamp
diff --git a/ccm-core/src/main/resources/com/arsdigita/ui/login/LoginResources_fr.properties b/ccm-core/src/main/resources/com/arsdigita/ui/login/LoginResources_fr.properties
new file mode 100755
index 000000000..be53f1a31
--- /dev/null
+++ b/ccm-core/src/main/resources/com/arsdigita/ui/login/LoginResources_fr.properties
@@ -0,0 +1,66 @@
+login.userEditPage.title=Modifier ce profil
+login.userEditPage.changePasswordLink=Modifier le mot de passe
+login.workspacePage.title=Votre espace de travail
+login.explainCookiesPage.title=Enregistrement de l'adresse de messagerie et du mot de passe
+login.userInfo.logoutLink=D\u00e9connexion
+login.userInfo.editProfileLink=Modifier ce profil
+login.userInfo.changePasswordLink=Modifier le mot de passe
+login.userRegistrationForm.title=Se connecter
+login.userRegistrationForm.email=Votre adresse de messagerie
+login.userRegistrationForm.screenName=Votre pseudonyme
+login.userRegistrationForm.password=Votre mot de passe
+login.userRegistrationForm.cookieOption=Retenir ce mot de passe?
+login.userRegistrationForm.explainCookieLink=(aide)
+login.userRegistrationForm.forgotPasswordLink=Vous avez oubli\u00e9 votre mot de passe?
+login.userNewForm.title=Cr\u00e9er votre compte
+login.userNewForm.aboutYouSectionHeader=A propos de vous
+login.userNewForm.securitySectionHeader=Securit\u00e9
+login.userNewForm.securityBlurb=Merci de nous fournir un mot de passe afin que nous puissions prot\u00e9ger votre indentit\u00e9 lorsque vous participez aux Quiz, aux forum de discussions et aux autres activit\u00e9s communes sur notre site.
+login.userNewForm.passwordBlurb=Choisissez un mot de passe qui contienne un m\u00e9lange de majuscules, de minuscules, de chiffres et d'autres caract\u00e8res. Si vous oubliez votre mot de passe, notre serveur vous aidera \u00e0 le remplacer par un nouveau
+login.userNewForm.questionBlurb=Merci de nous indiquer une question (et sa r\u00e9ponse) \u00e0 laquelle vous seul pouvez r\u00e9pondre pour que nous puission nous assurer que vous serez le seul \u00e0 pouvoir changer votre mot de passe
+login.changePasswordForm.oldPasswordLabel=Ancien mot de passe :
+login.changePasswordForm.newPasswordLabel=Nouveau mot de passe (au moins {0} caract\u00e8re et pas d'espace):
+login.changePasswordForm.confirmPasswordLabel=Confirmation du nouveau mot de passe
+login.changePasswordForm.submit=Soumettre
+login.changePasswordForm.noUserError=L'utilisateur n'est pas connect\u00e9
+login.changePasswordForm.badPasswordError=Mot de passe erron\u00e9
+login.changePasswordForm.mustDifferError=Le nouveau mot de passe doit \u00eatre diff\u00e9rent de l'ancien
+login.changePasswordForm.mustMatchError=Le nouveau mot de passe doit \u00eatre identique dans la zone de confirmation
+login.changePasswordForm.mailSubject=Votre mot de passe a \u00e9t\u00e9 modifi\u00e9
+login.changePasswordForm.mailBody=Cher {0}, \n\n Votre mot de passe a \u00e9t\u00e9 modifi\u00e9 avec succ\u00e8s.\nSi vous n'avez pas souhait\u00e9 changer votre mot de passe,\nmerci de r\u00e9pondre \u00e0 ce message en faisant part de ce probl\u00e8me.
+login.changePasswordPage.title=Modifier le mot de passe
+login.recoverPassword.mailSubject=Modifier votre mot de passe
+login.recoverPassword.mailBody=Cher {0},\n\nCliquer sur cet URL ou recopiez le dans la barre d'adresse de votre navigateur pour modifier votre mot de passe:\n{1}
+login.recoverPasswordPage.title=R\u00e9cup\u00e9rer le mot de passe
+login.loginExpiredPage.title=Votre connexion a expir\u00e9
+login.loginExpiredPage.before=La page de connexion a expir\u00e9. Merci\u0020
+login.loginExpiredPage.link=de vous connecter
+login.loginExpiredPage.after=\u0020\u00e0 nouveau
+login.passwordValidation.minLengthError=Le mot de passe doit faire au minimum {0} caract\u00e8res de long
+login.passwordValidation.whitespaceError=Le mot de passe ne doit pas contenir d'espace
+login.passwordValidation.minTypesError=Le mot de passe doit contenir {0} de ces 4 types de caract\u00e8res : majuscule, minuscule, chiffre ou autre symbole
+login.submit=Soumettre
+login.primaryEmail=Adresse de messagerie:
+login.additionalEmail=Adresse de messagerie suppl\u00e9mentaire
+login.firstName=Pr\u00e9nom:
+login.lastName=Nom:
+login.password=Mot de passe (au moins {0} caract\u00e8re et pas d'espace):
+login.passwordConfirm=Confirmation du mot de passe
+login.passwordQuestion=Question:
+login.passwordAnswer=R\u00e9ponse:
+login.url=URL de votre site web personnel
+login.screenName=Votre pseudonyme:
+login.bio=Biographie:
+login.error.duplicateScreenName=Un autre utilisateur utilise d\u00e9j\u00e0 ce pseudonyme
+login.error.duplicateEmail=Un autre utilisateur nous a d\u00e9j\u00e0 donner cette adresse de messagerie
+login.error.mismatchPassword=Le nouveau mot de passe doit \u00eatre identique dans la zone de confirmation
+login.error.badPassword=Mot de passe erron\u00e9
+login.error.badAnswer=R\u00e9ponse erron\u00e9e
+login.error.badEmail=Cette adresse de messagerie est inconnue
+login.changePasswortForm.greeting=Welcome {0}
+login.changePasswortForm.introText=To change your passwort please fill out this form.
+login.userRegistrationForm.newUserRegister=New users register here
+login.error.bannedEmail=User cannot currently access system
+login.error.loginFail=User cannot currently access system
+login.userForm.couldnt_load_user=Impossible de charger l'utilisateur
+login.userLoginForm.couldnt_create_timestamp=Impossible de cr\u00e9er timestamp
diff --git a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_0__create_tables.sql b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_0__create_tables.sql
index 2b3a9c562..c22e98f18 100644
--- a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_0__create_tables.sql
+++ b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/h2/V7_0_0_0__create_tables.sql
@@ -1,11 +1,3 @@
-
- create table ccm_core.application_types (
- resource_type_id bigint not null,
- container_group_id bigint,
- provider_app_type_id bigint,
- primary key (resource_type_id)
- );
-
create table ccm_core.applications (
primary_url varchar(1024) not null,
object_id bigint not null,
@@ -604,21 +596,6 @@
alter table ccm_core.workflow_user_task_assigned_users
add constraint UK_h62r6cqjp2tdnhscfkgwfupwj unique (assigned_user_id);
- alter table ccm_core.application_types
- add constraint FK_r9rd4iekfy3m8r1a1gto4t39
- foreign key (container_group_id)
- references ccm_core.ccm_groups;
-
- alter table ccm_core.application_types
- add constraint FK_i44k6al7mr4u1c76iudglds39
- foreign key (provider_app_type_id)
- references ccm_core.application_types;
-
- alter table ccm_core.application_types
- add constraint FK_41e4vrshljdkymnhb4cbkroa1
- foreign key (resource_type_id)
- references ccm_core.resource_types;
-
alter table ccm_core.applications
add constraint FK_kr3wur06hmironiamv0rn38nu
foreign key (container_group_id)
@@ -679,11 +656,6 @@
foreign key (subject_id)
references ccm_core.subjects;
- alter table ccm_core.ccm_privileges
- add constraint FK_g06a7mpltqti17tvibm2j7ti8
- foreign key (relevant_privilege_id)
- references ccm_core.application_types;
-
alter table ccm_core.ccm_roles
add constraint FK_ice2oswni34d2xx80cf81v2cv
foreign key (implicit_group_id)
diff --git a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_0__create_tables.sql b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_0__create_tables.sql
index ba2d7abb3..62014a005 100644
--- a/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_0__create_tables.sql
+++ b/ccm-core/src/main/resources/db/migrations/org/libreccm/ccm_core/pgsql/V7_0_0_0__create_tables.sql
@@ -1,11 +1,3 @@
-
- create table ccm_core.application_types (
- resource_type_id int8 not null,
- container_group_id int8,
- provider_app_type_id int8,
- primary key (resource_type_id)
- );
-
create table ccm_core.applications (
primary_url varchar(1024) not null,
object_id int8 not null,
@@ -603,21 +595,6 @@
alter table ccm_core.workflow_user_task_assigned_users
add constraint UK_h62r6cqjp2tdnhscfkgwfupwj unique (assigned_user_id);
- alter table ccm_core.application_types
- add constraint FK_r9rd4iekfy3m8r1a1gto4t39
- foreign key (container_group_id)
- references ccm_core.ccm_groups;
-
- alter table ccm_core.application_types
- add constraint FK_i44k6al7mr4u1c76iudglds39
- foreign key (provider_app_type_id)
- references ccm_core.application_types;
-
- alter table ccm_core.application_types
- add constraint FK_41e4vrshljdkymnhb4cbkroa1
- foreign key (resource_type_id)
- references ccm_core.resource_types;
-
alter table ccm_core.applications
add constraint FK_kr3wur06hmironiamv0rn38nu
foreign key (container_group_id)
@@ -678,11 +655,6 @@
foreign key (subject_id)
references ccm_core.subjects;
- alter table ccm_core.ccm_privileges
- add constraint FK_g06a7mpltqti17tvibm2j7ti8
- foreign key (relevant_privilege_id)
- references ccm_core.application_types;
-
alter table ccm_core.ccm_roles
add constraint FK_ice2oswni34d2xx80cf81v2cv
foreign key (implicit_group_id)
diff --git a/ccm-core/src/test/java/org/libreccm/categorization/EqualsAndHashCodeTest.java b/ccm-core/src/test/java/org/libreccm/categorization/EqualsAndHashCodeTest.java
index 51734389b..03751820e 100644
--- a/ccm-core/src/test/java/org/libreccm/categorization/EqualsAndHashCodeTest.java
+++ b/ccm-core/src/test/java/org/libreccm/categorization/EqualsAndHashCodeTest.java
@@ -24,7 +24,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.libreccm.tests.categories.UnitTest;
-import org.libreccm.web.Application;
+import org.libreccm.web.CcmApplication;
import java.net.URI;
import java.net.URISyntaxException;
@@ -68,11 +68,11 @@ public class EqualsAndHashCodeTest {
final Domain domain2 = new Domain();
domain2.setDomainKey("Domain Two");
- final Application application1 = new Application();
- application1.setPrimaryUrl(new URI("http://application-one.exampl.org"));
+ final CcmApplication application1 = new CcmApplication();
+ application1.setPrimaryUrl("http://application-one.exampl.org");
- final Application application2 = new Application();
- application2.setPrimaryUrl(new URI("http://application-two.exampl.org"));
+ final CcmApplication application2 = new CcmApplication();
+ application2.setPrimaryUrl("http://application-two.exampl.org");
EqualsVerifier
.forClass(entityClass)
@@ -81,7 +81,7 @@ public class EqualsAndHashCodeTest {
.withRedefinedSuperclass()
.withPrefabValues(Category.class, category1, category2)
.withPrefabValues(Domain.class, domain1, domain2)
- .withPrefabValues(Application.class, application1, application2)
+ .withPrefabValues(CcmApplication.class, application1, application2)
.verify();
}
diff --git a/ccm-core/src/test/java/org/libreccm/core/CcmObjectRepositoryTest.java b/ccm-core/src/test/java/org/libreccm/core/CcmObjectRepositoryTest.java
index a9ff2f14a..1700aa403 100644
--- a/ccm-core/src/test/java/org/libreccm/core/CcmObjectRepositoryTest.java
+++ b/ccm-core/src/test/java/org/libreccm/core/CcmObjectRepositoryTest.java
@@ -107,7 +107,7 @@ public class CcmObjectRepositoryTest {
.create(WebArchive.class,
"LibreCCM-org.libreccm.core.CcmObjectRepositoryTest.war")
.addPackage(CcmObject.class.getPackage())
- .addPackage(org.libreccm.web.Application.class.getPackage())
+ .addPackage(org.libreccm.web.CcmApplication.class.getPackage())
.addPackage(org.libreccm.categorization.Category.class.
getPackage())
.addPackage(org.libreccm.l10n.LocalizedString.class.getPackage()).
diff --git a/ccm-core/src/test/java/org/libreccm/core/EqualsAndHashCodeTest.java b/ccm-core/src/test/java/org/libreccm/core/EqualsAndHashCodeTest.java
index a5421115a..40795048c 100644
--- a/ccm-core/src/test/java/org/libreccm/core/EqualsAndHashCodeTest.java
+++ b/ccm-core/src/test/java/org/libreccm/core/EqualsAndHashCodeTest.java
@@ -26,7 +26,6 @@ import org.libreccm.tests.categories.UnitTest;
import java.util.Arrays;
import java.util.Collection;
-import org.libreccm.web.ApplicationType;
/**
*
diff --git a/ccm-core/src/test/java/org/libreccm/core/GroupManagerTest.java b/ccm-core/src/test/java/org/libreccm/core/GroupManagerTest.java
index f83c656ae..f36f56aaf 100644
--- a/ccm-core/src/test/java/org/libreccm/core/GroupManagerTest.java
+++ b/ccm-core/src/test/java/org/libreccm/core/GroupManagerTest.java
@@ -108,7 +108,7 @@ public class GroupManagerTest {
.create(WebArchive.class,
"LibreCCM-org.libreccm.core.GroupManagerTest.war")
.addPackage(User.class.getPackage())
- .addPackage(org.libreccm.web.Application.class.getPackage())
+ .addPackage(org.libreccm.web.CcmApplication.class.getPackage())
.addPackage(org.libreccm.categorization.Category.class.
getPackage())
.addPackage(org.libreccm.l10n.LocalizedString.class.getPackage()).
diff --git a/ccm-core/src/test/java/org/libreccm/core/GroupRepositoryTest.java b/ccm-core/src/test/java/org/libreccm/core/GroupRepositoryTest.java
index ccbde9774..5fde99ef5 100644
--- a/ccm-core/src/test/java/org/libreccm/core/GroupRepositoryTest.java
+++ b/ccm-core/src/test/java/org/libreccm/core/GroupRepositoryTest.java
@@ -102,7 +102,7 @@ public class GroupRepositoryTest {
.create(WebArchive.class,
"LibreCCM-org.libreccm.core.UserRepositoryTest.war")
.addPackage(User.class.getPackage())
- .addPackage(org.libreccm.web.Application.class.getPackage())
+ .addPackage(org.libreccm.web.CcmApplication.class.getPackage())
.addPackage(org.libreccm.categorization.Category.class.
getPackage())
.addPackage(org.libreccm.l10n.LocalizedString.class.getPackage()).
diff --git a/ccm-core/src/test/java/org/libreccm/core/PermissionManagerTest.java b/ccm-core/src/test/java/org/libreccm/core/PermissionManagerTest.java
index 894ddbdd6..82e9ac3ee 100644
--- a/ccm-core/src/test/java/org/libreccm/core/PermissionManagerTest.java
+++ b/ccm-core/src/test/java/org/libreccm/core/PermissionManagerTest.java
@@ -130,7 +130,7 @@ public class PermissionManagerTest {
String.format("LibreCCM-%s.war",
PermissionManagerTest.class.getName()))
.addPackage(User.class.getPackage())
- .addPackage(org.libreccm.web.Application.class.getPackage())
+ .addPackage(org.libreccm.web.CcmApplication.class.getPackage())
.addPackage(org.libreccm.categorization.Category.class.
getPackage())
.addPackage(org.libreccm.l10n.LocalizedString.class.getPackage()).
diff --git a/ccm-core/src/test/java/org/libreccm/core/PermissionRepositoryTest.java b/ccm-core/src/test/java/org/libreccm/core/PermissionRepositoryTest.java
index cdf87d3c0..4c1ecc5f3 100644
--- a/ccm-core/src/test/java/org/libreccm/core/PermissionRepositoryTest.java
+++ b/ccm-core/src/test/java/org/libreccm/core/PermissionRepositoryTest.java
@@ -123,7 +123,7 @@ public class PermissionRepositoryTest {
.create(WebArchive.class,
"LibreCCM-org.libreccm.core.UserRepositoryTest.war")
.addPackage(User.class.getPackage())
- .addPackage(org.libreccm.web.Application.class.getPackage())
+ .addPackage(org.libreccm.web.CcmApplication.class.getPackage())
.addPackage(org.libreccm.categorization.Category.class.
getPackage())
.addPackage(org.libreccm.l10n.LocalizedString.class.getPackage()).
diff --git a/ccm-core/src/test/java/org/libreccm/core/PrivilegeRepositoryTest.java b/ccm-core/src/test/java/org/libreccm/core/PrivilegeRepositoryTest.java
index 903d148ac..5455a834b 100644
--- a/ccm-core/src/test/java/org/libreccm/core/PrivilegeRepositoryTest.java
+++ b/ccm-core/src/test/java/org/libreccm/core/PrivilegeRepositoryTest.java
@@ -101,7 +101,7 @@ public class PrivilegeRepositoryTest {
.create(WebArchive.class,
"LibreCCM-org.libreccm.core.UserRepositoryTest.war")
.addPackage(User.class.getPackage())
- .addPackage(org.libreccm.web.Application.class.getPackage())
+ .addPackage(org.libreccm.web.CcmApplication.class.getPackage())
.addPackage(org.libreccm.categorization.Category.class.
getPackage())
.addPackage(org.libreccm.l10n.LocalizedString.class.getPackage()).
diff --git a/ccm-core/src/test/java/org/libreccm/core/RoleRepositoryTest.java b/ccm-core/src/test/java/org/libreccm/core/RoleRepositoryTest.java
index 36d59d1a2..1146d06e9 100644
--- a/ccm-core/src/test/java/org/libreccm/core/RoleRepositoryTest.java
+++ b/ccm-core/src/test/java/org/libreccm/core/RoleRepositoryTest.java
@@ -103,7 +103,7 @@ public class RoleRepositoryTest {
.create(WebArchive.class,
"LibreCCM-org.libreccm.core.RoleRepositoryTest.war")
.addPackage(User.class.getPackage())
- .addPackage(org.libreccm.web.Application.class.getPackage())
+ .addPackage(org.libreccm.web.CcmApplication.class.getPackage())
.addPackage(org.libreccm.categorization.Category.class.
getPackage())
.addPackage(org.libreccm.l10n.LocalizedString.class.getPackage()).
diff --git a/ccm-core/src/test/java/org/libreccm/core/ToStringTest.java b/ccm-core/src/test/java/org/libreccm/core/ToStringTest.java
index 5f502a490..c8ebdd5cd 100644
--- a/ccm-core/src/test/java/org/libreccm/core/ToStringTest.java
+++ b/ccm-core/src/test/java/org/libreccm/core/ToStringTest.java
@@ -26,7 +26,6 @@ import org.libreccm.tests.categories.UnitTest;
import java.util.Arrays;
import java.util.Collection;
-import org.libreccm.web.ApplicationType;
/**
*
@@ -39,7 +38,6 @@ public class ToStringTest extends ToStringVerifier {
@Parameterized.Parameters(name = "{0}")
public static Collection