text:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information
namespace DotNetNuke.UI.Skins
{
    using System;
    using System.Collections;
    using System.IO;
    using System.Text.RegularExpressions;
    using System.Web;
    using System.Xml;

    using DotNetNuke.Common;
    using DotNetNuke.Entities.Modules;
    using DotNetNuke.Instrumentation;
    using DotNetNuke.Services.Installer;

    public enum SkinParser
    {
        Localized = 0,
        Portable = 1,
    }

    /// -----------------------------------------------------------------------------
    /// Project  : DotNetNuke
    /// Class    : SkinFileProcessor
    ///
    /// -----------------------------------------------------------------------------
    /// <summary>
    ///     Handles processing of a list of uploaded skin files into a working skin.
    /// </summary>
    /// <remarks>
    /// </remarks>
    /// -----------------------------------------------------------------------------
    public class SkinFileProcessor
    {
        private static readonly ILog Logger = LoggerSource.Instance.GetLogger(typeof(SkinFileProcessor));
        private readonly string DUPLICATE_DETAIL = Util.GetLocalizedString("DuplicateSkinObject.Detail");
        private readonly string DUPLICATE_ERROR = Util.GetLocalizedString("DuplicateSkinObject.Error");
        private readonly string FILES_END = Util.GetLocalizedString("EndSkinFiles");
        private readonly string FILE_BEGIN = Util.GetLocalizedString("BeginSkinFile");
        private readonly string FILE_END = Util.GetLocalizedString("EndSkinFile");
        private readonly string INITIALIZE_PROCESSOR = Util.GetLocalizedString("StartProcessor");
        private readonly string LOAD_SKIN_TOKEN = Util.GetLocalizedString("LoadingSkinToken");
        private readonly string PACKAGE_LOAD = Util.GetLocalizedString("PackageLoad");
        private readonly string PACKAGE_LOAD_ERROR = Util.GetLocalizedString("PackageLoad.Error");
        private readonly ControlParser m_ControlFactory;
        private readonly Hashtable m_ControlList = new Hashtable();
        private readonly ObjectParser m_ObjectFactory;
        private readonly PathParser m_PathFactory = new PathParser();
        private readonly XmlDocument m_SkinAttributes = new XmlDocument { XmlResolver = null };
        private readonly string m_SkinName;
        private readonly string m_SkinPath;
        private readonly string m_SkinRoot;
        private string m_Message = string.Empty;

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// Initializes a new instance of the <see cref="SkinFileProcessor"/> class.
        ///     SkinFileProcessor class constructor.
        /// </summary>
        /// <remarks>
        ///     This constructor parses a memory based skin.
        /// </remarks>
        /// -----------------------------------------------------------------------------
        public SkinFileProcessor(string ControlKey, string ControlSrc)
        {
            this.m_ControlList.Add(ControlKey, ControlSrc);

            // Instantiate the control parser with the list of skin objects
            this.m_ControlFactory = new ControlParser(this.m_ControlList);

            // Instantiate the object parser with the list of skin objects
            this.m_ObjectFactory = new ObjectParser(this.m_ControlList);
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        /// Initializes a new instance of the <see cref="SkinFileProcessor"/> class.
        ///     SkinFileProcessor class constructor.
        /// </summary>
        /// <param name="SkinPath">File path to the portals upload directory.</param>
        /// <param name="SkinRoot">Specifies type of skin (Skins or Containers).</param>
        /// <param name="SkinName">Name of folder in which skin will reside (Zip file name).</param>
        /// <remarks>
        ///     The constructor primes the file processor with path information and
        ///     control data that should only be retrieved once.  It checks for the
        ///     existentce of a skin level attribute file and read it in, if found.
        ///     It also sorts through the complete list of controls and creates
        ///     a hashtable which contains only the skin objects and their source paths.
        ///     These are recognized by their ControlKey's which are formatted like
        ///     tokens ("[TOKEN]").  The hashtable is required for speed as it will be
        ///     processed for each token found in the source file by the Control Parser.
        /// </remarks>
        /// -----------------------------------------------------------------------------
        public SkinFileProcessor(string SkinPath, string SkinRoot, string SkinName)
        {
            this.Message += SkinController.FormatMessage(this.INITIALIZE_PROCESSOR, SkinRoot + " :: " + SkinName, 0, false);

            // Save path information for future use
            this.m_SkinRoot = SkinRoot;
            this.m_SkinPath = SkinPath;
            this.m_SkinName = SkinName;

            // Check for and read skin package level attribute information file
            string FileName = this.SkinPath + this.SkinRoot + "\\" + this.SkinName + "\\" + SkinRoot.Substring(0, SkinRoot.Length - 1) + ".xml";
            if (File.Exists(FileName))
            {
                try
                {
                    this.SkinAttributes.Load(FileName);
                    this.Message += SkinController.FormatMessage(this.PACKAGE_LOAD, Path.GetFileName(FileName), 2, false);
                }
                catch (Exception ex)
                {
                    // could not load XML file
                    Logger.Error(ex);
                    this.Message += SkinController.FormatMessage(string.Format(this.PACKAGE_LOAD_ERROR, ex.Message), Path.GetFileName(FileName), 2, true);
                }
            }

            // Look at every control
            string Token;
            foreach (SkinControlInfo objSkinControl in SkinControlController.GetSkinControls().Values)
            {
                Token = objSkinControl.ControlKey.ToUpper();

                // If the control is already in the hash table
                if (this.m_ControlList.ContainsKey(Token))
                {
                    this.Message += SkinController.FormatMessage(
                        string.Format(this.DUPLICATE_ERROR, Token),
                        string.Format(this.DUPLICATE_DETAIL, this.m_ControlList[Token], objSkinControl.ControlSrc),
                        2,
                        true);
                }
                else
                {
                    // Add it
                    this.Message += SkinController.FormatMessage(string.Format(this.LOAD_SKIN_TOKEN, Token), objSkinControl.ControlSrc, 2, false);
                    this.m_ControlList.Add(Token, objSkinControl.ControlSrc);
                }
            }

            // Instantiate the control parser with the list of skin objects
            this.m_ControlFactory = new ControlParser(this.m_ControlList);

            // Instantiate the object parser with the list of skin objects
            this.m_ObjectFactory = new ObjectParser(this.m_ControlList);
        }

        public string SkinRoot
        {
            get
            {
                return this.m_SkinRoot;
            }
        }

        public string SkinPath
        {
            get
            {
                return this.m_SkinPath;
            }
        }

        public string SkinName
        {
            get
            {
                return this.m_SkinName;
            }
        }

        private PathParser PathFactory
        {
            get
            {
                return this.m_PathFactory;
            }
        }

        private ControlParser ControlFactory
        {
            get
            {
                return this.m_ControlFactory;
            }
        }

        private ObjectParser ObjectFactory
        {
            get
            {
                return this.m_ObjectFactory;
            }
        }

        private XmlDocument SkinAttributes
        {
            get
            {
                return this.m_SkinAttributes;
            }
        }

        private string Message
        {
            get
            {
                return this.m_Message;
            }

            set
            {
                this.m_Message = value;
            }
        }

        public string ProcessFile(string FileName, SkinParser ParseOption)
        {
            string strMessage = SkinController.FormatMessage(this.FILE_BEGIN, Path.GetFileName(FileName), 0, false);
            var objSkinFile = new SkinFile(this.SkinRoot, FileName, this.SkinAttributes);
            switch (objSkinFile.FileExtension)
            {
                case ".htm":
                case ".html":
                    string contents = objSkinFile.Contents;
                    strMessage += this.ObjectFactory.Parse(ref contents);
                    strMessage += this.PathFactory.Parse(ref contents, this.PathFactory.HTMLList, objSkinFile.SkinRootPath, ParseOption);
                    strMessage += this.ControlFactory.Parse(ref contents, objSkinFile.Attributes);
                    objSkinFile.Contents = contents;
                    var Registrations = new ArrayList();
                    Registrations.AddRange(this.ControlFactory.Registrations);
                    Registrations.AddRange(this.ObjectFactory.Registrations);
                    strMessage += objSkinFile.PrependASCXDirectives(Registrations);
                    break;
            }

            objSkinFile.Write();
            strMessage += objSkinFile.Messages;
            strMessage += SkinController.FormatMessage(this.FILE_END, Path.GetFileName(FileName), 1, false);
            return strMessage;
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///     Perform processing on list of files to generate skin.
        /// </summary>
        /// <param name="FileList">ArrayList of files to be processed.</param>
        /// <returns>HTML formatted string of informational messages.</returns>
        /// <remarks>
        /// </remarks>
        /// -----------------------------------------------------------------------------
        public string ProcessList(ArrayList FileList)
        {
            return this.ProcessList(FileList, SkinParser.Localized);
        }

        public string ProcessList(ArrayList FileList, SkinParser ParseOption)
        {
            foreach (string FileName in FileList)
            {
                this.Message += this.ProcessFile(FileName, ParseOption);
            }

            this.Message += SkinController.FormatMessage(this.FILES_END, this.SkinRoot + " :: " + this.SkinName, 0, false);
            return this.Message;
        }

        public string ProcessSkin(string SkinSource, XmlDocument SkinAttributes, SkinParser ParseOption)
        {
            var objSkinFile = new SkinFile(SkinSource, SkinAttributes);
            string contents = objSkinFile.Contents;
            this.Message += this.ControlFactory.Parse(ref contents, objSkinFile.Attributes);
            this.Message += objSkinFile.PrependASCXDirectives(this.ControlFactory.Registrations);
            return contents;
        }

        /// -----------------------------------------------------------------------------
        /// Project  : DotNetNuke
        /// Class    : SkinFileProcessor.ControlParser
        ///
        /// -----------------------------------------------------------------------------
        /// <summary>
        ///     Parsing functionality for token replacement in new skin files.
        /// </summary>
        /// <remarks>
        ///     This class encapsulates the data and methods necessary to appropriately
        ///     handle all the token parsing needs for new skin files (which is appropriate
        ///     only for HTML files).  The parser accomodates some ill formatting of tokens
        ///     (ignoring whitespace and casing) and allows for naming of token instances
        ///     if more than one instance of a particular control is desired on a skin.  The
        ///     proper syntax for an instance is: "[TOKEN:INSTANCE]" where the instance can
        ///     be any alphanumeric string.  Generated control ID's all take the
        ///     form of "TOKENINSTANCE".
        /// </remarks>
        /// -----------------------------------------------------------------------------
        private class ControlParser
        {
            private static readonly Regex FindTokenInstance =
                new Regex("\\[\\s*(?<token>\\w*)\\s*:?\\s*(?<instance>\\w*)\\s*]", RegexOptions.IgnoreCase | RegexOptions.Compiled);

            private readonly Hashtable m_ControlList;
            private XmlDocument m_Attributes = new XmlDocument { XmlResolver = null };
            private string m_ParseMessages = string.Empty;
            private ArrayList m_RegisterList = new ArrayList();

            /// -----------------------------------------------------------------------------
            /// <summary>
            /// Initializes a new instance of the <see cref="ControlParser"/> class.
            ///     ControlParser class constructor.
            /// </summary>
            /// <remarks>
            ///     The constructor processes accepts a hashtable of skin objects to process against.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public ControlParser(Hashtable ControlList)
            {
                this.m_ControlList = (Hashtable)ControlList.Clone();
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Gets registration directives generated as a result of the Parse method.
            /// </summary>
            /// <returns>ArrayList of formatted registration directives.</returns>
            /// <remarks>
            ///     In addition to the updated file contents, the Parse method also
            ///     creates this list of formatted registration directives which can
            ///     be processed later.  They are not performed in place during the
            ///     Parse method in order to preserve the formatting of the input file
            ///     in case additional parsing might not anticipate the formatting of
            ///     those directives.  Since they are properly formatted, it is better
            ///     to exclude them from being subject to parsing.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            internal ArrayList Registrations
            {
                get
                {
                    return this.m_RegisterList;
                }
            }

            private MatchEvaluator Handler
            {
                get
                {
                    return this.TokenMatchHandler;
                }
            }

            private Hashtable ControlList
            {
                get
                {
                    return this.m_ControlList;
                }
            }

            private ArrayList RegisterList
            {
                get
                {
                    return this.m_RegisterList;
                }

                set
                {
                    this.m_RegisterList = value;
                }
            }

            private XmlDocument Attributes
            {
                get
                {
                    return this.m_Attributes;
                }

                set
                {
                    this.m_Attributes = value;
                }
            }

            private string Messages
            {
                get
                {
                    return this.m_ParseMessages;
                }

                set
                {
                    this.m_ParseMessages = value;
                }
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Perform parsing on the specified source file using the specified attributes.
            /// </summary>
            /// <param name="Source">Pointer to Source string to be parsed.</param>
            /// <param name="Attributes">XML document containing token attribute information (can be empty).</param>
            /// <remarks>
            ///     This procedure invokes a handler for each match of a formatted token.
            ///     The attributes are first set because they will be referenced by the
            ///     match handler.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public string Parse(ref string Source, XmlDocument Attributes)
            {
                this.Messages = string.Empty;

                // set the token attributes
                this.Attributes = Attributes;

                // clear register list
                this.RegisterList.Clear();

                // define the regular expression to match tokens

                // parse the file
                Source = FindTokenInstance.Replace(Source, this.Handler);
                return this.Messages;
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Process regular expression matches.
            /// </summary>
            /// <param name="m">Regular expression match for token which requires processing.</param>
            /// <returns>Properly formatted token.</returns>
            /// <remarks>
            ///     The handler is invoked by the Regex.Replace method once for each match that
            ///     it encounters.  The returned value of the handler is substituted for the
            ///     original match.  So the handler properly formats the replacement for the
            ///     token and returns it instead.  If an unknown token is encountered, the token
            ///     is unmodified.  This can happen if a token is used for a skin object which
            ///     has not yet been installed.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            private string TokenMatchHandler(Match m)
            {
                string TOKEN_PROC = Util.GetLocalizedString("ProcessToken");
                string TOKEN_SKIN = Util.GetLocalizedString("SkinToken");
                string TOKEN_PANE = Util.GetLocalizedString("PaneToken");
                string TOKEN_FOUND = Util.GetLocalizedString("TokenFound");
                string TOKEN_FORMAT = Util.GetLocalizedString("TokenFormat");
                string TOKEN_NOTFOUND_INFILE = Util.GetLocalizedString("TokenNotFoundInFile");
                string CONTROL_FORMAT = Util.GetLocalizedString("ControlFormat");
                string TOKEN_NOTFOUND = Util.GetLocalizedString("TokenNotFound");

                string Token = m.Groups["token"].Value.ToUpper();
                string ControlName = Token + m.Groups["instance"].Value;

                // if the token has an instance name, use it to look for the corresponding attributes
                string AttributeNode = Token + (string.IsNullOrEmpty(m.Groups["instance"].Value) ? string.Empty : ":" + m.Groups["instance"].Value);

                this.Messages += SkinController.FormatMessage(TOKEN_PROC, "[" + AttributeNode + "]", 2, false);

                // if the token is a recognized skin control
                if (this.ControlList.ContainsKey(Token) || Token.IndexOf("CONTENTPANE") != -1)
                {
                    string SkinControl = string.Empty;

                    if (this.ControlList.ContainsKey(Token))
                    {
                        this.Messages += SkinController.FormatMessage(TOKEN_SKIN, (string)this.ControlList[Token], 2, false);
                    }
                    else
                    {
                        this.Messages += SkinController.FormatMessage(TOKEN_PANE, Token, 2, false);
                    }

                    // f there is an attribute file
                    if (this.Attributes.DocumentElement != null)
                    {
                        // look for the the node of this instance of the token
                        XmlNode xmlSkinAttributeRoot = this.Attributes.DocumentElement.SelectSingleNode("descendant::Object[Token='[" + AttributeNode + "]']");

                        // if the token is found
                        if (xmlSkinAttributeRoot != null)
                        {
                            this.Messages += SkinController.FormatMessage(TOKEN_FOUND, "[" + AttributeNode + "]", 2, false);

                            // process each token attribute
                            foreach (XmlNode xmlSkinAttribute in xmlSkinAttributeRoot.SelectNodes(".//Settings/Setting"))
                            {
                                if (!string.IsNullOrEmpty(xmlSkinAttribute.SelectSingleNode("Value").InnerText))
                                {
                                    // append the formatted attribute to the inner contents of the control statement
                                    this.Messages += SkinController.FormatMessage(
                                        TOKEN_FORMAT,
                                        xmlSkinAttribute.SelectSingleNode("Name").InnerText + "=\"" + xmlSkinAttribute.SelectSingleNode("Value").InnerText + "\"",
                                        2,
                                        false);
                                    SkinControl += " " + xmlSkinAttribute.SelectSingleNode("Name").InnerText + "=\"" + xmlSkinAttribute.SelectSingleNode("Value").InnerText.Replace("\"", """) +
                                                   "\"";
                                }
                            }
                        }
                        else
                        {
                            this.Messages += SkinController.FormatMessage(TOKEN_NOTFOUND_INFILE, "[" + AttributeNode + "]", 2, false);
                        }
                    }

                    if (this.ControlList.ContainsKey(Token))
                    {
                        // create the skin object user control tag
                        SkinControl = "dnn:" + Token + " runat=\"server\" id=\"dnn" + ControlName + "\"" + SkinControl;

                        // save control registration statement
                        string ControlRegistration = "<%@ Register TagPrefix=\"dnn\" TagName=\"" + Token + "\" Src=\"~/" + (string)this.ControlList[Token] + "\" %>" + Environment.NewLine;
                        if (this.RegisterList.Contains(ControlRegistration) == false)
                        {
                            this.RegisterList.Add(ControlRegistration);
                        }

                        // return the control statement
                        this.Messages += SkinController.FormatMessage(CONTROL_FORMAT, "<" + SkinControl + " />", 2, false);

                        SkinControl = "<" + SkinControl + " />";
                    }
                    else
                    {
                        if (SkinControl.IndexOf("id=", StringComparison.InvariantCultureIgnoreCase) == -1)
                        {
                            SkinControl = " id=\"ContentPane\"";
                        }

                        SkinControl = "div runat=\"server\"" + SkinControl + "></div";

                        // return the control statement
                        this.Messages += SkinController.FormatMessage(CONTROL_FORMAT, "<" + SkinControl + ">", 2, false);

                        SkinControl = "<" + SkinControl + ">";
                    }

                    return SkinControl;
                }
                else
                {
                    // return the unmodified token
                    // note that this is currently protecting array syntax in embedded javascript
                    // should be fixed in the regular expressions but is not, currently.
                    this.Messages += SkinController.FormatMessage(TOKEN_NOTFOUND, "[" + m.Groups["token"].Value + "]", 2, false);
                    return "[" + m.Groups["token"].Value + "]";
                }
            }
        }

        /// -----------------------------------------------------------------------------
        /// Project  : DotNetNuke
        /// Class    : SkinFileProcessor.ObjectParser
        ///
        /// -----------------------------------------------------------------------------
        /// <summary>
        ///     Parsing functionality for token replacement in new skin files.
        /// </summary>
        /// <remarks>
        ///     This class encapsulates the data and methods necessary to appropriately
        ///     handle all the object parsing needs for new skin files (which is appropriate
        ///     only for HTML files).  The parser accomodates some ill formatting of objects
        ///     (ignoring whitespace and casing) and allows for naming of object instances
        ///     if more than one instance of a particular control is desired on a skin.  The
        ///     proper syntax for an instance is: "[OBJECT:INSTANCE]" where the instance can
        ///     be any alphanumeric string.  Generated control ID's all take the
        ///     form of "OBJECTINSTANCE".
        /// </remarks>
        /// -----------------------------------------------------------------------------
        private class ObjectParser
        {
            // define the regular expression to match objects
            private static readonly Regex FindObjectInstance =
                new Regex("\\<object(?<token>.*?)</object>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled);

            private static readonly Regex MultiSpaceRegex = new Regex("\\s+", RegexOptions.Compiled);

            private readonly Hashtable m_ControlList;
            private string m_ParseMessages = string.Empty;
            private ArrayList m_RegisterList = new ArrayList();

            /// -----------------------------------------------------------------------------
            /// <summary>
            /// Initializes a new instance of the <see cref="ObjectParser"/> class.
            ///     ControlParser class constructor.
            /// </summary>
            /// <remarks>
            ///     The constructor processes accepts a hashtable of skin objects to process against.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public ObjectParser(Hashtable ControlList)
            {
                this.m_ControlList = (Hashtable)ControlList.Clone();
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Gets registration directives generated as a result of the Parse method.
            /// </summary>
            /// <returns>ArrayList of formatted registration directives.</returns>
            /// <remarks>
            ///     In addition to the updated file contents, the Parse method also
            ///     creates this list of formatted registration directives which can
            ///     be processed later.  They are not performed in place during the
            ///     Parse method in order to preserve the formatting of the input file
            ///     in case additional parsing might not anticipate the formatting of
            ///     those directives.  Since they are properly formatted, it is better
            ///     to exclude them from being subject to parsing.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            internal ArrayList Registrations
            {
                get
                {
                    return this.m_RegisterList;
                }
            }

            private MatchEvaluator Handler
            {
                get
                {
                    return this.ObjectMatchHandler;
                }
            }

            private Hashtable ControlList
            {
                get
                {
                    return this.m_ControlList;
                }
            }

            private ArrayList RegisterList
            {
                get
                {
                    return this.m_RegisterList;
                }

                set
                {
                    this.m_RegisterList = value;
                }
            }

            private string Messages
            {
                get
                {
                    return this.m_ParseMessages;
                }

                set
                {
                    this.m_ParseMessages = value;
                }
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Perform parsing on the specified source file.
            /// </summary>
            /// <param name="Source">Pointer to Source string to be parsed.</param>
            /// <remarks>
            ///     This procedure invokes a handler for each match of a formatted object.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public string Parse(ref string Source)
            {
                this.Messages = string.Empty;

                // clear register list
                this.RegisterList.Clear();

                // parse the file
                Source = FindObjectInstance.Replace(Source, this.Handler);

                return this.Messages;
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Process regular expression matches.
            /// </summary>
            /// <param name="m">Regular expression match for object which requires processing.</param>
            /// <returns>Properly formatted token.</returns>
            /// <remarks>
            ///     The handler is invoked by the Regex.Replace method once for each match that
            ///     it encounters.  The returned value of the handler is substituted for the
            ///     original match.  So the handler properly formats the replacement for the
            ///     object and returns it instead.  If an unknown object is encountered, the object
            ///     is unmodified.  This can happen if an object is a client-side object or
            ///     has not yet been installed.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            private string ObjectMatchHandler(Match m)
            {
                string OBJECT_PROC = Util.GetLocalizedString("ProcessObject");
                string OBJECT_SKIN = Util.GetLocalizedString("SkinObject");
                string OBJECT_PANE = Util.GetLocalizedString("PaneObject");
                string CONTROL_FORMAT = Util.GetLocalizedString("ControlFormat");
                string OBJECT_NOTFOUND = Util.GetLocalizedString("ObjectNotFound");

                // "token" string matches will be in the form of (" id=".." codetype=".." codebase=".." etc...><param name=".." value=".." />")
                // we need to assume properly formatted HTML - attributes will be enclosed in double quotes and there will no spaces between assignments ( ie. attribute="value" )

                // extract the embedded object attributes (" id=".." codetype=".." codebase=".." etc...")
                string EmbeddedObjectAttributes = m.Groups["token"].Value.Substring(0, m.Groups["token"].Value.IndexOf(">"));

                // split into array
                string[] Attributes = EmbeddedObjectAttributes.Split(' ');

                // declare skin object elements
                string AttributeNode = string.Empty;
                string Token = string.Empty;
                string ControlName = string.Empty;

                // iterate and process valid attributes
                string[] Attribute;
                string AttributeName;
                string AttributeValue;
                foreach (string strAttribute in Attributes)
                {
                    if (strAttribute != string.Empty)
                    {
                        Attribute = strAttribute.Split('=');
                        AttributeName = Attribute[0].Trim();
                        AttributeValue = Attribute[1].Trim().Replace("\"", string.Empty);
                        switch (AttributeName.ToLowerInvariant())
                        {
                            case "id":
                                ControlName = AttributeValue;
                                break;
                            case "codetype":
                                AttributeNode = AttributeValue;
                                break;
                            case "codebase":
                                Token = AttributeValue.ToUpper();
                                break;
                        }
                    }
                }

                // process skin object
                if (AttributeNode.Equals("dotnetnuke/server", StringComparison.InvariantCultureIgnoreCase))
                {
                    // we have a valid skin object specification
                    this.Messages += SkinController.FormatMessage(OBJECT_PROC, Token, 2, false);

                    // if the embedded object is a recognized skin object
                    if (this.ControlList.ContainsKey(Token) || Token == "CONTENTPANE")
                    {
                        string SkinControl = string.Empty;

                        if (this.ControlList.ContainsKey(Token))
                        {
                            this.Messages += SkinController.FormatMessage(OBJECT_SKIN, (string)this.ControlList[Token], 2, false);
                        }
                        else
                        {
                            this.Messages += SkinController.FormatMessage(OBJECT_PANE, Token, 2, false);
                        }

                        // process embedded object params
                        string Parameters = m.Groups["token"].Value.Substring(m.Groups["token"].Value.IndexOf(">") + 1);
                        Parameters = Parameters.Replace("<param name=\"", string.Empty);
                        Parameters = Parameters.Replace("\" value", string.Empty);
                        Parameters = Parameters.Replace("/>", string.Empty);

                        // convert multiple spaces and carriage returns into single spaces
                        Parameters = MultiSpaceRegex.Replace(Parameters, " ");

                        if (this.ControlList.ContainsKey(Token))
                        {
                            // create the skin object user control tag
                            SkinControl = "dnn:" + Token + " runat=\"server\" ";
                            if (!string.IsNullOrEmpty(ControlName))
                            {
                                SkinControl += "id=\"" + ControlName + "\" ";
                            }

                            SkinControl += Parameters;

                            // save control registration statement
                            string ControlRegistration = "<%@ Register TagPrefix=\"dnn\" TagName=\"" + Token + "\" Src=\"~/" + (string)this.ControlList[Token] + "\" %>" + Environment.NewLine;
                            if (this.RegisterList.Contains(ControlRegistration) == false)
                            {
                                this.RegisterList.Add(ControlRegistration);
                            }

                            // return the control statement
                            this.Messages += SkinController.FormatMessage(CONTROL_FORMAT, "<" + SkinControl + " />", 2, false);
                            SkinControl = "<" + SkinControl + "/>";
                        }
                        else
                        {
                            SkinControl = "div runat=\"server\" ";
                            if (!string.IsNullOrEmpty(ControlName))
                            {
                                SkinControl += "id=\"" + ControlName + "\" ";
                            }
                            else
                            {
                                SkinControl += "id=\"ContentPane\" ";
                            }

                            SkinControl += Parameters + "></div";

                            // return the control statement
                            this.Messages += SkinController.FormatMessage(CONTROL_FORMAT, "<" + SkinControl + ">", 2, false);
                            SkinControl = "<" + SkinControl + ">";
                        }

                        return SkinControl;
                    }
                    else
                    {
                        // return the unmodified embedded object
                        this.Messages += SkinController.FormatMessage(OBJECT_NOTFOUND, Token, 2, false);
                        return "<object" + m.Groups["token"].Value + "</object>";
                    }
                }
                else
                {
                    // return unmodified embedded object
                    return "<object" + m.Groups["token"].Value + "</object>";
                }
            }
        }

        /// -----------------------------------------------------------------------------
        /// Project  : DotNetNuke
        /// Class    : SkinFileProcessor.PathParser
        ///
        /// -----------------------------------------------------------------------------
        /// <summary>
        ///     Parsing functionality for path replacement in new skin files.
        /// </summary>
        /// <remarks>
        ///     This class encapsulates the data and methods necessary to appropriately
        ///     handle all the path replacement parsing needs for new skin files. Parsing
        ///     supported for CSS syntax and HTML syntax (which covers ASCX files also).
        /// </remarks>
        /// -----------------------------------------------------------------------------
        private class PathParser
        {
            private const RegexOptions PatternOptions = RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.Compiled;

            // retrieve the patterns
            private static readonly Regex[] HtmlArrayPattern =
            {
                new Regex("(?<tag><head[^>]*?\\sprofile\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><object[^>]*?\\s(?:codebase|data|usemap)\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><img[^>]*?\\s(?:src|longdesc|usemap)\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><input[^>]*?\\s(?:src|usemap)\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><iframe[^>]*?\\s(?:src|longdesc)\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><(?:td|th|table|body)[^>]*?\\sbackground\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><(?:script|bgsound|embed|xml|frame)[^>]*?\\ssrc\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><(?:base|link|a|area)[^>]*?\\shref\\s*=\\s*\")(?!https://|http://|\\\\|[~/]|javascript:|mailto:)(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><(?:blockquote|ins|del|q)[^>]*?\\scite\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><(?:param\\s+name\\s*=\\s*\"(?:movie|src|base)\")[^>]*?\\svalue\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
                new Regex("(?<tag><embed[^>]*?\\s(?:src)\\s*=\\s*\")(?!https://|http://|\\\\|[~/])(?<content>[^\"]*)(?<endtag>\"[^>]*>)", PatternOptions),
            };

            // retrieve the patterns
            private static readonly Regex[] CssArrayPattern =
            {
                new Regex("(?<tag>\\surl\\u0028)(?<content>[^\\u0029]*)(?<endtag>\\u0029.*;)", PatternOptions),
            };

            private readonly string SUBST = Util.GetLocalizedString("Substituting");
            private readonly string SUBST_DETAIL = Util.GetLocalizedString("Substituting.Detail");
            private readonly ArrayList m_CSSPatterns = new ArrayList();
            private readonly ArrayList m_HTMLPatterns = new ArrayList();
            private string m_Messages = string.Empty;
            private string m_SkinPath = string.Empty;

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Gets list of regular expressions for processing HTML syntax.
            /// </summary>
            /// <returns>ArrayList of Regex objects formatted for the Parser method.</returns>
            /// <remarks>
            ///     Additional patterns can be added to this list (if necessary) if properly
            ///     formatted to return <tag/>, <content/> and <endtag/> groups.  For future
            ///     consideration, this list could be imported from a configuration file to
            ///     provide for greater flexibility.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public ArrayList HTMLList
            {
                get
                {
                    // if the arraylist in uninitialized
                    if (this.m_HTMLPatterns.Count == 0)
                    {
                        // for each pattern, create a regex object
                        this.m_HTMLPatterns.AddRange(HtmlArrayPattern);

                        // optimize the arraylist size since it will not change
                        this.m_HTMLPatterns.TrimToSize();
                    }

                    return this.m_HTMLPatterns;
                }
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Gets list of regular expressions for processing CSS syntax.
            /// </summary>
            /// <returns>ArrayList of Regex objects formatted for the Parser method.</returns>
            /// <remarks>
            ///     Additional patterns can be added to this list (if necessary) if properly
            ///     formatted to return <tag/>, <content/> and <endtag/> groups.  For future
            ///     consideration, this list could be imported from a configuration file to
            ///     provide for greater flexibility.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public ArrayList CSSList
            {
                get
                {
                    // if the arraylist in uninitialized
                    if (this.m_CSSPatterns.Count == 0)
                    {
                        // for each pattern, create a regex object
                        this.m_CSSPatterns.AddRange(CssArrayPattern);

                        // optimize the arraylist size since it will not change
                        this.m_CSSPatterns.TrimToSize();
                    }

                    return this.m_CSSPatterns;
                }
            }

            private MatchEvaluator Handler
            {
                get
                {
                    return this.MatchHandler;
                }
            }

            private string SkinPath
            {
                get
                {
                    return this.m_SkinPath;
                }

                set
                {
                    this.m_SkinPath = value;
                }
            }

            private SkinParser ParseOption { get; set; }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Perform parsing on the specified source file.
            /// </summary>
            /// <param name="Source">Pointer to Source string to be parsed.</param>
            /// <param name="RegexList">ArrayList of properly formatted regular expression objects.</param>
            /// <param name="SkinPath">Path to use in replacement operation.</param>
            /// <param name="ParseOption">Parse Opition.</param>
            /// <remarks>
            ///     This procedure iterates through the list of regular expression objects
            ///     and invokes a handler for each match which uses the specified path.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public string Parse(ref string Source, ArrayList RegexList, string SkinPath, SkinParser ParseOption)
            {
                this.m_Messages = string.Empty;

                // set path propery which is file specific
                this.SkinPath = SkinPath;

                // set parse option
                this.ParseOption = ParseOption;

                // process each regular expression
                for (int i = 0; i <= RegexList.Count - 1; i++)
                {
                    Source = ((Regex)RegexList[i]).Replace(Source, this.Handler);
                }

                return this.m_Messages;
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Process regular expression matches.
            /// </summary>
            /// <param name="m">Regular expression match for path information which requires processing.</param>
            /// <returns>Properly formatted path information.</returns>
            /// <remarks>
            ///     The handler is invoked by the Regex.Replace method once for each match that
            ///     it encounters.  The returned value of the handler is substituted for the
            ///     original match.  So the handler properly formats the path information and
            ///     returns it in favor of the improperly formatted match.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            private string MatchHandler(Match m)
            {
                string strOldTag = m.Groups["tag"].Value + m.Groups["content"].Value + m.Groups["endtag"].Value;
                string strNewTag = strOldTag;

                // we do not want to process object tags to DotNetNuke widgets
                if (!m.Groups[0].Value.ToLowerInvariant().Contains("codetype=\"dotnetnuke/client\""))
                {
                    switch (this.ParseOption)
                    {
                        case SkinParser.Localized:
                            // if the tag does not contain the localized path
                            if (strNewTag.IndexOf(this.SkinPath) == -1)
                            {
                                // insert the localized path
                                strNewTag = m.Groups["tag"].Value + this.SkinPath + m.Groups["content"].Value + m.Groups["endtag"].Value;
                            }

                            break;
                        case SkinParser.Portable:
                            // if the tag does not contain a reference to the skinpath
                            if (strNewTag.IndexOf("<%= skinpath %>", StringComparison.InvariantCultureIgnoreCase) == -1)
                            {
                                // insert the skinpath
                                strNewTag = m.Groups["tag"].Value + "<%= SkinPath %>" + m.Groups["content"].Value + m.Groups["endtag"].Value;
                            }

                            // if the tag contains the localized path
                            if (strNewTag.IndexOf(this.SkinPath) != -1)
                            {
                                // remove the localized path
                                strNewTag = strNewTag.Replace(this.SkinPath, string.Empty);
                            }

                            break;
                    }
                }

                this.m_Messages += SkinController.FormatMessage(this.SUBST, string.Format(this.SUBST_DETAIL, HttpUtility.HtmlEncode(strOldTag), HttpUtility.HtmlEncode(strNewTag)), 2, false);
                return strNewTag;
            }
        }

        /// -----------------------------------------------------------------------------
        /// <summary>
        ///     Utility class for processing of skin files.
        /// </summary>
        /// <remarks>
        /// </remarks>
        /// -----------------------------------------------------------------------------
        private class SkinFile
        {
            private const string StrPattern = "<\\s*body[^>]*>(?<skin>.*)<\\s*/\\s*body\\s*>";

            private static readonly Regex PaneCheck1Regex = new Regex("\\s*id\\s*=\\s*\"" + Globals.glbDefaultPane + "\"", RegexOptions.IgnoreCase | RegexOptions.Compiled);
            private static readonly Regex PaneCheck2Regex = new Regex("\\s*[" + Globals.glbDefaultPane + "]", RegexOptions.IgnoreCase | RegexOptions.Compiled);
            private static readonly Regex BodyExtractionRegex = new Regex(StrPattern, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Compiled);

            private readonly string CONTROL_DIR = Util.GetLocalizedString("ControlDirective");
            private readonly string CONTROL_REG = Util.GetLocalizedString("ControlRegister");
            private readonly string FILE_FORMAT_ERROR = Util.GetLocalizedString("FileFormat.Error");
            private readonly string FILE_LOAD = Util.GetLocalizedString("SkinFileLoad");
            private readonly string FILE_LOAD_ERROR = Util.GetLocalizedString("SkinFileLoad.Error");
            private readonly string FILE_WRITE = Util.GetLocalizedString("FileWrite");
            private readonly XmlDocument m_FileAttributes;
            private readonly string m_FileExtension;
            private readonly string m_FileName;
            private readonly string m_SkinRoot;
            private readonly string m_SkinRootPath;
            private readonly string m_WriteFileName;
            private string FILE_FORMAT_DETAIL = Util.GetLocalizedString("FileFormat.Detail");
            private string m_Messages = string.Empty;

            /// -----------------------------------------------------------------------------
            /// <summary>
            /// Initializes a new instance of the <see cref="SkinFile"/> class.
            ///     SkinFile class constructor.
            /// </summary>
            /// <param name="SkinContents"></param>
            /// <param name="SkinAttributes"></param>
            /// <remarks>
            ///     The constructor primes the utility class with basic file information.
            ///     It also checks for the existentce of a skinfile level attribute file
            ///     and read it in, if found.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public SkinFile(string SkinContents, XmlDocument SkinAttributes)
            {
                this.m_FileAttributes = SkinAttributes;
                this.Contents = SkinContents;
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            /// Initializes a new instance of the <see cref="SkinFile"/> class.
            ///     SkinFile class constructor.
            /// </summary>
            /// <param name="SkinRoot"></param>
            /// <param name="FileName"></param>
            /// <param name="SkinAttributes"></param>
            /// <remarks>
            ///     The constructor primes the utility class with basic file information.
            ///     It also checks for the existentce of a skinfile level attribute file
            ///     and read it in, if found.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public SkinFile(string SkinRoot, string FileName, XmlDocument SkinAttributes)
            {
                // capture file information
                this.m_FileName = FileName;
                this.m_FileExtension = Path.GetExtension(FileName);
                this.m_SkinRoot = SkinRoot;
                this.m_FileAttributes = SkinAttributes;

                // determine and store path to portals skin root folder
                string strTemp = FileName.Replace(Path.GetFileName(FileName), string.Empty);
                strTemp = strTemp.Replace("\\", "/");
                this.m_SkinRootPath = Globals.ApplicationPath + strTemp.Substring(strTemp.ToUpper().IndexOf("/PORTALS"));

                // read file contents
                this.Contents = this.Read(FileName);

                // setup some attributes based on file extension
                switch (this.FileExtension)
                {
                    case ".htm":
                    case ".html":
                        // set output file name to <filename>.ASCX
                        this.m_WriteFileName = FileName.Replace(Path.GetExtension(FileName), ".ascx");

                        // capture warning if file does not contain a id="ContentPane" or [CONTENTPANE]
                        if (!PaneCheck1Regex.IsMatch(this.Contents) && !PaneCheck2Regex.IsMatch(this.Contents))
                        {
                            this.m_Messages += SkinController.FormatMessage(this.FILE_FORMAT_ERROR, string.Format(this.FILE_FORMAT_ERROR, FileName), 2, true);
                        }

                        // Check for existence of and load skin file level attribute information
                        if (File.Exists(FileName.Replace(this.FileExtension, ".xml")))
                        {
                            try
                            {
                                this.m_FileAttributes.Load(FileName.Replace(this.FileExtension, ".xml"));
                                this.m_Messages += SkinController.FormatMessage(this.FILE_LOAD, FileName, 2, false);
                            }
                            catch (Exception exc) // could not load XML file
                            {
                                Logger.Error(exc);
                                this.m_FileAttributes = SkinAttributes;
                                this.m_Messages += SkinController.FormatMessage(this.FILE_LOAD_ERROR, FileName, 2, true);
                            }
                        }

                        break;
                    default:
                        // output file name is same as input file name
                        this.m_WriteFileName = FileName;
                        break;
                }
            }

            public string SkinRoot
            {
                get
                {
                    return this.m_SkinRoot;
                }
            }

            public XmlDocument Attributes
            {
                get
                {
                    return this.m_FileAttributes;
                }
            }

            public string Messages
            {
                get
                {
                    return this.m_Messages;
                }
            }

            public string FileName
            {
                get
                {
                    return this.m_FileName;
                }
            }

            public string WriteFileName
            {
                get
                {
                    return this.m_WriteFileName;
                }
            }

            public string FileExtension
            {
                get
                {
                    return this.m_FileExtension;
                }
            }

            public string SkinRootPath
            {
                get
                {
                    return this.m_SkinRootPath;
                }
            }

            public string Contents { get; set; }

            public void Write()
            {
                // delete the file before attempting to write
                if (File.Exists(this.WriteFileName))
                {
                    File.Delete(this.WriteFileName);
                }

                this.m_Messages += SkinController.FormatMessage(this.FILE_WRITE, Path.GetFileName(this.WriteFileName), 2, false);
                using (var objStreamWriter = new StreamWriter(this.WriteFileName))
                {
                    objStreamWriter.WriteLine(this.Contents);
                    objStreamWriter.Flush();
                    objStreamWriter.Close();
                }
            }

            /// -----------------------------------------------------------------------------
            /// <summary>
            ///     Prepend ascx control directives to file contents.
            /// </summary>
            /// <param name="Registrations">ArrayList of registration directives.</param>
            /// <remarks>
            ///     This procedure formats the @Control directive and prepends it and all
            ///     registration directives to the file contents.
            /// </remarks>
            /// -----------------------------------------------------------------------------
            public string PrependASCXDirectives(ArrayList Registrations)
            {
                string Messages = string.Empty;
                string Prefix = string.Empty;

                // format and save @Control directive
                Match objMatch = BodyExtractionRegex.Match(this.Contents);
                if (objMatch.Success && !string.IsNullOrEmpty(objMatch.Groups[1].Value))
                {
                    // if the skin source is an HTML document, extract the content within the <body> tags
                    this.Contents = objMatch.Groups[1].Value;
                }

                if (this.SkinRoot == SkinController.RootSkin)
                {
                    Prefix += "<%@ Control language=\"vb\" AutoEventWireup=\"false\" Explicit=\"True\" Inherits=\"DotNetNuke.UI.Skins.Skin\" %>" + Environment.NewLine;
                }
                else if (this.SkinRoot == SkinController.RootContainer)
                {
                    Prefix += "<%@ Control language=\"vb\" AutoEventWireup=\"false\" Explicit=\"True\" Inherits=\"DotNetNuke.UI.Containers.Container\" %>" + Environment.NewLine;
                }

                Messages += SkinController.FormatMessage(this.CONTROL_DIR, HttpUtility.HtmlEncode(Prefix), 2, false);

                // add preformatted Control Registrations
                foreach (string Item in Registrations)
                {
                    Messages += SkinController.FormatMessage(this.CONTROL_REG, HttpUtility.HtmlEncode(Item), 2, false);
                    Prefix += Item;
                }

                // update file contents to include ascx header information
                this.Contents = Prefix + this.Contents;
                return Messages;
            }

            private string Read(string FileName)
            {
                using (var objStreamReader = new StreamReader(FileName))
                {
                    string strFileContents = objStreamReader.ReadToEnd();
                    objStreamReader.Close();
                    return strFileContents;
                }
            }
        }
    }
}


text:
Competition litigation funding: New UK competition litigation regime brings uncertainties for claimants and funders2015-10-152019-10-24https://www.balancelegalcapital.com/wp-content/uploads/2015/07/trans-blc-horizontal-75.pngBalance Legal Capital - litigation and arbitration financehttps://www.balancelegalcapital.com/wp-content/uploads/2015/10/autumn-daylight-fall-574345.jpg200px200px
As a regular partner to companies seeking competition litigation funding – including funding class actions – Balance Legal Capital has followed changes to the Consumer Rights Act with interest.
On 1 October 2015, the new Consumer Rights Act 2015 (the 'CRA') came into force, bringing with it some significant changes to the UK competition litigation regime. In particular, Section 8 of the CRA introduces a new "opt-out" collective action regime for competition claims. The collective action regime is intended to provide individuals and businesses with an improved route to obtaining redress for losses caused by anti-competitive conduct, which are often ripe for competition litigation funding. Although such actions will often require support from Competition litigation funders like Balance Legal Capital, uncertainties around how the funding of opt-out actions under the CRA will work in practice have called into question the current utility of the new regime. Many collective actions may be unfeasible until these uncertainties are clarified. Add to this the number of issues that have arisen in relation to the transitional provisions and it currently seems unlikely that these reforms will prove much more successful than the old opt-in regime, either for potential claimants or for competition litigation funders.
Opt-out proceedings
The old opt-in collective regime (permitted for follow-on cases only) has been used just once in over ten years. Under the new regime, opt-out collective actions can be brought by a class representative in the UK's Competition Appeal Tribunal (the 'CAT') on behalf of a defined group without the need to identify all individual claimants (or specify their losses). UK domiciled claimants within the class will be automatically included in the action (non-UK claimants can opt–in) unless they take active steps to opt-out. In deciding whether to allow an action to proceed on an opt-out basis, the CAT will take into account the strength of the claims and the practicalities of bringing the proceedings on an opt-out basis.
Collective actions must be certified by the CAT via a Collective Proceedings Order ('CPO'). The CAT will issue a CPO where: it authorises the class representative (which could be a class member or a third party – such as consumer body) to bring the action; and where the claims raise similar or related issues of fact/law and are suitable for resolution via collective proceedings.
Factors for the CAT to take into account in deciding whether it will authorise a representative to bring a class action include: whether the representative can fairly and adequately act in the interests of the class members; whether it has any conflict of interest; and, crucially in the context of competition litigation funding, whether it can pay the defendant's costs if ordered to do so. It will be interesting to see how the CAT deals with situations in which there are competing representatives for a class. Commentators predict that certification will be a key battleground between parties to collective competition actions, as is seen in the US.
The enhanced jurisdiction of the Competition Appeal Tribunal
The new regime significantly extends the jurisdiction of the CAT, promoting its standing as a forum for the types of competition claims often backed by competition litigation funding. The CAT can now hear stand-alone cases (where a party must prove liability as well as damages) in certain circumstances, in addition to follow-on cases (in which a party can rely on a finding by a competition authority). It has the power to award injunctive relief, and will have a limitation period aligned with that in the High Court. The CAT will be able to award damages without assessing quantum for each individual class member. However, a key difference from the US class action regime is that no exemplary damages can be awarded (to safeguard against unmeritorious claims). Any unclaimed damages will be paid to a prescribed charity.
Third party funding of collective competition actions
Collective competition litigation is an area where competition litigation funding can be of particular assistance to claimants, given the high costs involved and the fact that individual losses may be too small to bring as standalone actions. It is unlikely that a single claimant, or consumer body, would be willing to assume responsibility for funding an entire competition litigation action and given that the regime prohibits damages-based agreements ('DBAs') with lawyers, claimants may need to turn to competition litigation funding for support. However, since the CRA fails to directly address the role of competition litigation funding in collective actions, there is some uncertainty over the ability of funders to recoup their returns in successful opt-out actions. The CAT has the power to order unclaimed damages to be paid to the class representative in respect of costs and expenses incurred in collective proceedings, but retains significant discretion as to the level of damages awarded. Competition litigation funders will therefore be interested to see the approach the CAT chooses to take in practice, the scrutiny it gives to any funding arrangements, and whether "costs and expenses" accepted by the CAT will include the full return due to a third party funder.
The uncertainties around competition litigation funding (and the other issues outlined above) represent a missed opportunity for the CRA to make an immediate impact in promoting access to justice for consumers and small businesses, and in significantly improving the attractiveness of the UK as a forum for competition claims. It is hoped that the first round of decisions from the CAT will go some way to clarifying some of these shortcomings, allowing Balance Legal Capital and other competition litigation funders to maintain our active role in this area of access to justice.
Oliver Hayes Partner
Oliver Hayes is a consultant at Balance Legal Capital LLP, a provider of third party funding for international arbitration and litigation, headquartered in London, UK. Prior to joining Balance, Oliver was an associate in the dispute resolution team at Freshfields Bruckhaus Deringer LLP, specializing in contentious matters in the financial services sector.
Third-party funding in arbitration—a funder's view
In Events, Industry News
Balance sponsors CrowdJustice initiative to support legal aid unit
Beyond Grey Hair and Gut Instincts: Using Decision Trees to Supplement Analysis in Litigation
In Events, Seminars
Robert Rothkopf to speak at JLT Commercial Litigation Conference – 12 May 2016
Litigation Superforecasting, Part 3: A Way of Thinking
The Allen & Overy Public International Law Debate
In Litigation Superforecasting
Litigation Superforecasting Survey Results Part 1: Miscommunication in legal advice
Balance joins Association of Litigation Funders
CAT, competition appeal tribunal, competition litigation, consumer rights act, CRA, funding
Financial disputes funding boosted by UK High Court Financial List?Industry News, Opinion

redpajama_set_name: RedPajamaCommonCrawl


Finch's Beer Co. is making their Minnesota debut this week with a calendar full of events and tastings.
Finch's Beer Co. has been brewing quality craft beer in Chicago since 2011 primarily for the local Illinois consumers. Their distribution partnership with Artisan Beer Company is making the Windy City brew to available to Minnesota beginning this week.
Although a fairly young brewery at only 3 years, Finch's is highly regarded. They've won gold medals for their IPA and stout. The Finch's core beers are Golden Wing Blonde Ale, Cutthroat Pale Ale, Threadless IPA, Secret Stache Stout, and Fascist Pig Ale. The pale ale and blonde ale may not make to the Minnesota this year, but the rest of the lineup as well as limited-edition specialties are already showing up on store shelves and in pubs.


It's like riding a motorcycle: "Where you look is where you'll go." Although I don't ride motor bikes, the statement stands.
At one point as a teenager, I remember watching a news report on something that Microsoft was doing (likely, releasing a new version of Windows). I remember thinking to myself: "Wow, it'd be really neat to be in Seattle." That moment, the universe shifted and some years later, I'm living smack dab in the middle of the Pacific Northwest.
I also remember thinking (and telling people) that I couldn't stand to wear ties. The last thing I ever wanted to do was take a job that would require me to get dressed up every day. Lo and behold, I'm typing this out while sitting in a ball cap, t-shirt, and pajama bottoms in my home office on Monday morning.
You make your own luck, good or bad.
I've been in control of my own destiny throughout my adult life, and when I felt that direction slipping away from my own grasp (namely, when I lived in Los Angeles for a couple of years), I snapped back to an internal locus of control and drove my career forward. My quality of life has been pretty good because I find myself thinking of the things I want to do.
Despite experiencing a fair amount of setbacks in recent years, I don't feel I'm any worse for it. My dominant thoughts must thusly be positive (although, I am prone to sharing what's on my mind – even if it sounds like I'm complaining).
As Ponzi and I prepared to leave for our Hawaiian Cruise last week, we had a discussion about different "code words" and phrases that we tend to use. I bet you and your significant other know exactly what we're talking about.
We've decided to start using the word "Chumby" as our code word when we want to avoid an arguement. We're sure this will work. In the past, we've had to use a code word for a stinky person. We've been on many flights where we had to sit next to someone who just smelled so awful. I kept trying to let Chris know that the man next to me was stinky, without letting the man know I was saying something negative about him. You know… you're trying to tell the person next to you something, without conveying to everyone else in the vicinity exactly what it is you're trying to say? So this is what happened to me on this particular fight. We agreed if we ever had that issue again, we'd say "when are we going to play tennis?". That stands for "someone next to me/us STINKS!".
So… back to Chumby. Ponzi and I both have very strong personalities, so we tend to get into arguements at times. We had to pick a word we would both remember, that would hopefully make us laugh, instead of argue. The chat room came up with the word Chumby. Not only is it a funny word in and of itself… it's also my newest fun gadget!!
Go ahead – guess what's happening.


Brian Bress, Blindseye Arranger (Max), 2013. (Video Still).
Blindseye Arranger (Max) (2013) features a greyscale arrangement of rudimentary shapes layered atop one another like a dense cluster of wood block prints, the juxtaposition of sharp lines and acute angles creating an abstracted field of rectangular and triangulated forms composed as if in a cubist landscape. As the video progresses, however, a disembodied hand begins to move these forms, animating a pictorial frame that was previously still. The hand – ostensibly the "arranger" of the works title – functions as a metonym of the artist's hand, quite literally bringing a motionless work to life. The hand in Blindseye Arranger, though, also signals a shift towards the performative, functioning as a reminder that all works of art are created by a maker's "hand" and, in such, are never fully separate from the context in which they are made. Bress's gesture towards interdisciplinarity in his work, by extension, signals an important moment in which questions of medium-specificity give way to more trenchant inquiries into notions of authorship and creative process.


text:
Etihad Unveils New First-Class Amenity Kits
Cristina Gonzalez
With premium cabins like The Residence and The Apartment, Etihad is known for offering top-notch service and an unbelievable hard product (to business and first-class passengers, at least). Now, the airline's adding something extra: a new first-class amenity kit in partnership with designer Christian Lacroix and Hungarian skincare brand Omorovicza.
The new kits are replacing the current version, made in partnership with Le Labo. In 2016, you'll find the new bags on A380 flights between Abu Dhabi and London, New York, Sydney, Melbourne and Mumbai; then fleet-wide in 2017.
Generally, your best option for booking the Etihad Apartment is to redeem American Airlines miles at the following rates:
Abu Dhabi to/from Mumbai: 40,000 miles
Abu Dhabi to/from London: 62,500 miles
Abu Dhabi to/from Australia: 100,000 miles
Abu Dhabi to/from New York: 115,000 miles
Etihad's new first-class amenity kits.
In the first of two phases, Etihad will introduce a purple kit for women and a gray kit for men (phase two introduces burgundy for women and black for men). The men's kit can turn into a wash bag or a storage bag for electronics, while the ladies' case is meant to be up-cycled as a clutch.
But enough about that — what's inside? Swag includes the following products from Omorovicza's Gold collection: lip balm, facial moisturizer, "hand treatment" (we can only assume it's lotion) and cleansing wipes. Omorovicza will also stock the onboard washrooms with face mist, face and hand wash and hand lotion.
Of course, you'll also get the standards: ear plugs, a comb, a toothbrush and toothpaste, socks and eye mask, and a relax pack of pillow mist and pulse point oil. Men get an extra: a shaving kit.
H/T: Point Me to the Plane
What's your favorite airline amenity kit?


The Maison Otis is located in the heart of downtown Baie-Saint-Paul, on St-Jean-Baptiste street. It is the perfect place to stay if you want to discover the beautiful region of Charlevoix. Please don't hesitate to contact us; our team is always available to answer any questions you may have.


Challenge your friends to a game of chess with 32 highly detailed custom sculpted vinyl pieces. Each piece is approx. 3-6 cm tall. This Collectors chess set features a UV finished board and comes in a collector tin.


text:
Lothar Wolleh (* 20. Januar 1930 in Berlin; † 28. September 1979 in London) war ein deutscher Fotograf. Bis Ende der 1960er Jahre arbeitete er als Werbefotograf, dann begann er, international bekannte Maler, Bildhauer und Aktionskünstler zu porträtieren. Insgesamt wurden dabei 109 Künstler fotografiert, darunter bekannte Persönlichkeiten wie Georg Baselitz, Joseph Beuys, Lucio Fontana, Dieter Roth, Jean Tinguely, Man Ray, René Magritte, Günther Uecker, Gerhard Richter oder Christo.

Leben
Lothar Wolleh wurde als erster von vier Söhnen der alleinstehenden Arbeiterin Else Martha Wolleh in Berlin-Wedding geboren. Die Kriegsjahre verbrachte er in Berlin. Die Bombenangriffe der Alliierten, insbesondere der Tod der Familie des Onkels als auch seine Beteiligung "im letzten Aufgebot" im Endkampf um Berlin im April und Mai 1945 hinterließen ihm tiefe psychische Spuren.

Wolleh studierte trotz großer finanzieller Schwierigkeiten von 1946 bis 1947 in der Klasse für Elementarlehre und gegenständliche Malerei bei Ernst Rudolf Vogenauer an der Hochschule für angewandte Kunst in Berlin-Weißensee. Von Dezember 1947 bis Oktober 1949 lebte er in "Boys Town" in Bad Vilbel, einem von der US-Armee unterhaltenen Lager für entwurzelte junge Deutsche, welches auf dem Ansatz von Pater Edward Flanagan basierte.

Wenige Monate nach seiner Rückkehr nach Berlin wurde er im Juli 1950 durch die sowjetische Besatzungsmacht verhaftet und durch ein Sondergericht "OSO" (Fernurteil aus Moskau) wegen angeblicher Spionage und Diversion nach Artikel 58.6 und 58.9 des StGB der RSFSR zu 15 Jahren Arbeitslager verurteilt. Bis 1956 war Wolleh im Arbeitslager Workuta in der UdSSR, wo er Zwangsarbeit in einem Kohleschacht leistete. 1956 konnte Wolleh, aufgrund Konrad Adenauers erfolgreichen Verhandlungen über die Heimkehr deutscher Kriegsgefangener, nach Berlin zurückkehren. Die Folter nach der Verhaftung sowie die harten Haft- und Arbeitsbedingungen im Kohleabbau hinterließen körperliche Schäden und posttraumatische Störungen. Gleichzeitig begründete Workuta seinen ersten Kontakt mit der Fotografie und eine mythische Verehrung des Lichtes.

Nach seiner Freilassung absolvierte er von 1956 bis 1957 eine Ausbildung im Lette-Verein, einer Berufsfachschule für Fotografie, Grafik und Mode in Berlin. Ein mehrmonatiges Erholungsprogramm des Weltkirchenrats für kriegsgeschädigte Jugendliche, das ihm im Jahr 1958 einen Aufenthalt auf der schwedischen Insel Gotland ermöglichte, begründete bei ihm eine lebenslange Zuneigung zu den Menschen und der Landschaft dieser Region. Von 1959 bis 1961 studierte Wolleh an der Folkwangschule für Gestaltung in Essen. Zu seinen Lehrern gehörte der Fotograf Otto Steinert.

Ab 1962 bis zu seinem Tode lebte und arbeitete er in Düsseldorf als freischaffender Fotograf. Zunächst war er vorwiegend in der Werbung tätig und wandte sich später seinem künstlerischen Werk zu. 1964 heiratete er seine Frau Karin. Sein Sohn Oliver kam 1965, seine Tochter Anouchka 1966 zur Welt. 1979 verstarb Lothar Wolleh nach einem Asthmaanfall in London, nachdem er Henry Moore porträtiert hatte. Sein Grab befindet sich auf Gotland.

Werk
Als freier Mitarbeiter für die Werbeagentur TEAM, der auch Helmut Newton angehörte, avancierte Wolleh zu einem der bekanntesten und teuersten Mode-, Werbe- und Porträtfotografen der Bundesrepublik. Zu seinen Kunden gehörten bekannte Unternehmen wie die Deutsche Bundesbahn, Tchibo oder Volkswagen. 1965 porträtierte er Ludwig Erhard für die Kampagne zur Bundestagswahl.

In den Jahren 1962 bis 1965 fotografierte Wolleh in Rom das Zweite Vatikanische Konzil. Unter Mitwirkung von Pater Emil Schmitz SJ wurde Wollehs erstes Fotobuch Das Konzil. II. Vatikanisches Konzil 1965 im Belser-Verlag veröffentlicht. 1975 illustrierte er die Botschaft des Papstes Paul VI. anlässlich des Heiligen Jahres in der Farbfotofolianten mit dem Titel der päpstlichen Bulle Apostolorum Limina (Belser-Verlag). Dieses Werk, mit seinen Unschärfen und Verwischungen, stellt eine radikale Weiterentwicklung der Wollehschen Farbfotografie dar, wie sie sich bei dem ersten Farbband Konzil andeutete.

1969 reiste Wolleh für mehrere Monate durch die Sowjetunion. Die auf dieser Reise entstandenen Fotografien fanden Eingang in den 1970 erschienenen Bildband UdSSR – Der Sowjetstaat und seine Menschen, den er zusammen mit Heinrich Böll und Valentin Katajew im Belser Verlag veröffentlichte.

Auf Anregung seines Freundes, des deutschen Malers und Objektkünstlers Günther Uecker, begann Wolleh ab 1967 systematisch insgesamt über einhundert international bekannte Maler, Bildhauer und Aktionskünstler zu porträtieren. Ab den 1970er Jahren arbeitete er kaum noch als Werbefotograf und widmete sich fast ausschließlich seiner Serie der Künstlerporträts, in der er zunächst die mit ihm bekannten Künstler der Düsseldorfer Szene fotografierte, darunter Heinz Mack, Otto Piene, Joseph Beuys und Gerhard Richter. Bald weitete sich sein Projekt aber über die Grenzen des Rheinlands auf ganz Europa aus mit Schwerpunkten wiederum auf die ZERO Gruppe oder den Nouveau Réalisme.

Aus den daraus entstandenen Porträtserien entstanden mehrere umfangreiche Fotobuch-Projekte und Kooperationen mit Künstlern. 1970/71 entstand der Band Uecker in der Edition Artifex in einer Auflage von 530. 1972 erstellte Wolleh den Film Schwarzraum-Weißraum zu der gleichnamigen Aktion von Günther Uecker. 1978 entstand die Uecker Mappe Zum Zeichen der Schrift oder die Sprachlosigkeit mit Fotos von Lothar Wolleh, die von Uecker teilweise übermalt worden sind. Die bemalten Fotografien sind u. a. die Vorlagen für das 1979 nachfolgende Auflagenwerk gleichen Titels. 1970 fotografierte Wolleh die Aktion Filz-TV von Joseph Beuys, die von Gerry Schum auf Film beziehungsweise Video aufgenommen wurde. 1972 entstand der Foto-Band Art Scene Düsseldorf, welcher durch Multiples der Künstler begleitet war.

Mehrere Buch- und Kunstmappen Projekte blieben unvollendet, so ein Band zu Lucio Fontana, Jan Schoonhoven, "Art Scene Düsseldorf No. 2" mit Arbeiten u. a. zu Dieter Roth, Stefan Wewerka, Klaus Rinke, sowie die Dokumentation über das "Trauerbankett" der Gruppe Nouveau Réalisme anlässlich ihrer Auflösung in Mailand 1970. Der Bildband Männer der Wirtschaft, in dem Firmengründer und Manager der führenden bundesdeutschen Unternehmen porträtiert wurden, konnte wegen befürchteter Angriffe durch die Rote Armee Fraktion nicht veröffentlicht werden. Weitere Projekte waren das Unterwasserbuch, das Beuys und Wolleh gemeinsam geplant hatten und für das Wolleh die 51 formatfüllenden Bilder erstellte. Die Aufnahmen entstanden beim Aufbau der Beuys-Ausstellung im Moderna Museet in Stockholm 1971.

Zwischen 1977 und 1979 erfolgten mehrere Aufenthalte in Polen. in den Wolleh bis zu seinem Tod an den unvollendeten Foto-Bänden Die schwarze Madonna von Tschenstochau und Schloss Wawel arbeitete. In dieser Zeit entstanden auch zahlreiche Porträts der polnischen Avantgarde.

Lothar Wolleh verfolgte in seinem Werk eigenwillige gestalterische Prinzipien, die seinen Porträts eine unverwechselbare Handschrift verleihen. Seine Arbeiten sind durch einen klaren oftmals streng symmetrischen Bildaufbau präzise konzipiert und inszeniert. Die porträtierten Personen sind meist als Ganzfigur auf der senkrechten Mittelachse in der oberen Bildhälfte platziert. Oft steht die Kamera auf den Boden oder nahe am Boden, sodass dieser fast die gesamte untere Bildhälfte einnimmt, eine Sockelfunktion erhält und eine klare Horizontlinie die Bildfläche gliedert. Als ein weiteres Charakteristikum gilt die Schwarz-Weiß-Fotografie, im Rahmen eines grundsätzlich quadratischen Formats. Insgesamt porträtierte Lothar Wolleh 109 Künstler.

Nach seinem frühen Tod gerieten das Werk und das Wirken Lothar Wollehs zunächst in Vergessenheit. Sein Sohn Oliver Wolleh übernahm die Erschließung und Verwaltung des fotografischen Nachlasses und initiierte zahlreiche Ausstellungen und Publikationen. 2005 fand in der Kunsthalle Bremen die erste große Einzelausstellung statt – der erste Schritt der bis heute andauernden Wiederentdeckung seines Werkes.

Porträtierte Künstler

Weitere, von Lothar Wolleh porträtierte Künstler sind unter anderem Magdalena Abakanowicz, Max Bill, Pol Bury, Alexander Camaro, Christo, Sonia Delaunay-Terk, Paul Dierkes, François Dufrêne, Robert Filliou, Vic Gentils, Karl Gerstner, Gotthard Graubner, Raymond Hains, Al Hansen, Bernhard Heiliger, Dorothy Iannone, Ferdinand Kriwet, Laszlo Lakner, Adolf Luther, Heinz Mack, Claes Oldenburg, Roman Opalka, César Pelli, Martial Raysse, Hans Richter, Klaus Rinke, Ulrich Rückriem, Reiner Ruthenbeck, Jesús Rafael Soto, Daniel Spoerri, André Thomkins, Mark Tobey, Georges Vantongerloo, Stefan Wewerka, Fritz Wotruba.

Publikationen
 Lothar Wolleh: Das Konzil. II. Vatikanisches Konzil. Chr. Belser Verlag, 1965.
 Lothar Wolleh, Heinrich Böll, Valentine Katajew: UdSSR. Der Sowjetstaat und seine Menschen. Chr. Belser Verlag, 1970, ISBN 3-7630-1548-5.
 Lothar Wolleh (Photo), Günther Uecker: Nagelbuch. Verlag Galerie Der Spiegel, Köln 1971.
 Lothar Wolleh: Art Scene Düsseldorf 1. Chr. Belser Verlag, 1972.
 Lothar Wolleh (Photo), Papst Paul VI.: Apostolorum Limina. Litterae Apostolicae. Arcade Verlag, Brüssel 1975, ISBN 2-8005-0100-6.
 Lothar Wolleh (Photo), Günther Uecker: Ludwig van Beethovens Leonore. Idee einer Oper. Belser Verlag, 1978.
 Lothar Wolleh (Photo), Günther Uecker, Guido de Werd (Text): Bühnenskulpturen für Lohengrin. Städtisches Museum Haus Koekkoek, Kleve 1981.

Ausstellungen (Auswahl)
 1962: Otto Steinert und Schüler. Fotografische Ausstellung, Gruppenausstellung in der Göppinger Galerie, Frankfurt am Main
 1964: Farbige Fotografie. Bilder aus dem Vatikan, Einzelausstellung: Schatzkammer des Essener Münsters. Die Ausstellung wurde vom Essener Bischof Hengsbach (am 21. März 1964) eröffnet
 1965: Zyklus von Farbfotos zum römischen Konzil, Einzelausstellung in der Galerie Valentin, Stuttgart

posthum
 1979: Lothar Wolleh (NIEMCY). Portrety Artystów, Einzelausstellung im Muzeum Sztuki w Łodzi, Polen
 1980: Lothar Wolleh: Künstlerbildnisse. Kunstobjekte, Photographien, Einzelausstellung der Künstlerporträts in der Städtischen Kunsthalle Düsseldorf
 1986: Lothar Wolleh – Das Foto als Kunststück, Einzelausstellung der Lippischen Gesellschaft für Kunst e. V. im Detmolder Schloss
 1995: Lothar Wolleh 1930–1979: Künstlerbildnisse – Kunstobjekte, Photographien, Kunstmuseum Ahlen
 2005–2007: Lothar Wolleh. Eine Wiederentdeckung: Fotografien 1959 bis 1979, Kunsthalle Bremen, Ludwig Museum Koblenz, Kunst-Museum Ahlen, Stadtmuseum Hofheim am Taunus
 2006: Joseph Beuys in Aktion. Heilkräfte der Kunst, Gruppenausstellung: museum kunst palast, Düsseldorf
 2008: Fotos schreiben Kunstgeschichte, Gruppenausstellung: museum kunst palast, Düsseldorf
 2008: Unsterblich! Das Foto des Künstlers, Staatliche Museen zu Berlin, Kunstbibliothek
 2008: Lothar Wolleh: Künstlerportraits, Galerie f5,6, München
 2009: Lothar Wolleh: Portraits d'artistes, Goethe-Institut Paris
 2012: Lothar Wolleh: Joseph Beuys im Moderna Museet, Stockholm, Januar 1971, Hamburger Bahnhof – Museum für Gegenwart, Berlin
 2012: Das Konzil – Fotografien von Lothar Wolleh, Berlin, Bonifatiushaus Fulda
 2013: Lothar Wolleh (1930–1979): Das Zweite Vatikanische Konzil im Bild: Fotografien, Franz Hitze Haus, Münster
 2014: Lothar Wolleh Künstlerportraits der sechziger und siebziger Jahre, Kunstmuseum Kloster Unser Lieben Frauen Magdeburg
 2015: Lothar Wolleh – Die ZERO–Künstler, pavlov's dog, Berlin
 2015: Lothar Wolleh – Vaticanum II, Galerie f5,6, München
 2017: Lothar Wolleh – Portraits international bekannter Künstler, Galerie Ruth Leuchter, Düsseldorf
 2018: Lothar Wolleh – Bernd Jansen Künstlerportraits, Hermann Harry Schmitz Institut – Museum, Düsseldorf
 2018: "Lothar Wolleh – Bernd Jansen Künstlerportraits", Hermann Harry Schmitz Institut, Düsseldorf
 2019: "Lothar Wolleh Raum 1 – Menschen, Farben, Licht", Lothar Wolleh Raum, Berlin
 2020: "Subjekt und Objekt. Foto Rhein Ruhr", Kunsthalle Düsseldorf
 2020: "Lothar Wolleh Raum 2 – Jenseits der Gegen¬ständlichkeit", Lothar Wolleh Raum, Berlin
 2020: "The Sky as a Studio. Yves Klein and his Contemporaries", Centre Pompidou-Metz
 2020: "Lothar Wolleh Raum 3 – Atmosphären der Phantasie", Lothar Wolleh Raum, Berlin
 2020: "TRUTH/REALITY", Coppejans Gallery, Antwerpen
 2021: "Joseph Beuys. Der Raumkurator", mit Arbeiten von Lothar Wolleh, Staatsgalerie, Stuttgart
 2021: "Der Erfinder der Elektrizität. Joseph Beuys und der Christusimpuls." Mit einer Dokumentation von Lothar Wolleh, St. Matthäus-Kirche, Berlin
 2021: "Wer nicht denken will fliegt raus", Coppejans Gallery, Antwerpen
 2021: "Lothar Wolleh: Intuition! Interaction!", Museum für Zeitgenössische Kunst Antwerpen (M HKA)
 2021: "Sankt Peter in Sankt Peter – Die Rombilder" von Lothar Wolleh, Kunst-Station Sankt Peter, Köln
 2021: "Joseph Beuys – Lothar Wolleh: das Unterwasserbuch-Projekt", Lothar Wolleh Raum, Berlin
 2021: "Westblick – Ostblick" | Künstlerporträts von Lothar Wolleh und Lenke Szilágyi, Collegium Hungaricum, Berlin
 2021: "Kriwet – ein Dichter aus Düsseldorf", Heinrich-Heine-Institut, Düsseldorf
 2021: "NOTHINGTOSEENESS void/white/silence", Akademie der Künste, Berlin
 2021: "Beat the System!", Ludwig Forum Aachen
 2021: "Beuys & Duchamp Artists of the Future", Kaiser Wilhelm Museum, Krefeld
 2021: "Lothar Wolleh: Joseph Beuys – Vom Moderna Museet zum Unterwasserbuch-Projekt", Goethe-Institut, Stockholm
 2021: "Warum denn in die Ferne ..." oder "In 18 Büchern um die Welt", Esslinger Kunstverein e.V.
 2021: "Joseph Beuys: Antecedent, Coincidences and Influences", Museo de Arte Contemporaneo Helga de Alvear, Cáceres
 2022: "Lothar Wolleh Raum 5 – Im Focus Günther Uecker", Lothar Wolleh Raum, Berlin

Werke in öffentlichen Sammlungen
 Kunsthalle Bremen
 Kunstmuseum Ahlen
 Museum Folkwang, Essen
 Kunstpalast, Düsseldorf
 Tate Gallery of Modern Art, London
 Scottish National Gallery, Edinburgh
 Muzeum Sztuki w Łodzi, Łódź

Literatur
 Karl-Heinz Hering: Lothar Wolleh. Künstlerbildnisse, Kunstobjekte, Photographien. Kunstverein für die Rheinlande und Westfalen, Düsseldorf 1980.
 Das Foto als Kunststück. In: art Das Kunstmagazin. Ausgabe 8/1980.
 Patricia G. Berman, Art Scene Düsseldorf: The Performative Print. In: Reinhold Heller, Anja Chávez: Two and One, Printmaking in Germany 1945–1990, Davis Museum and Cultural Center, Wellesley College. Wellesley, Mass. 2003, ISBN 0-9744898-0-8.
 Wulf Herzogenrath, Burkhard Leismann (Hrsg.): Lothar Wolleh. Eine Wiederentdeckung. Fotografien 1959–1979. Hauschild Verlag, Bremen 2005, ISBN 3-89757-304-0.
 Kristina Lowis (Hrsg.): Unsterblich! Das Foto des Künstlers. Staatliche Museen zu Berlin, Berlin 2008, ISBN 978-3-88609-656-5.
 Udo Kittelmann (Hrsg.): Lothar Wolleh. Joseph Beuys im Moderna Museet, Stockholm, Januar 1971. Buchhandlung Walther König, Köln 2012, ISBN 978-3-86335-263-9.
 Antoon Melissen, Lothar Wolleh Raum (Hrsg.): Joseph Beuys und Lothar Wolleh - Das Unterwasserbuch-Projekt. Kerber Verlag, Bielefeld/Berlin 2021, ISBN 978-3-7356-0769-0.

Weblinks

 Seite von Lothar Wolleh Raum, Berlin
 Staat in Gala: Lothar Wolleh u. a.: "UdSSR. Der Sowjetstaat und seine Menschen." Der Spiegel
 Lothar Wolleh – Eine Wiederentdeckung, Ausstellung im Ludwig Museum im Deutschherrenhaus, Koblenz

 Lothar Wolleh: Joseph Beuys im Moderna Museet, Stockholm, Januar 1971, Ausstellung im Hamburger Bahnhof Museum für Gegenwart, Berlin
 Kultur MD Lothar Wolleh Ausstellung im Kunstmuseum Magdeburg
 Hier wird ein großer Foto-Schatz gehoben. Berliner Zeitung. (29. April 2015)
 Ausführliche Biografie und Bildbeispiele von Lothar Wolleh
 Dokumentengalerie zur Haft von Lothar Wolleh in Workuta
 Ausstellungen, von und mit Lothar Wolleh bei Photography Now

Einzelnachweise

Fotograf (20. Jahrhundert)
Fotograf (Düsseldorf)
Fotokünstler
Häftling im Gulag
Deutscher
Geboren 1930
Gestorben 1979
Mann

redpajama_set_name: RedPajamaWikipedia