Jump to content
Xtreme .Net Talk

Recommended Posts

Posted

Hi All,

 

Apoligies for my absence, moved house last year.

 

I am trying to copy an element from an esisting XML file.

 

For example here is simplified version of this file:

<Ctrl Name="Customers">
   <Group GroupId="1">
       <FirstName>"Joe"</FirstName>
       <Surname>"Soap"</Surname>
   </Group >
   <Group Group ="2">
        <FirstName>"Joe"</FirstName>
       <Surname>"Pope"</Surname>
   </Group >
</Ctrl >

All I am trying to do programmitically is trying to copy the element store where it's ID is 1 to a new element store of ID 3 into the existing XML document, like this:

<Ctrl Name="Customers">
   <Group GroupId="1">
       <FirstName>"Joe"</FirstName>
       <Surname>"Soap"</Surname>
   </Group >
   <Group Group ="2">
        <FirstName>"Jane"</FirstName>
       <Surname>"Pope"</Surname>
   </Group >
    <Group GroupId="3">
       <FirstName>"Joe"</FirstName>
       <Surname>"Soap"</Surname>
   </Group >
</Ctrl >

I have not wrote much of the code as yet, all I have is this:

Dim xmlReader As New XmlTextReader("C:\work\homeTest\source\test.xml")
Dim xmlDoc As New XmlDocument
xmlDoc.Load(xmlReader)

As I understand the object XmlDocument is the correct choice for ths task. But I cannot fathom how to firstly access the element I want and the to iterate through all of this elements nodes and then create this new element in the existing xml document.

 

Please help?

 

Thanks, Dave.

Posted

To append an element to XmlDocument you have to go thru these three simple steps:

 

* Find a place where exactly you want new element to be place.

This can be done using XPath queries (this it the preferred way -

see SelectSingleNode method in MSDN) or by iterating thru XmlDocuments

object model (which is only reasonable in case of small documents with

simple structure). As a result of your search you would have a reference

to XmlNode object - the future parent for new 'to-be-created' node.

 

* Find a node you want to make a copy. When that node is found - clone it by

using, comes with no surprise, 'Clone' method.

 

* Append duplicate (created at the previous step) to the parent node (found at

first step) using AppendChild method.

 

That's all.

 

ps: and don't forget to save changed XmlDocument.

 

 

Hi All,

 

Apoligies for my absence, moved house last year.

 

I am trying to copy an element from an esisting XML file.

 

For example here is simplified version of this file:

<Ctrl Name="Customers">
   <Group GroupId="1">
       <FirstName>"Joe"</FirstName>
       <Surname>"Soap"</Surname>
   </Group >
   <Group Group ="2">
        <FirstName>"Joe"</FirstName>
       <Surname>"Pope"</Surname>
   </Group >
</Ctrl >

All I am trying to do programmitically is trying to copy the element store where it's ID is 1 to a new element store of ID 3 into the existing XML document, like this:

<Ctrl Name="Customers">
   <Group GroupId="1">
       <FirstName>"Joe"</FirstName>
       <Surname>"Soap"</Surname>
   </Group >
   <Group Group ="2">
        <FirstName>"Jane"</FirstName>
       <Surname>"Pope"</Surname>
   </Group >
    <Group GroupId="3">
       <FirstName>"Joe"</FirstName>
       <Surname>"Soap"</Surname>
   </Group >
</Ctrl >

I have not wrote much of the code as yet, all I have is this:

Dim xmlReader As New XmlTextReader("C:\work\homeTest\source\test.xml")
Dim xmlDoc As New XmlDocument
xmlDoc.Load(xmlReader)

As I understand the object XmlDocument is the correct choice for ths task. But I cannot fathom how to firstly access the element I want and the to iterate through all of this elements nodes and then create this new element in the existing xml document.

 

Please help?

 

Thanks, Dave.

Posted

Thats All???

 

I appreciate the overview..... but it caught me funny... I have been searching on how to accomplish something like this for hours....

 

I have seen hundreds of post and questions all over the internet where people respond with overviews and search this method or that method but no examples whatsoever.

 

It took me 4 days to finannly figure out how to update an sml file using textboxes... wow is it simple, however..... finding it is like pulling teeth most undesirable.

 

I can find all sort of examples that are no were near what I am needing done.... or what this guy is needing done...

 

Well at least you tried to give input....

 

Dont get me wrong not coming down on you .... just got tickled since I have been searching for a few hours myself...

 

Well back to the search ....

 

Oh for the thread starter I hope you find what your looking for, if you do please post it cause I am looking too.

 

I need something simular..

 

 

I have a structure like this

 

 

,

<ChurchMembership>
 <Members>
   <Name>Jim Bob</Name>
   <Phone>(123) 456-7890</Phone>
   <Address>Not a Cowboy</Address>
 </Members>
 <Members>
   <Name>testing again and again</Name>
   <Phone>(098) 765-4321</Phone>
   <Address>well anything yet?</Address>
 </Members>
 <Members>
   <Name>Johnny Boy</Name>
   <Phone>(123) 456-7890</Phone>
   <Address>Not a Cowboy</Address>
 </Members>
 <Members>
   <Name>testing again and again</Name>
   <Phone>(098) 765-4321</Phone>
   <Address>well anything yet?</Address>
 </Members>
 <Members>
   <Name>Franky</Name>
   <Phone>(123) 456-7890</Phone>
   <Address>Not a Cowboy</Address>
 </Members>
 <Members>
   <Name>Number 6</Name>
   <Phone>(123) 456-9878</Phone>
   <Address>THIS SHOULD LAND AS NUMBER SIX</Address>
 </Members>
  ' New REcord added here I hope
</ChurchMembership>

 

 

ANyway..... I am simply trying to add this at the bottom

 

<Members>
   <Name>Number 7</Name>
   <Phone>123-456-7890</Phone>
   <Address>THIS SHOULD LAND AS NUMBER SEVEN</Address>
 </Members>

 

Any chance you all have an idea how to do it or where its done I would love to know....

Visual Basic 2008 Express Edition!
Posted

Here is a snippet of code I ended up using if it helps at all:

Dim xmlNodelst2 As XmlNodeList = xmlDoc.SelectNodes("//Firstname")
For Each xmlItem As XmlNode In xmlNodelst2
     Dim storeId As Int32 = 0
     If Not xmlItem.Attributes("Firstname") Is Nothing Then
         idType = "StoreGroupId"
         Int32.TryParse(xmlItem.Attributes("Firstname").Value, storeId)
         If storeId = 1 Then
              StoreUKXMLNode = xmlItem
              Exit For
         End If
     End If
Next
If Not StoreUKXMLNode Is Nothing Then
   'Create Store3 Element
   Dim StoreBizElement As XmlElement
   StoreBizElement = xmlDoc.CreateElement(StoreUKXMLNode.Name)
   Dim StoreIdAttr As XmlAttribute
   StoreIdAttr = xmlDoc.CreateAttribute(idType)
   StoreIdAttr.Value = 3
   StoreBizElement.Attributes.Append(StoreIdAttr)
   StoreBizElement.InnerXml = StoreUKXMLNode.InnerXml
   StoreUKXMLNode.ParentNode.AppendChild(StoreBizElement)
End If

Posted

vbMarkO this will suit your purposes....

// get the original document
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
xmlDoc.Load(@"C:\input.xml");

// create a new node
System.Xml.XmlNode memberNode = xmlDoc.CreateElement("Members");

// create the child nodes
System.Xml.XmlNode nameNode = xmlDoc.CreateElement("Name");
nameNode.InnerText = "Peter";
System.Xml.XmlNode phoneNode = xmlDoc.CreateElement("Phone");
phoneNode.InnerText = "01664 123456";
System.Xml.XmlNode addressNode = xmlDoc.CreateElement("Address");
addressNode.InnerText = "This is my address";

// add the child nodes to the new node
memberNode.AppendChild(nameNode);
memberNode.AppendChild(phoneNode);
memberNode.AppendChild(addressNode);

// add the new node to the file and save
xmlDoc.FirstChild.AppendChild(memberNode);
xmlDoc.Save(@"C:\output.xml");

        ' get the original document
       Dim xmlDoc As New System.Xml.XmlDocument
       xmlDoc.Load("C:\input.xml")

       ' create a new node
       Dim memberNode As System.Xml.XmlNode = xmlDoc.CreateElement("Members")

       ' create the child nodes
       Dim nameNode As System.Xml.XmlNode = xmlDoc.CreateElement("Name")
       nameNode.InnerText = "Peter"
       Dim phoneNode As System.Xml.XmlNode = xmlDoc.CreateElement("Phone")
       phoneNode.InnerText = "01664 123456"
       Dim addressNode As System.Xml.XmlNode = xmlDoc.CreateElement("Address")
       addressNode.InnerText = "This is my address"

       ' add the child nodes to the new node
       memberNode.AppendChild(nameNode)
       memberNode.AppendChild(phoneNode)
       memberNode.AppendChild(addressNode)

       ' add the new node to the file and save
       xmlDoc.FirstChild.AppendChild(memberNode)
       xmlDoc.Save("C:\output.xml")

Hope the vb is right i'm a little rusty

Anybody looking for a graduate programmer (Midlands, England)?
Posted

Thanx this works great but to get it to work I had to make one change...

 

xmlDoc.DocumentElement.AppendChild(memberNode) ' DocumentElement


xmlDoc.FirstChild.AppendChild(memberNode) '  instead of FirstChild

 

It works great..... Also I love the simple understandable approach... and I need that approach LOL

 

Anyway, would it be to much to ask if you just as simple of an approach to read it back out to textboxes....

 

For instance txtName.text, txtPhone.text, txtAddress.text????

 

I have found one way to do this byval of posistion as integer

 

As I understand it it iterates through the nodes by index posistion if index is the right word to describe it...

 

But I would be very interested in your approach to this!!!

 

vbMarkO

Visual Basic 2008 Express Edition!
Posted

Well this has certainly been a refresher for me, I haven't used the DOM (XmlDocument class) in a long time, because it becomes extremely inefficient and resource intensive with large xml files. Reading it back out is a simple matter of reading the InnerText property of the right nodes.

' once you have the right Members node you can just do
txtName.Text = myNode.ChildNodes(0)
txtPhone.Text = myNode.ChildNodes(1)
txtAddress.Text = myNode.ChildNodes(2)

Obviously this has some limitations in as much as it won't work correctly if you re-order the nodes within a Members node. It would be possible to create a slightly more effective system by looping through the childnodes of a Members node, and checking if the Name property is the one your looking for before retrieving the value.

 

It will be difficult to help you much better without knowing exactly how you are specifying which Members node you wish to retrieve. Moving between the Members nodes can be as simple as a for each loop, but as I said helping much further would require knowing how you plan on stopping at a specific node.

        Dim myDoc As System.Xml.XmlDocument
       myDoc.Load("c:\Test.xml")

       ' An example of a conditional search (albiet a poor one)
       ' the code will return the values from the first Members 
       ' node with the name bob
       For Each myNode As System.Xml.XmlNode In myDoc.DocumentElement.ChildNodes
           If myNode.FirstChild.InnerText = "Bob" Then
               txtName.Text = myNode.ChildNodes(0)
               txtPhone.Text = myNode.ChildNodes(1)
               txtAddress.Text = myNode.ChildNodes(2)
               Exit For
           End If
       Next

Anybody looking for a graduate programmer (Midlands, England)?
Posted

This is great stuff.....

 

What I would have done to find a thread like this a week ago.

 

The code works great thanx for pointing me in the right direction.

Since we are on a roll think we could do one more? :)

Now that I can append a node and its children I am looking for how to delete that node and its children.

 

 

 

vbMarkO

Visual Basic 2008 Express Edition!
Posted
        Dim myDoc As New System.Xml.XmlDocument
       myDoc.Load("c:\input.xml")

       For Each myNode As System.Xml.XmlNode In myDoc.DocumentElement.ChildNodes
           If myNode.FirstChild.InnerText = "Bob" Then
               myNode.ParentNode.RemoveChild(myNode)
               Exit For
           End If
       Next

       myDoc.Save("c:\output.xml")

Anybody looking for a graduate programmer (Midlands, England)?
Posted

Awesome Guys.... this has really been an eye opener for me.... no wonder everyon has been trying to tell me to use xml instead of textfiles....

 

I have a question if I might......

 

About the following code

 

 
Dim xmlDoc As New xmlDocument
xmlDoc.Load(xmlFile)

 

Does this ever need to be closed?

 

Should this only be opened once? or can it be loaded in each button click event such as

 

Savexml event

DeleteNode event

update event and so on?

 

vbMarkO

Visual Basic 2008 Express Edition!
Posted

Oddly the XmlDocument class doesn't implement IDisposable, which seems strange for a class that seems to be a resource hog. It doesn't even have a Close method. You could assign the class to null after your finished with it, but with my limited understanding I have no idea if this will help the Garbage Collector clear up the resources any quicker.

 

As for whether to open the file only once or multiple times is a mixed bag. Loading the file can take time, as such if you are likely to be performing many operations on it, it would make sense to keep the file open. The XmlDocument class can however use alot of memory so you could argue that these resources should be freed up when not being used. The only problem with this is that since there is no Dispose method, there is no clear way todo this.

 

Personally most of the problems I've had with resources have been when I was using very large xml files (i'm talking 30MB). As long as you are using smallish files, I see no reason why you can't just keep the file open and make changes whenever you want, this will save you waiting for the file to be opened before making the next change.

Anybody looking for a graduate programmer (Midlands, England)?
  • Administrators
Posted

Dispose is only required when an object holds onto non-managed resources (think file handles, db connections etc.) as these need an immediate release when no longer required.

Even though the XmlDocument class can be a memory hog it's just memory and therefore the GC knows best ;)

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted
This being the case is it advisable to set the object equal to null thus removing all references to it, helping the GC to realise it is no longer in use or is this not required? Assumably an object created by a method would become null as soon as the method was through anyway, but I'm talking more about if its a public level object that is no longer needed.
Anybody looking for a graduate programmer (Midlands, England)?
  • Administrators
Posted

If you are doing a release build you don't need to set it to null for objects within a method, the GC will free resources that are no longer used even if it triggers mid function.

 

For class level variables then if you no longer need the object setting it to null will allow the GC to free it.

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted

That application in this case or xmlfile is very small.... it will only be holding 100 + names and personal info as addres phones and such as it is an addressbook

 

Let me ask this then...

 

Perhaps I should open it do what is needed then when I close the app will this then free or act as though the xmlDoc has been closed or are those resources still being used even after the app is closed...

 

vbMarkO

Visual Basic 2008 Express Edition!
Posted
Basically what PlausiblyDamp was explaining to me is that it will clear up it's own resources. If you close the application the Garbage Collector will handle it for you. The only time you can really do anything is if you have an XmlDocument declared at class level (ie. public XmlDocument myDocument; at the top of your class) that you will no longer need. If this is the case you can assign it to null (myDocument = null;) to let the GC know your done with it.
Anybody looking for a graduate programmer (Midlands, England)?
Posted

Great, sounds good.

 

Thank you guys for all the input here it has been some of the best I have found online.

 

Learned alot from this thread.

 

vbMarkO

Visual Basic 2008 Express Edition!
Posted

Consider serialization

 

Now that you've found a satisfactory solution, I'm just going to jump in here and add my "two cents".

 

When I first started working with XML documents in .Net, I too approached it by reading, deleting, and inserting individual nodes "manually". However, once the complexity of your XML document design passes a certain point, I find this approach can be difficult to maintain, and tightly links your code to the XML schema, so even relatively small changes in the schema can require major code changes.

 

The method I now use is to create an XML schema document (XSD) which defines very strictly how the XML is arranged, and then use the VS xsd tool to generate classes based on the schema. Then it is a simple matter of using the XmlSerializer class to instantiate a graph of useable objects from the XML, and later serialize the objects back into XML.

 

Besides the initial creation of the XSD, this approach requires a minimal amount of programming and maximum flexibility. And resource usage wise, I would expect it to be pretty efficient too, especially if there are a large number of insert and delete operations.

 

Of course for the time being neither of you (davearia and vbMarkO) need to worry about this, as your document models are both fairly simple, but if you ever work with XML on a larger scale, I would recommend you look into serialization.

 

:cool:

Never trouble another for what you can do for yourself.
Posted

Thanx for the 2 cents I will certainly take that into consideration as I learn more about xml....

 

Ok, just found a problem too..... it is in reguard to the update code I got here...

 

Note: It works perfect until I sort the listbox..... since this code updates the xml node based on posistion by the index of the node..... when I use the selectedIndex of the lstNames listbox the sorted names no longer are in the same index sequence.

 

The listbox is in sort order but the xmlfile is not.

 

So I need to modify this code so that it updates somehow based on the selectedItem of the listbox(lstNames) and not selectedIndex...

 

Here is the code as I have it... NOTE again if I change the sort property of the listbox this code works great for updating my records....

 

So how can we change it

 

 xmlDoc.Load(xmlFile)
       Dim ChurchMembership As XmlNode = xmlDoc.ChildNodes(1)

       Dim i As Integer = lstNames.SelectedIndex ' <-- This is the problem lstNames is now sorted so
       ' the index doesnt match and is overwriting other nodes... instead of updating the right one


       ' //////////////// TODO:  This block of code is not working properly \\\\\\\\\
       ChurchMembership.ChildNodes(i).ChildNodes(0).InnerText = txtName.Text
       ChurchMembership.ChildNodes(i).ChildNodes(1).InnerText = txtPhone.Text
       ChurchMembership.ChildNodes(i).ChildNodes(2).InnerText = txtAddress.Text
       ChurchMembership.ChildNodes(i).ChildNodes(3).InnerText = cboCity.Text
       ChurchMembership.ChildNodes(i).ChildNodes(4).InnerText = txtState.Text
       ChurchMembership.ChildNodes(i).ChildNodes(5).InnerText = txtZip.Text
       'ChurchMembership.ChildNodes(i).ChildNodes(6).InnerText = lstDates.Items.Add("No Dates available")
       ChurchMembership.ChildNodes(i).ChildNodes(7).InnerText = txtNote.Text
       xmlDoc.Save(xmlFile)

 

Here again is a smaple of my xmlFIle structure

 

<ChurchMembership>
 <Members>
   <Name>Jim Bob</Name>
   <Phone>(123) 456-7890</Phone>
   <Address>Not a Cowboy</Address>
 </Members>
 <Members>
   <Name>John Hancock</Name>
   <Phone>(098) 765-4321</Phone>
   <Address>Sometown</Address>
 </Members>
 <Members>
   <Name>Johnny Boy</Name>
   <Phone>(123) 456-7890</Phone>
   <Address>Not a Cowboy</Address>
 </Members>
 <Members>
   <Name>Mary Jane</Name>
   <Phone>(123) 456-4321</Phone>
   <Address>well anything yet?</Address>
 </Members>
 <Members>
   <Name>Franky</Name>
   <Phone>(123) 456-7890</Phone>
   <Address>Not a Cowboy</Address>
 </Members>
</ChurchMembership>

Visual Basic 2008 Express Edition!
Posted
The neatest solution would actually use MrPauls suggestion. The idea is you create a class called Person. This class stores all the information about a person, including name, address etc. You then have a collection of these classes. Whenever you make changes to one of the people, you update their class. In order to store the data to file you can then Serialise and DeSerialise the file. Now I can guide you through an example of this if you require and I'm sure MrPaul can provide a more elegant solution (I have no experience of using an XSD).
Anybody looking for a graduate programmer (Midlands, England)?
  • Administrators
Posted

Basic sample class...

Public Class Member
   Private _Name As String
   Private _Phone As String
   Private _Address As String

   Public Sub New()

   End Sub

   Public Sub New(ByVal name As String, ByVal phone As String, ByVal address As String)
       _Name = name
       _Phone = phone
       _Address = address
   End Sub

   Public Property Name() As String
       Get
           Return _Name
       End Get
       Set(ByVal value As String)
           _Name = value
       End Set
   End Property
   Public Property Phone() As String
       Get
           Return _Phone
       End Get
       Set(ByVal value As String)
           _Phone = value
       End Set
   End Property
   Public Property Address() As String
       Get
           Return _Address
       End Get
       Set(ByVal value As String)
           _Address = value
       End Set
   End Property
End Class

 

Sample code to populate / save list...

Dim members As New List(Of Member) 'for .net 2 use an array or arraylist for .net 1

       members.Add(New Member("Jim Bob", "(123) 456-7890", "Not a cowboy"))
       members.Add(New Member("John Hancock", "(098) 765-4321", "Sometown"))
       members.Add(New Member("etc.", "etc.", "etc."))

       Dim xs As New Xml.Serialization.XmlSerializer(GetType(List(Of Member)))
       Dim fs As New System.IO.FileStream("c:\test.xml", IO.FileMode.Create, IO.FileAccess.ReadWrite)

       xs.Serialize(fs, members)
       fs.Close()

 

Reading it back in...

       Dim xs2 As New Xml.Serialization.XmlSerializer(GetType(List(Of Member)))
       Dim fs2 As New System.IO.FileStream("c:\test.xml", IO.FileMode.Open, IO.FileAccess.Read)

       Dim m As List(Of Member)
       m = DirectCast(xs2.Deserialize(fs2), List(Of Member))
       fs2.Close()

 

hope that gets you started.

Posting Guidelines FAQ Post Formatting

 

Intellectuals solve problems; geniuses prevent them.

-- Albert Einstein

Posted

Wow,

 

It sounds complicated but I need it and if its the only way to get it done then I am up to learning how....

 

vbMarkO

Visual Basic 2008 Express Edition!
Posted

You must have posted that near the same time I was posting earlier...

 

Again WOW thats going to take some breaking down .... I will certainly dive into this to try and understand it so I can recreate it later if needed.

 

However while you were writing this out, I went to figuring about how I did the save and also the remove and it hit me I could remove and save a newrecord at the same time simulating an update of sorts...

 

Crude but it works :)

 

I still cant help but feel if I can search and find the exact node to remove I should be able to update more efficiently than this....

 

 

Here is my crude method of updating this small addressbook

 

  Dim xmlDoc As New XmlDocument
       xmlDoc.Load(xmlFile)

       For Each myNode As XmlNode In xmlDoc.DocumentElement.ChildNodes
           If myNode.FirstChild.InnerText = lstNames.SelectedItem Then
               myNode.ParentNode.RemoveChild(myNode)
               Exit For
           End If
       Next

       xmlDoc.Save(xmlFile)

       '/////////////////////////////////

      
       ' create a new node
       Dim memberNode As System.Xml.XmlNode = xmlDoc.CreateElement("Members")

       ' create the child nodes
       Dim nameNode As System.Xml.XmlNode = xmlDoc.CreateElement("Name")
       nameNode.InnerText = txtName.Text
       Dim phoneNode As System.Xml.XmlNode = xmlDoc.CreateElement("Phone")
       phoneNode.InnerText = txtPhone.Text
       'Dim addressNode As System.Xml.XmlNode = xmlDoc.CreateElement("Address")
       'addressNode.InnerText = "This is my address"

       ' add the child nodes to the new node
       memberNode.AppendChild(nameNode)
       memberNode.AppendChild(phoneNode)
       'memberNode.AppendChild(addressNode)

       ' add the new node to the file and save
       xmlDoc.DocumentElement.AppendChild(memberNode)
       xmlDoc.Save(xmlFile)
      
       Call DisplayRecords()

 

 

Creative I think :) Dont worry Im not satisfied just yet.... There has got to be a way to iterate through find the node block that needs to be updated and update it...

 

so I am a searching

 

vbMarkO

Visual Basic 2008 Express Edition!
Posted
It's certainly possible to iterate through and find the person you wish to update, but there is one major issue with your current system that could cause a problem. You use the name to search, but the chances are in even a relatively small system there will be more than one person with the same name. Using an iterative system would be far easier if you had a unique ID for each person.
Anybody looking for a graduate programmer (Midlands, England)?
Posted

Sure I understand that and certainly agree with you.

Though in this case at the moment there are no duplicate names within this group though it is certianly possible there could be done the road.

 

Not sure how to make a UNique ID ... I mean I know we could use SS# but thats to personal for this group as there is no need for me to have access to that info...

 

Phone Number isnt specific enough.... So Im not sure what I would use

 

 

But I would love to see how I might iterate this as you mentioned even with this potential problem and see how I might iterate through and then update

 

 

vbMarkO

Visual Basic 2008 Express Edition!

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...